aboutsummaryrefslogtreecommitdiffstats
path: root/lib/stdlib
diff options
context:
space:
mode:
Diffstat (limited to 'lib/stdlib')
-rw-r--r--lib/stdlib/doc/src/calendar.xml9
-rw-r--r--lib/stdlib/doc/src/ets.xml6
-rw-r--r--lib/stdlib/doc/src/gen_event.xml2
-rw-r--r--lib/stdlib/doc/src/io.xml17
-rw-r--r--lib/stdlib/doc/src/notes.xml393
-rw-r--r--lib/stdlib/src/array.erl2
-rw-r--r--lib/stdlib/src/erl_eval.erl46
-rw-r--r--lib/stdlib/src/gen_event.erl4
-rw-r--r--lib/stdlib/src/gen_fsm.erl4
-rw-r--r--lib/stdlib/src/gen_server.erl4
-rw-r--r--lib/stdlib/src/gen_statem.erl2
-rw-r--r--lib/stdlib/src/io.erl11
-rw-r--r--lib/stdlib/src/io_lib_pretty.erl2
-rw-r--r--lib/stdlib/src/maps.erl9
-rw-r--r--lib/stdlib/src/proc_lib.erl2
-rw-r--r--lib/stdlib/src/string.erl12
-rw-r--r--lib/stdlib/src/supervisor.erl6
-rw-r--r--lib/stdlib/src/supervisor_bridge.erl4
-rw-r--r--lib/stdlib/src/uri_string.erl206
-rw-r--r--lib/stdlib/test/Makefile3
-rw-r--r--lib/stdlib/test/ets_SUITE.erl13
-rw-r--r--lib/stdlib/test/io_SUITE.erl22
-rw-r--r--lib/stdlib/test/maps_SUITE.erl4
-rw-r--r--lib/stdlib/test/proc_lib_SUITE.erl10
-rw-r--r--lib/stdlib/test/property_test/uri_string_recompose.erl260
-rw-r--r--lib/stdlib/test/string_SUITE.erl12
-rw-r--r--lib/stdlib/test/uri_string_SUITE.erl261
-rw-r--r--lib/stdlib/test/uri_string_property_test_SUITE.erl7
-rw-r--r--lib/stdlib/vsn.mk2
29 files changed, 749 insertions, 586 deletions
diff --git a/lib/stdlib/doc/src/calendar.xml b/lib/stdlib/doc/src/calendar.xml
index 8f2b6b747a..6b4fa7f98a 100644
--- a/lib/stdlib/doc/src/calendar.xml
+++ b/lib/stdlib/doc/src/calendar.xml
@@ -323,7 +323,9 @@
<type name="rfc3339_string"/>
<type name="rfc3339_time_unit"/>
<desc>
- <p>Converts an RFC 3339 timestamp into system time.</p>
+ <p>Converts an RFC 3339 timestamp into system time. The data format
+ of RFC 3339 timestamps is described by
+ <url href="https://www.ietf.org/rfc/rfc3339.txt">RFC 3339</url>.</p>
<p>Valid option:</p>
<taglist>
<tag><c>{unit, Unit}</c></tag>
@@ -378,7 +380,10 @@
<type name="rfc3339_string"/>
<type name="rfc3339_time_unit"/>
<desc>
- <p>Converts a system time into RFC 3339 timestamp.</p>
+ <p>Converts a system time into an RFC 3339 timestamp. The data format
+ of RFC 3339 timestamps is described by
+ <url href="https://www.ietf.org/rfc/rfc3339.txt">RFC 3339</url>.
+ The data format of offsets is also described by RFC 3339.</p>
<p>Valid options:</p>
<taglist>
<tag><c>{offset, Offset}</c></tag>
diff --git a/lib/stdlib/doc/src/ets.xml b/lib/stdlib/doc/src/ets.xml
index 305376a425..1995262145 100644
--- a/lib/stdlib/doc/src/ets.xml
+++ b/lib/stdlib/doc/src/ets.xml
@@ -49,14 +49,16 @@
associated with each key. A <c>bag</c> or <c>duplicate_bag</c> table can
have many objects associated with each key.</p>
+ <marker id="max_ets_tables"></marker>
<note>
<p>
The number of tables stored at one Erlang node <em>used</em> to
be limited. This is no longer the case (except by memory usage).
The previous default limit was about 1400 tables and
could be increased by setting the environment variable
- <c>ERL_MAX_ETS_TABLES</c> before starting the Erlang runtime
- system. This hard limit has been removed, but it is currently
+ <c>ERL_MAX_ETS_TABLES</c> or the command line option
+ <seealso marker="erts:erl#+e"><c>+e</c></seealso> before starting the
+ Erlang runtime system. This hard limit has been removed, but it is currently
useful to set the <c>ERL_MAX_ETS_TABLES</c> anyway. It should be
set to an approximate of the maximum amount of tables used. This since
an internal table for named tables is sized using this value. If
diff --git a/lib/stdlib/doc/src/gen_event.xml b/lib/stdlib/doc/src/gen_event.xml
index 51378a6b73..6170801e87 100644
--- a/lib/stdlib/doc/src/gen_event.xml
+++ b/lib/stdlib/doc/src/gen_event.xml
@@ -207,7 +207,7 @@ gen_event:stop -----> Module:terminate/2
</item>
<item>
<p>If the event handler is deleted later, the event manager
- sends a message<c>{gen_event_EXIT,Handler,Reason}</c> to
+ sends a message <c>{gen_event_EXIT,Handler,Reason}</c> to
the calling process. <c>Reason</c> is one of the following:</p>
<list type="bulleted">
<item>
diff --git a/lib/stdlib/doc/src/io.xml b/lib/stdlib/doc/src/io.xml
index f1037ec76b..d4a2713840 100644
--- a/lib/stdlib/doc/src/io.xml
+++ b/lib/stdlib/doc/src/io.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2017</year>
+ <year>1996</year><year>2018</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -332,11 +332,22 @@ Here T = [{attributes,[[{id,age,1.5},
{tag,{'PRIVATE',3}},
{mode,implicit}]
ok</pre>
+
+ <p>As from Erlang/OTP 21.0, a field width of value
+ <c>0</c> can be used for specifying that a line is
+ infinitely long, which means that no line breaks
+ are inserted. For example:</p>
+
+ <pre>
+5> <input>io:fwrite("~0p~n", [lists:seq(1, 30)]).</input>
+[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30]
+ok</pre>
+
<p>When the modifier <c>l</c> is specified, no detection of
printable character lists takes place, for example:</p>
<pre>
-5> <input>S = [{a,"a"}, {b, "b"}].</input>
-6> <input>io:fwrite("~15p~n", [S]).</input>
+6> <input>S = [{a,"a"}, {b, "b"}],
+ io:fwrite("~15p~n", [S]).</input>
[{a,"a"},
{b,"b"}]
ok
diff --git a/lib/stdlib/doc/src/notes.xml b/lib/stdlib/doc/src/notes.xml
index b925fa3b02..e26c4aba74 100644
--- a/lib/stdlib/doc/src/notes.xml
+++ b/lib/stdlib/doc/src/notes.xml
@@ -31,399 +31,6 @@
</header>
<p>This document describes the changes made to the STDLIB application.</p>
-<section><title>STDLIB 3.5</title>
-
- <section><title>Fixed Bugs and Malfunctions</title>
- <list>
- <item>
- <p><c>gen_statem</c> improvements.</p> <p> When using an
- exception that is valid but not allowed in a state enter
- call, the reason has been changed from
- <c>{bad_action_from_state_function,Action}</c> to
- <c>{bad_state_enter_action_from_state_function,Action}</c>.
- </p><p> Timer parsing has been improved. Many erroneous
- timeout tuples was not handled correctly. </p><p> The
- documentation has been updated, in particular the User's
- Guide and the pointer to it from the Reference Manual is
- much more obvious. </p>
- <p>
- Own Id: OTP-14015</p>
- </item>
- <item>
- <p>
- The type specifications for <c>file:posix/0</c> and
- <c>inet:posix/0</c> have been updated according to which
- errors file and socket operations should be able to
- return.</p>
- <p>
- Own Id: OTP-14019 Aux Id: ERL-550 </p>
- </item>
- <item>
- <p> File operations used to accept <seealso
- marker="kernel:file#type-name_all">filenames</seealso>
- containing null characters (integer value zero). This
- caused the name to be truncated and in some cases
- arguments to primitive operations to be mixed up.
- Filenames containing null characters inside the filename
- are now <em>rejected</em> and will cause primitive file
- operations to fail. </p> <p> Also environment variable
- operations used to accept <seealso
- marker="kernel:os#type-env_var_name">names</seealso> and
- <seealso
- marker="kernel:os#type-env_var_value">values</seealso> of
- environment variables containing null characters (integer
- value zero). This caused operations to silently produce
- erroneous results. Environment variable names and values
- containing null characters inside the name or value are
- now <em>rejected</em> and will cause environment variable
- operations to fail. </p> <p>Primitive environment
- variable operations also used to accept the <c>$=</c>
- character in environment variable names causing various
- problems. <c>$=</c> characters in environment variable
- names are now also <em>rejected</em>. </p> <p>Also
- <seealso
- marker="kernel:os#cmd/1"><c>os:cmd/1</c></seealso> now
- reject null characters inside its <seealso
- marker="kernel:os#type-os_command">command</seealso>.
- </p> <p><seealso
- marker="erts:erlang#open_port/2"><c>erlang:open_port/2</c></seealso>
- will also reject null characters inside the port name
- from now on.</p>
- <p>
- *** POTENTIAL INCOMPATIBILITY ***</p>
- <p>
- Own Id: OTP-14543 Aux Id: ERL-370 </p>
- </item>
- <item>
- <p> Make <c>io_lib:unscan_format/1</c> work with pad char
- and default precision. </p>
- <p>
- Own Id: OTP-14958 Aux Id: PR-1735 </p>
- </item>
- <item>
- <p> The control sequence modifiers <c>t</c> and <c>l</c>
- can be used together in the same control sequence which
- makes it possible to have Unicode atoms and no detection
- of printable character lists at the same time. </p>
- <p>
- Own Id: OTP-14971 Aux Id: PR-1743 </p>
- </item>
- <item>
- <p> Fix a bug in the Erlang code linter: the check of
- guard expressions no longer returns <c>false</c> if the
- map syntax is used. The bug affected the Erlang shell,
- the Debugger, and other modules evaluating abstract code.
- </p>
- <p>
- Own Id: OTP-15035 Aux Id: ERL-613 </p>
- </item>
- <item>
- <p>
- A sys debug fun of type {Fun,State} should not be
- possible to install twice. This was, however, possible if
- the current State was 'undefined', which was mistaken for
- non-existing fun. This has been corrected.</p>
- <p>
- Own Id: OTP-15049</p>
- </item>
- </list>
- </section>
-
-
- <section><title>Improvements and New Features</title>
- <list>
- <item>
- <p>
- The <c>gen_server</c> has gotten a new callback
- <c>handle_continue/2</c> for check pointing the state.
- This is useful at least when implementing behaviours on
- top of <c>gen_server</c> and for some start up scenarios.</p>
- <p>
- Own Id: OTP-13019 Aux Id: PR-1490 </p>
- </item>
- <item>
- <p> The semantics of timeout parameter
- <c>{clean_timeout,infinity}</c> to
- <c>gen_statem:call/3</c> has been changed to use a proxy
- process for the call. With this change
- <c>clean_timeout</c> implicates a proxy process with no
- exceptions. This may be a hard to observe
- incompatibility: in the presence of network problems a
- late reply could arrive in the caller's message queue
- when catching errors. That will not happen after this
- correction. </p><p> The semantics of timeout parameter
- <c>infinity</c> has not been changed. </p>
- <p>
- *** POTENTIAL INCOMPATIBILITY ***</p>
- <p>
- Own Id: OTP-13073 Aux Id: PR-1595 </p>
- </item>
- <item>
- <p>A new logging API is added to Erlang/OTP, see the
- <seealso
- marker="kernel:logger"><c>logger(3)</c></seealso> manual
- page, and section <seealso
- marker="kernel:logger_chapter">Logging</seealso> in the
- Kernel User's Guide.</p>
- <p>Calls to <c>error_logger</c> are automatically
- redirected to the new API, and legacy error logger event
- handlers can still be used. It is, however, recommended
- to use the Logger API directly when writing new code.</p>
- <p>Notice the following potential incompatibilities:</p>
- <list> <item><p>Kernel configuration parameters
- <c>error_logger</c> still works, but is overruled if the
- default handler's output destination is configured with
- Kernel configuration parameter <c>logger</c>.</p> <p>In
- general, parameters for configuring error logger are
- overwritten by new parameters for configuring
- Logger.</p></item> <item><p>The concept of SASL error
- logging is deprecated, meaning that by default the SASL
- application does not affect which log events are
- logged.</p> <p>By default, supervisor reports and crash
- reports are logged by the default Logger handler started
- by Kernel, and end up at the same destination (terminal
- or file) as other standard log event from Erlang/OTP.</p>
- <p>Progress reports are not logged by default, but can be
- enabled with the Kernel configuration parameter
- <c>logger_progress_reports</c>.</p> <p>To obtain
- backwards compatibility with the SASL error logging
- functionality from earlier releases, set Kernel
- configuration parameter <c>logger_sasl_compatible</c> to
- <c>true</c>. This prevents the default Logger handler
- from logging any supervisor-, crash-, or progress
- reports. Instead, SASL adds a separate Logger handler
- during application start, which takes care of these log
- events. The SASL configuration parameters
- <c>sasl_error_logger</c> and <c>sasl_errlog_type</c>
- specify the destination (terminal or file) and severity
- level to log for these events.</p></item></list>
- <p>
- *** POTENTIAL INCOMPATIBILITY ***</p>
- <p>
- Own Id: OTP-13295</p>
- </item>
- <item>
- <p> Add functions
- <c>calendar:system_time_to_local_time/2</c> and
- <c>calendar:system_time_to_universal_time/2</c>. </p>
- <p>
- Own Id: OTP-13413</p>
- </item>
- <item>
- <p> Functions <c>rand:uniform_real/0</c> and
- <c>rand:uniform_real_s/1</c> have been added. They
- produce uniformly distributed numbers in the range <c>0.0
- =&lt; X &lt; 1.0</c> that are as close to random real
- numbers as Normalized IEEE 754 Double Precision allows.
- Because the random real number exactly <c>0.0</c> is
- infinitely improbable they will never return exactly
- <c>0.0</c>. </p><p> These properties are useful when you
- need to call for example <c>math:log(X)</c> or <c>1 /
- X</c> on a random value <c>X</c>, since that will never
- fail with a number from these new functions. </p>
- <p>
- Own Id: OTP-13764 Aux Id: PR-1574 </p>
- </item>
- <item>
- <p>
- Added maps:iterator/0 and maps:next/1 to be used for
- iterating over the key-value associations in a map.</p>
- <p>
- Own Id: OTP-14012</p>
- </item>
- <item>
- <p>Changed the default behaviour of <c>.erlang</c>
- loading: <c>.erlang</c> is no longer loaded from the
- current directory. <c>c:erlangrc(PathList)</c> can be
- used to search and load an <c>.erlang</c> file from user
- specified directories.</p> <p><c>escript</c>,
- <c>erlc</c>, <c>dialyzer</c> and <c>typer</c> no longer
- load an <c>.erlang</c> at all.</p>
- <p>
- *** POTENTIAL INCOMPATIBILITY ***</p>
- <p>
- Own Id: OTP-14439</p>
- </item>
- <item>
- <p>
- Added new uri_string module to stdlib for handling URIs
- (RFC 3986).</p>
- <p>
- Own Id: OTP-14496</p>
- </item>
- <item>
- <p>
- Update Unicode specification to version 10.0.</p>
- <p>
- Own Id: OTP-14503</p>
- </item>
- <item>
- <p><c>filelib:wildcard()</c> now allows characters with a
- special meaning to be escaped using backslashes.</p>
- <p>This is an incompatible change, but note that the use
- of backslashes in wildcards would already work
- differently on Windows and Unix. Existing calls to
- <c>filelib:wildcard()</c> needs to be updated. On
- Windows, directory separators must always be written as a
- slash.</p>
- <p>
- *** POTENTIAL INCOMPATIBILITY ***</p>
- <p>
- Own Id: OTP-14577</p>
- </item>
- <item>
- <p>
- The supervisor now stores its child specifications in a
- map instead of a list. This causes a significant
- improvement when starting many children under a
- non-simple_one_for_one supervisor.</p>
- <p>
- Own Id: OTP-14586</p>
- </item>
- <item>
- <p> The <c>base64</c> module is optimized. </p> <p> Note
- that the functions <c>encode/1</c>, <c>decode/1</c>, and
- <c>mime_decode/1</c> fail unless called with an argument
- of the documented type. They used to accept any
- <c>iodata()</c>. </p>
- <p>
- Own Id: OTP-14624 Aux Id: PR-1565 </p>
- </item>
- <item>
- <p> Add function <c>lists:search/2</c>. </p>
- <p>
- Own Id: OTP-14675 Aux Id: PR-102 </p>
- </item>
- <item>
- <p>
- uri_string module extended with functions for handling
- application/x-www-form-urlencoded query strings based on
- the HTML5 specification.</p>
- <p>
- Own Id: OTP-14747</p>
- </item>
- <item>
- <p> Add functions
- <c>calendar:rfc3339_to_system_time/1,2</c> and
- <c>calendar:system_time_to_rfc3339/1,2</c>. </p>
- <p>
- Own Id: OTP-14764</p>
- </item>
- <item>
- <p> The stack traces returned by the functions of the
- <c>erl_eval</c> module more accurately reflect where the
- exception occurred. </p>
- <p>
- Own Id: OTP-14826 Aux Id: PR 1540 </p>
- </item>
- <item>
- <p> Add options <c>atime</c>, <c>mtime</c>, <c>ctime</c>,
- <c>uid</c>, and <c>gid</c> to the <c>erl_tar:add/3,4</c>
- functions. </p>
- <p>
- Own Id: OTP-14834 Aux Id: PR 1608 </p>
- </item>
- <item>
- <p>Added <c>ets:whereis/1</c> for retrieving the table
- identifier of a named table.</p>
- <p>
- Own Id: OTP-14884</p>
- </item>
- <item>
- <p>
- Improved URI normalization functions in the uri_string
- module.</p>
- <p>
- Own Id: OTP-14910</p>
- </item>
- <item>
- <p> The new functions <c>io_lib:fwrite/3</c> and
- <c>io_lib:format/3</c> take a third argument, an option
- list. The only option is <c>chars_limit</c>, which is
- used for limiting the number of returned characters. The
- limit is soft, which means that the number of returned
- characters exceeds the limit with at most a smallish
- amount. If the limit is set, the functions
- <c>format/3</c> and <c>fwrite/3</c> try to distribute the
- number of characters evenly over the control sequences
- <c>pPswW</c>. Furthermore, the control sequences
- <c>pPwP</c> try to distribute the number of characters
- evenly over substructures. </p> <p> A modification of the
- control sequences <c>pPwW</c> is that even if there is no
- limit on the number of returned characters, all
- associations of a map are printed to the same depth. The
- aim is to give a more consistent output as the order of
- map keys is not defined. As before, if the depth is less
- than the number of associations of a map, the selection
- of associations to print is arbitrary. </p>
- <p>
- Own Id: OTP-14983</p>
- </item>
- <item>
- <p> Add functions <c>ordsets:is_empty/1</c> and
- <c>sets:is_empty/1</c>. </p>
- <p>
- Own Id: OTP-14996 Aux Id: ERL-557, PR-1703 </p>
- </item>
- <item>
- <p>
- Improve performance of <c>string:uppercase/1</c>,
- <c>string:lowercase/1</c> and <c>string:casefold/1</c>
- when handling ASCII characters.</p>
- <p>
- Own Id: OTP-14998</p>
- </item>
- <item>
- <p>External funs with literal values for module, name,
- and arity (e.g. <c>erlang:abs/1</c>) are now treated as
- literals. That means more efficient code that produces
- less garbage on the heap.</p>
- <p>
- Own Id: OTP-15003</p>
- </item>
- <item>
- <p>
- sys:statistics(Pid,get) did not report 'out' messages
- from gen_server. This is now corrected.</p>
- <p>
- Own Id: OTP-15047</p>
- </item>
- <item>
- <p>
- A sys debug function can now have the format
- {Id,Fun,State} in addition to the old {Fun,State}. This
- allows installing multiple instances of a debug fun.</p>
- <p>
- Own Id: OTP-15048</p>
- </item>
- <item>
- <p> The <c>lib</c> module is removed:</p> <list
- type="bulleted"> <item><c>lib:error_message/2</c> is
- removed.</item> <item><c>lib:flush_receive/0</c> is
- removed.</item> <item><c>lib:nonl/1</c> is
- removed.</item> <item><c>lib:progname/0</c> is replaced
- by <c>ct:get_progname/0</c>.</item>
- <item><c>lib:send/2</c> is removed.</item>
- <item><c>lib:sendw/2</c> is removed.</item> </list>
- <p>
- *** POTENTIAL INCOMPATIBILITY ***</p>
- <p>
- Own Id: OTP-15072 Aux Id: PR 1786 </p>
- </item>
- <item>
- <p>
- Function <c>ets:delete_all_objects/1</c> now yields the
- scheduler thread for large tables that take significant
- time to clear. This to improve real time characteristics
- of other runnable processes.</p>
- <p>
- Own Id: OTP-15078</p>
- </item>
- </list>
- </section>
-
-</section>
-
<section><title>STDLIB 3.4.5</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/stdlib/src/array.erl b/lib/stdlib/src/array.erl
index a237eaa489..939b1fb488 100644
--- a/lib/stdlib/src/array.erl
+++ b/lib/stdlib/src/array.erl
@@ -290,7 +290,7 @@ new(Size, Fixed, Default) ->
end,
#array{size = Size, max = M, default = Default, elements = E}.
--spec find_max(integer(), integer()) -> integer().
+-spec find_max(integer(), non_neg_integer()) -> non_neg_integer().
find_max(I, M) when I >= M ->
find_max(I, ?extend(M));
diff --git a/lib/stdlib/src/erl_eval.erl b/lib/stdlib/src/erl_eval.erl
index 0f6d48b9a3..31c0e60fe1 100644
--- a/lib/stdlib/src/erl_eval.erl
+++ b/lib/stdlib/src/erl_eval.erl
@@ -29,7 +29,7 @@
-export([new_bindings/0,bindings/1,binding/2,add_binding/3,del_binding/2]).
-export([extended_parse_exprs/1, extended_parse_term/1,
subst_values_for_vars/2]).
--export([is_constant_expr/1, partial_eval/1]).
+-export([is_constant_expr/1, partial_eval/1, eval_str/1]).
%% Is used by standalone Erlang (escript).
%% Also used by shell.erl.
@@ -1557,6 +1557,50 @@ ev_expr({cons,_,H,T}) -> [ev_expr(H) | ev_expr(T)].
%% true = erl_internal:guard_bif(F, length(As)),
%% apply(erlang, F, [ev_expr(X) || X <- As]);
+%% eval_str(InStr) -> {ok, OutStr} | {error, ErrStr'}
+%% InStr must represent a body
+%% Note: If InStr is a binary it has to be a Latin-1 string.
+%% If you have a UTF-8 encoded binary you have to call
+%% unicode:characters_to_list/1 before the call to eval_str().
+
+-define(result(F,D), lists:flatten(io_lib:format(F, D))).
+
+-spec eval_str(string() | unicode:latin1_binary()) ->
+ {'ok', string()} | {'error', string()}.
+
+eval_str(Str) when is_list(Str) ->
+ case erl_scan:tokens([], Str, 0) of
+ {more, _} ->
+ {error, "Incomplete form (missing .<cr>)??"};
+ {done, {ok, Toks, _}, Rest} ->
+ case all_white(Rest) of
+ true ->
+ case erl_parse:parse_exprs(Toks) of
+ {ok, Exprs} ->
+ case catch erl_eval:exprs(Exprs, erl_eval:new_bindings()) of
+ {value, Val, _} ->
+ {ok, Val};
+ Other ->
+ {error, ?result("*** eval: ~p", [Other])}
+ end;
+ {error, {_Line, Mod, Args}} ->
+ Msg = ?result("*** ~ts",[Mod:format_error(Args)]),
+ {error, Msg}
+ end;
+ false ->
+ {error, ?result("Non-white space found after "
+ "end-of-form :~ts", [Rest])}
+ end
+ end;
+eval_str(Bin) when is_binary(Bin) ->
+ eval_str(binary_to_list(Bin)).
+
+all_white([$\s|T]) -> all_white(T);
+all_white([$\n|T]) -> all_white(T);
+all_white([$\t|T]) -> all_white(T);
+all_white([]) -> true;
+all_white(_) -> false.
+
ret_expr(_Old, New) ->
%% io:format("~w: reduced ~s => ~s~n",
%% [line(Old), erl_pp:expr(Old), erl_pp:expr(New)]),
diff --git a/lib/stdlib/src/gen_event.erl b/lib/stdlib/src/gen_event.erl
index 3ee2031d02..8213282867 100644
--- a/lib/stdlib/src/gen_event.erl
+++ b/lib/stdlib/src/gen_event.erl
@@ -589,7 +589,7 @@ server_update(Handler1, Func, Event, SName) ->
?LOG_WARNING(#{label=>{gen_event,no_handle_info},
module=>Mod1,
message=>Event},
- #{domain=>[beam,erlang,otp],
+ #{domain=>[otp],
report_cb=>fun gen_event:format_log/1,
error_logger=>#{tag=>warning_msg}}), % warningmap??
{ok, Handler1};
@@ -751,7 +751,7 @@ report_error(Handler, Reason, State, LastIn, SName) ->
state=>format_status(terminate,Handler#handler.module,
get(),State),
reason=>Reason},
- #{domain=>[beam,erlang,otp],
+ #{domain=>[otp],
report_cb=>fun gen_event:format_log/1,
error_logger=>#{tag=>error}}).
diff --git a/lib/stdlib/src/gen_fsm.erl b/lib/stdlib/src/gen_fsm.erl
index 1646186761..caaaf8fa2e 100644
--- a/lib/stdlib/src/gen_fsm.erl
+++ b/lib/stdlib/src/gen_fsm.erl
@@ -505,7 +505,7 @@ handle_msg(Msg, Parent, Name, StateName, StateData, Mod, _Time, HibernateAfterTi
?LOG_WARNING(#{label=>{gen_fsm,no_handle_info},
module=>Mod,
message=>Msg},
- #{domain=>[beam,erlang,otp],
+ #{domain=>[otp],
report_cb=>fun gen_fsm:format_log/1,
error_logger=>#{tag=>warning_msg}}),
loop(Parent, Name, StateName, StateData, Mod, infinity, HibernateAfterTimeout, []);
@@ -616,7 +616,7 @@ error_info(Reason, Name, Msg, StateName, StateData, Debug) ->
state_name=>StateName,
state_data=>StateData,
reason=>Reason},
- #{domain=>[beam,erlang,otp],
+ #{domain=>[otp],
report_cb=>fun gen_fsm:format_log/1,
error_logger=>#{tag=>error}}),
sys:print_log(Debug),
diff --git a/lib/stdlib/src/gen_server.erl b/lib/stdlib/src/gen_server.erl
index 09f77c0810..342cc2a8e3 100644
--- a/lib/stdlib/src/gen_server.erl
+++ b/lib/stdlib/src/gen_server.erl
@@ -645,7 +645,7 @@ try_dispatch(Mod, Func, Msg, State) ->
#{label=>{gen_server,no_handle_info},
module=>Mod,
message=>Msg},
- #{domain=>[beam,erlang,otp],
+ #{domain=>[otp],
report_cb=>fun gen_server:format_log/1,
error_logger=>#{tag=>warning_msg}}),
{ok, {noreply, State}};
@@ -891,7 +891,7 @@ error_info(Reason, Name, From, Msg, Mod, State, Debug) ->
state=>format_status(terminate, Mod, get(), State),
reason=>Reason,
client_info=>client_stacktrace(From)},
- #{domain=>[beam,erlang,otp],
+ #{domain=>[otp],
report_cb=>fun gen_server:format_log/1,
error_logger=>#{tag=>error}}),
sys:print_log(Debug),
diff --git a/lib/stdlib/src/gen_statem.erl b/lib/stdlib/src/gen_statem.erl
index b36b8cd5a5..19d22f24f0 100644
--- a/lib/stdlib/src/gen_statem.erl
+++ b/lib/stdlib/src/gen_statem.erl
@@ -1900,7 +1900,7 @@ error_info(
state_enter=>StateEnter,
state=>format_status(terminate, get(), S),
reason=>{Class,Reason,Stacktrace}},
- #{domain=>[beam,erlang,otp],
+ #{domain=>[otp],
report_cb=>fun gen_statem:format_log/1,
error_logger=>#{tag=>error}}).
diff --git a/lib/stdlib/src/io.erl b/lib/stdlib/src/io.erl
index f510f61e9f..5d5773c80c 100644
--- a/lib/stdlib/src/io.erl
+++ b/lib/stdlib/src/io.erl
@@ -86,7 +86,16 @@ put_chars(Chars) ->
CharData :: unicode:chardata().
put_chars(Io, Chars) ->
- o_request(Io, {put_chars,unicode,Chars}, put_chars).
+ put_chars(Io, unicode, Chars).
+
+%% This function is here to make the erlang:raise in o_request actually raise to
+%% a valid function.
+-spec put_chars(IoDevice, Encoding, CharData) -> 'ok' when
+ IoDevice :: device(),
+ Encoding :: unicode,
+ CharData :: unicode:chardata().
+put_chars(Io, Encoding, Chars) ->
+ o_request(Io, {put_chars,Encoding,Chars}, put_chars).
-spec nl() -> 'ok'.
diff --git a/lib/stdlib/src/io_lib_pretty.erl b/lib/stdlib/src/io_lib_pretty.erl
index 3d5a979b3e..dca1b37ef3 100644
--- a/lib/stdlib/src/io_lib_pretty.erl
+++ b/lib/stdlib/src/io_lib_pretty.erl
@@ -131,6 +131,8 @@ print(Term, Col, Ll, D, M0, T, RecDefFun, Enc, Str) when is_tuple(Term);
%% use Len as CHAR_MAX if M0 = -1
M = max_cs(M0, Len),
if
+ Ll =:= 0 ->
+ write(If);
Len < Ll - Col, Len =< M ->
%% write the whole thing on a single line when there is room
write(If);
diff --git a/lib/stdlib/src/maps.erl b/lib/stdlib/src/maps.erl
index a13f340709..a1634547f3 100644
--- a/lib/stdlib/src/maps.erl
+++ b/lib/stdlib/src/maps.erl
@@ -249,7 +249,7 @@ fold(Fun,Init,Map) when is_function(Fun,3), is_map(Map) ->
fold(Fun,Init,Iterator) when is_function(Fun,3), ?IS_ITERATOR(Iterator) ->
fold_1(Fun,Init,Iterator);
fold(Fun,Init,Map) ->
- erlang:error(error_type(Map),[Fun,Init,Map]).
+ erlang:error(error_type_iter(Map),[Fun,Init,Map]).
fold_1(Fun, Acc, Iter) ->
case next(Iter) of
@@ -272,7 +272,7 @@ map(Fun,Map) when is_function(Fun, 2), is_map(Map) ->
map(Fun,Iterator) when is_function(Fun, 2), ?IS_ITERATOR(Iterator) ->
maps:from_list(map_1(Fun, Iterator));
map(Fun,Map) ->
- erlang:error(error_type(Map),[Fun,Map]).
+ erlang:error(error_type_iter(Map),[Fun,Map]).
map_1(Fun, Iter) ->
case next(Iter) of
@@ -342,5 +342,8 @@ with(Ks,M) ->
erlang:error(error_type(M),[Ks,M]).
-error_type(M) when is_map(M); ?IS_ITERATOR(M) -> badarg;
+error_type(M) when is_map(M) -> badarg;
error_type(V) -> {badmap, V}.
+
+error_type_iter(M) when is_map(M); ?IS_ITERATOR(M) -> badarg;
+error_type_iter(V) -> {badmap, V}.
diff --git a/lib/stdlib/src/proc_lib.erl b/lib/stdlib/src/proc_lib.erl
index 5f14e78f91..cca1628aba 100644
--- a/lib/stdlib/src/proc_lib.erl
+++ b/lib/stdlib/src/proc_lib.erl
@@ -508,7 +508,7 @@ crash_report(Class, Reason, StartF, Stacktrace) ->
?LOG_ERROR(#{label=>{proc_lib,crash},
report=>[my_info(Class, Reason, StartF, Stacktrace),
linked_info(self())]},
- #{domain=>[beam,erlang,otp,sasl],
+ #{domain=>[otp,sasl],
report_cb=>fun proc_lib:report_cb/1,
logger_formatter=>#{title=>"CRASH REPORT"},
error_logger=>#{tag=>error_report,type=>crash_report}}).
diff --git a/lib/stdlib/src/string.erl b/lib/stdlib/src/string.erl
index f5d271c06d..cf48b882e4 100644
--- a/lib/stdlib/src/string.erl
+++ b/lib/stdlib/src/string.erl
@@ -691,9 +691,9 @@ uppercase_list(CPs0, Changed) ->
uppercase_bin(CP1, <<CP2/utf8, Bin/binary>>, _Changed)
when $a =< CP1, CP1 =< $z, CP2 < 256 ->
[CP1-32|uppercase_bin(CP2, Bin, true)];
-uppercase_bin(CP1, <<CP2/utf8, Bin/binary>>, _Changed)
+uppercase_bin(CP1, <<CP2/utf8, Bin/binary>>, Changed)
when CP1 < 128, CP2 < 256 ->
- [CP1|uppercase_bin(CP2, Bin, false)];
+ [CP1|uppercase_bin(CP2, Bin, Changed)];
uppercase_bin(CP1, Bin, Changed) ->
case unicode_util:uppercase([CP1|Bin]) of
[CP1|CPs] ->
@@ -732,9 +732,9 @@ lowercase_list(CPs0, Changed) ->
lowercase_bin(CP1, <<CP2/utf8, Bin/binary>>, _Changed)
when $A =< CP1, CP1 =< $Z, CP2 < 256 ->
[CP1+32|lowercase_bin(CP2, Bin, true)];
-lowercase_bin(CP1, <<CP2/utf8, Bin/binary>>, _Changed)
+lowercase_bin(CP1, <<CP2/utf8, Bin/binary>>, Changed)
when CP1 < 128, CP2 < 256 ->
- [CP1|lowercase_bin(CP2, Bin, false)];
+ [CP1|lowercase_bin(CP2, Bin, Changed)];
lowercase_bin(CP1, Bin, Changed) ->
case unicode_util:lowercase([CP1|Bin]) of
[CP1|CPs] ->
@@ -773,9 +773,9 @@ casefold_list(CPs0, Changed) ->
casefold_bin(CP1, <<CP2/utf8, Bin/binary>>, _Changed)
when $A =< CP1, CP1 =< $Z, CP2 < 256 ->
[CP1+32|casefold_bin(CP2, Bin, true)];
-casefold_bin(CP1, <<CP2/utf8, Bin/binary>>, _Changed)
+casefold_bin(CP1, <<CP2/utf8, Bin/binary>>, Changed)
when CP1 < 128, CP2 < 256 ->
- [CP1|casefold_bin(CP2, Bin, false)];
+ [CP1|casefold_bin(CP2, Bin, Changed)];
casefold_bin(CP1, Bin, Changed) ->
case unicode_util:casefold([CP1|Bin]) of
[CP1|CPs] ->
diff --git a/lib/stdlib/src/supervisor.erl b/lib/stdlib/src/supervisor.erl
index eb46ac611a..9b6c2a5f0b 100644
--- a/lib/stdlib/src/supervisor.erl
+++ b/lib/stdlib/src/supervisor.erl
@@ -43,7 +43,7 @@
{errorContext,Error},
{reason,Reason},
{offender,extract_child(Child)}]},
- #{domain=>[beam,erlang,otp,sasl],
+ #{domain=>[otp,sasl],
report_cb=>fun logger:format_otp_report/1,
logger_formatter=>#{title=>"SUPERVISOR REPORT"},
error_logger=>#{tag=>error_report,
@@ -580,7 +580,7 @@ handle_info({'EXIT', Pid, Reason}, State) ->
handle_info(Msg, State) ->
?LOG_ERROR("Supervisor received unexpected message: ~tp~n",[Msg],
- #{domain=>[beam,erlang,otp],
+ #{domain=>[otp],
error_logger=>#{tag=>error}}),
{noreply, State}.
@@ -1419,7 +1419,7 @@ report_progress(Child, SupName) ->
?LOG_INFO(#{label=>{supervisor,progress},
report=>[{supervisor,SupName},
{started,extract_child(Child)}]},
- #{domain=>[beam,erlang,otp,sasl],
+ #{domain=>[otp,sasl],
report_cb=>fun logger:format_otp_report/1,
logger_formatter=>#{title=>"PROGRESS REPORT"},
error_logger=>#{tag=>info_report,type=>progress}}).
diff --git a/lib/stdlib/src/supervisor_bridge.erl b/lib/stdlib/src/supervisor_bridge.erl
index 39372935fa..2db0a895d6 100644
--- a/lib/stdlib/src/supervisor_bridge.erl
+++ b/lib/stdlib/src/supervisor_bridge.erl
@@ -135,7 +135,7 @@ report_progress(Pid, Mod, StartArgs, SupName) ->
report=>[{supervisor, SupName},
{started, [{pid, Pid},
{mfa, {Mod, init, [StartArgs]}}]}]},
- #{domain=>[beam,erlang,otp,sasl],
+ #{domain=>[otp,sasl],
report_cb=>fun logger:format_otp_report/1,
logger_formatter=>#{title=>"PROGRESS REPORT"},
error_logger=>#{tag=>info_report,type=>progress}}).
@@ -146,7 +146,7 @@ report_error(Error, Reason, #state{name = Name, pid = Pid, mod = Mod}) ->
{errorContext, Error},
{reason, Reason},
{offender, [{pid, Pid}, {mod, Mod}]}]},
- #{domain=>[beam,erlang,otp,sasl],
+ #{domain=>[otp,sasl],
report_cb=>fun logger:format_otp_report/1,
logger_formatter=>#{title=>"SUPERVISOR REPORT"},
error_logger=>#{tag=>error_report,type=>supervisor_report}}).
diff --git a/lib/stdlib/src/uri_string.erl b/lib/stdlib/src/uri_string.erl
index 28d36ea229..48cce90d68 100644
--- a/lib/stdlib/src/uri_string.erl
+++ b/lib/stdlib/src/uri_string.erl
@@ -297,7 +297,10 @@
NormalizedURI :: uri_string()
| error().
normalize(URIMap) ->
- normalize(URIMap, []).
+ try normalize(URIMap, [])
+ catch
+ throw:{error, Atom, RestData} -> {error, Atom, RestData}
+ end.
-spec normalize(URI, Options) -> NormalizedURI when
@@ -523,34 +526,34 @@ parse_relative_part(?STRING_REST("//", Rest), URI) ->
{T, URI1} ->
Userinfo = calculate_parsed_userinfo(Rest, T),
URI2 = maybe_add_path(URI1),
- URI2#{userinfo => decode_userinfo(Userinfo)}
+ URI2#{userinfo => Userinfo}
catch
throw:{_,_,_} ->
{T, URI1} = parse_host(Rest, URI),
Host = calculate_parsed_host_port(Rest, T),
URI2 = maybe_add_path(URI1),
- URI2#{host => decode_host(remove_brackets(Host))}
+ URI2#{host => remove_brackets(Host)}
end;
parse_relative_part(?STRING_REST($/, Rest), URI) ->
{T, URI1} = parse_segment(Rest, URI), % path-absolute
Path = calculate_parsed_part(Rest, T),
- URI1#{path => decode_path(?STRING_REST($/, Path))};
+ URI1#{path => ?STRING_REST($/, Path)};
parse_relative_part(?STRING_REST($?, Rest), URI) ->
{T, URI1} = parse_query(Rest, URI), % path-empty ?query
Query = calculate_parsed_query_fragment(Rest, T),
URI2 = maybe_add_path(URI1),
- URI2#{query => decode_query(Query)};
+ URI2#{query => Query};
parse_relative_part(?STRING_REST($#, Rest), URI) ->
{T, URI1} = parse_fragment(Rest, URI), % path-empty
Fragment = calculate_parsed_query_fragment(Rest, T),
URI2 = maybe_add_path(URI1),
- URI2#{fragment => decode_fragment(Fragment)};
+ URI2#{fragment => Fragment};
parse_relative_part(?STRING_REST(Char, Rest), URI) ->
case is_segment_nz_nc(Char) of
true ->
{T, URI1} = parse_segment_nz_nc(Rest, URI), % path-noscheme
Path = calculate_parsed_part(Rest, T),
- URI1#{path => decode_path(?STRING_REST(Char, Path))};
+ URI1#{path => ?STRING_REST(Char, Path)};
false -> throw({error,invalid_uri,[Char]})
end.
@@ -593,11 +596,11 @@ parse_segment(?STRING_REST($/, Rest), URI) ->
parse_segment(?STRING_REST($?, Rest), URI) ->
{T, URI1} = parse_query(Rest, URI), % ?query
Query = calculate_parsed_query_fragment(Rest, T),
- {Rest, URI1#{query => decode_query(Query)}};
+ {Rest, URI1#{query => Query}};
parse_segment(?STRING_REST($#, Rest), URI) ->
{T, URI1} = parse_fragment(Rest, URI),
Fragment = calculate_parsed_query_fragment(Rest, T),
- {Rest, URI1#{fragment => decode_fragment(Fragment)}};
+ {Rest, URI1#{fragment => Fragment}};
parse_segment(?STRING_REST(Char, Rest), URI) ->
case is_pchar(Char) of
true -> parse_segment(Rest, URI);
@@ -616,11 +619,11 @@ parse_segment_nz_nc(?STRING_REST($/, Rest), URI) ->
parse_segment_nz_nc(?STRING_REST($?, Rest), URI) ->
{T, URI1} = parse_query(Rest, URI), % ?query
Query = calculate_parsed_query_fragment(Rest, T),
- {Rest, URI1#{query => decode_query(Query)}};
+ {Rest, URI1#{query => Query}};
parse_segment_nz_nc(?STRING_REST($#, Rest), URI) ->
{T, URI1} = parse_fragment(Rest, URI),
Fragment = calculate_parsed_query_fragment(Rest, T),
- {Rest, URI1#{fragment => decode_fragment(Fragment)}};
+ {Rest, URI1#{fragment => Fragment}};
parse_segment_nz_nc(?STRING_REST(Char, Rest), URI) ->
case is_segment_nz_nc(Char) of
true -> parse_segment_nz_nc(Rest, URI);
@@ -709,31 +712,31 @@ parse_hier(?STRING_REST("//", Rest), URI) ->
try parse_userinfo(Rest, URI) of
{T, URI1} ->
Userinfo = calculate_parsed_userinfo(Rest, T),
- {Rest, URI1#{userinfo => decode_userinfo(Userinfo)}}
+ {Rest, URI1#{userinfo => Userinfo}}
catch
throw:{_,_,_} ->
{T, URI1} = parse_host(Rest, URI),
Host = calculate_parsed_host_port(Rest, T),
- {Rest, URI1#{host => decode_host(remove_brackets(Host))}}
+ {Rest, URI1#{host => remove_brackets(Host)}}
end;
parse_hier(?STRING_REST($/, Rest), URI) ->
{T, URI1} = parse_segment(Rest, URI), % path-absolute
Path = calculate_parsed_part(Rest, T),
- {Rest, URI1#{path => decode_path(?STRING_REST($/, Path))}};
+ {Rest, URI1#{path => ?STRING_REST($/, Path)}};
parse_hier(?STRING_REST($?, Rest), URI) ->
{T, URI1} = parse_query(Rest, URI), % path-empty ?query
Query = calculate_parsed_query_fragment(Rest, T),
- {Rest, URI1#{query => decode_query(Query)}};
+ {Rest, URI1#{query => Query}};
parse_hier(?STRING_REST($#, Rest), URI) ->
{T, URI1} = parse_fragment(Rest, URI), % path-empty
Fragment = calculate_parsed_query_fragment(Rest, T),
- {Rest, URI1#{fragment => decode_fragment(Fragment)}};
+ {Rest, URI1#{fragment => Fragment}};
parse_hier(?STRING_REST(Char, Rest), URI) -> % path-rootless
case is_pchar(Char) of
true -> % segment_nz
{T, URI1} = parse_segment(Rest, URI),
Path = calculate_parsed_part(Rest, T),
- {Rest, URI1#{path => decode_path(?STRING_REST(Char, Path))}};
+ {Rest, URI1#{path => ?STRING_REST(Char, Path)}};
false -> throw({error,invalid_uri,[Char]})
end;
parse_hier(?STRING_EMPTY, URI) ->
@@ -770,7 +773,7 @@ parse_userinfo(?CHAR($@), URI) ->
parse_userinfo(?STRING_REST($@, Rest), URI) ->
{T, URI1} = parse_host(Rest, URI),
Host = calculate_parsed_host_port(Rest, T),
- {Rest, URI1#{host => decode_host(remove_brackets(Host))}};
+ {Rest, URI1#{host => remove_brackets(Host)}};
parse_userinfo(?STRING_REST(Char, Rest), URI) ->
case is_userinfo(Char) of
true -> parse_userinfo(Rest, URI);
@@ -836,20 +839,25 @@ parse_host(?STRING_REST($:, Rest), URI) ->
parse_host(?STRING_REST($/, Rest), URI) ->
{T, URI1} = parse_segment(Rest, URI), % path-abempty
Path = calculate_parsed_part(Rest, T),
- {Rest, URI1#{path => decode_path(?STRING_REST($/, Path))}};
+ {Rest, URI1#{path => ?STRING_REST($/, Path)}};
parse_host(?STRING_REST($?, Rest), URI) ->
{T, URI1} = parse_query(Rest, URI), % path-empty ?query
Query = calculate_parsed_query_fragment(Rest, T),
- {Rest, URI1#{query => decode_query(Query)}};
+ {Rest, URI1#{query => Query}};
parse_host(?STRING_REST($[, Rest), URI) ->
parse_ipv6_bin(Rest, [], URI);
parse_host(?STRING_REST($#, Rest), URI) ->
{T, URI1} = parse_fragment(Rest, URI), % path-empty
Fragment = calculate_parsed_query_fragment(Rest, T),
- {Rest, URI1#{fragment => decode_fragment(Fragment)}};
+ {Rest, URI1#{fragment => Fragment}};
parse_host(?STRING_REST(Char, Rest), URI) ->
case is_digit(Char) of
- true -> parse_ipv4_bin(Rest, [Char], URI);
+ true ->
+ try parse_ipv4_bin(Rest, [Char], URI)
+ catch
+ throw:{_,_,_} ->
+ parse_reg_name(?STRING_REST(Char, Rest), URI)
+ end;
false -> parse_reg_name(?STRING_REST(Char, Rest), URI)
end;
parse_host(?STRING_EMPTY, URI) ->
@@ -865,15 +873,15 @@ parse_reg_name(?STRING_REST($:, Rest), URI) ->
parse_reg_name(?STRING_REST($/, Rest), URI) ->
{T, URI1} = parse_segment(Rest, URI), % path-abempty
Path = calculate_parsed_part(Rest, T),
- {Rest, URI1#{path => decode_path(?STRING_REST($/, Path))}};
+ {Rest, URI1#{path => ?STRING_REST($/, Path)}};
parse_reg_name(?STRING_REST($?, Rest), URI) ->
{T, URI1} = parse_query(Rest, URI), % path-empty ?query
Query = calculate_parsed_query_fragment(Rest, T),
- {Rest, URI1#{query => decode_query(Query)}};
+ {Rest, URI1#{query => Query}};
parse_reg_name(?STRING_REST($#, Rest), URI) ->
{T, URI1} = parse_fragment(Rest, URI), % path-empty
Fragment = calculate_parsed_query_fragment(Rest, T),
- {Rest, URI1#{fragment => decode_fragment(Fragment)}};
+ {Rest, URI1#{fragment => Fragment}};
parse_reg_name(?STRING_REST(Char, Rest), URI) ->
case is_reg_name(Char) of
true -> parse_reg_name(Rest, URI);
@@ -899,17 +907,17 @@ parse_ipv4_bin(?STRING_REST($/, Rest), Acc, URI) ->
_ = validate_ipv4_address(lists:reverse(Acc)),
{T, URI1} = parse_segment(Rest, URI), % path-abempty
Path = calculate_parsed_part(Rest, T),
- {Rest, URI1#{path => decode_path(?STRING_REST($/, Path))}};
+ {Rest, URI1#{path => ?STRING_REST($/, Path)}};
parse_ipv4_bin(?STRING_REST($?, Rest), Acc, URI) ->
_ = validate_ipv4_address(lists:reverse(Acc)),
{T, URI1} = parse_query(Rest, URI), % path-empty ?query
Query = calculate_parsed_query_fragment(Rest, T),
- {Rest, URI1#{query => decode_query(Query)}};
+ {Rest, URI1#{query => Query}};
parse_ipv4_bin(?STRING_REST($#, Rest), Acc, URI) ->
_ = validate_ipv4_address(lists:reverse(Acc)),
{T, URI1} = parse_fragment(Rest, URI), % path-empty
Fragment = calculate_parsed_query_fragment(Rest, T),
- {Rest, URI1#{fragment => decode_fragment(Fragment)}};
+ {Rest, URI1#{fragment => Fragment}};
parse_ipv4_bin(?STRING_REST(Char, Rest), Acc, URI) ->
case is_ipv4(Char) of
true -> parse_ipv4_bin(Rest, [Char|Acc], URI);
@@ -961,15 +969,15 @@ parse_ipv6_bin_end(?STRING_REST($:, Rest), URI) ->
parse_ipv6_bin_end(?STRING_REST($/, Rest), URI) ->
{T, URI1} = parse_segment(Rest, URI), % path-abempty
Path = calculate_parsed_part(Rest, T),
- {Rest, URI1#{path => decode_path(?STRING_REST($/, Path))}};
+ {Rest, URI1#{path => ?STRING_REST($/, Path)}};
parse_ipv6_bin_end(?STRING_REST($?, Rest), URI) ->
{T, URI1} = parse_query(Rest, URI), % path-empty ?query
Query = calculate_parsed_query_fragment(Rest, T),
- {Rest, URI1#{query => decode_query(Query)}};
+ {Rest, URI1#{query => Query}};
parse_ipv6_bin_end(?STRING_REST($#, Rest), URI) ->
{T, URI1} = parse_fragment(Rest, URI), % path-empty
Fragment = calculate_parsed_query_fragment(Rest, T),
- {Rest, URI1#{fragment => decode_fragment(Fragment)}};
+ {Rest, URI1#{fragment => Fragment}};
parse_ipv6_bin_end(?STRING_REST(Char, Rest), URI) ->
case is_ipv6(Char) of
true -> parse_ipv6_bin_end(Rest, URI);
@@ -999,15 +1007,15 @@ validate_ipv6_address(Addr) ->
parse_port(?STRING_REST($/, Rest), URI) ->
{T, URI1} = parse_segment(Rest, URI), % path-abempty
Path = calculate_parsed_part(Rest, T),
- {Rest, URI1#{path => decode_path(?STRING_REST($/, Path))}};
+ {Rest, URI1#{path => ?STRING_REST($/, Path)}};
parse_port(?STRING_REST($?, Rest), URI) ->
{T, URI1} = parse_query(Rest, URI), % path-empty ?query
Query = calculate_parsed_query_fragment(Rest, T),
- {Rest, URI1#{query => decode_query(Query)}};
+ {Rest, URI1#{query => Query}};
parse_port(?STRING_REST($#, Rest), URI) ->
{T, URI1} = parse_fragment(Rest, URI), % path-empty
Fragment = calculate_parsed_query_fragment(Rest, T),
- {Rest, URI1#{fragment => decode_fragment(Fragment)}};
+ {Rest, URI1#{fragment => Fragment}};
parse_port(?STRING_REST(Char, Rest), URI) ->
case is_digit(Char) of
true -> parse_port(Rest, URI);
@@ -1033,7 +1041,7 @@ parse_port(?STRING_EMPTY, URI) ->
parse_query(?STRING_REST($#, Rest), URI) ->
{T, URI1} = parse_fragment(Rest, URI),
Fragment = calculate_parsed_query_fragment(Rest, T),
- {Rest, URI1#{fragment => decode_fragment(Fragment)}};
+ {Rest, URI1#{fragment => Fragment}};
parse_query(?STRING_REST(Char, Rest), URI) ->
case is_query(Char) of
true -> parse_query(Rest, URI);
@@ -1088,6 +1096,31 @@ is_fragment(Char) -> is_pchar(Char).
%%
%%-------------------------------------------------------------------------
+%% Return true if input char is reserved.
+-spec is_reserved(char()) -> boolean().
+is_reserved($:) -> true;
+is_reserved($/) -> true;
+is_reserved($?) -> true;
+is_reserved($#) -> true;
+is_reserved($[) -> true;
+is_reserved($]) -> true;
+is_reserved($@) -> true;
+
+is_reserved($!) -> true;
+is_reserved($$) -> true;
+is_reserved($&) -> true;
+is_reserved($') -> true;
+is_reserved($() -> true;
+is_reserved($)) -> true;
+
+is_reserved($*) -> true;
+is_reserved($+) -> true;
+is_reserved($,) -> true;
+is_reserved($;) -> true;
+is_reserved($=) -> true;
+is_reserved(_) -> false.
+
+
%% Check if char is sub-delim.
-spec is_sub_delim(char()) -> boolean().
is_sub_delim($!) -> true;
@@ -1276,36 +1309,6 @@ byte_size_exl_head(Binary) -> byte_size(Binary) + 1.
%%
%% pct-encoded = "%" HEXDIG HEXDIG
%%-------------------------------------------------------------------------
--spec decode_userinfo(binary()) -> binary().
-decode_userinfo(Cs) ->
- check_utf8(decode(Cs, fun is_userinfo/1, <<>>)).
-
--spec decode_host(binary()) -> binary().
-decode_host(Cs) ->
- check_utf8(decode(Cs, fun is_host/1, <<>>)).
-
--spec decode_path(binary()) -> binary().
-decode_path(Cs) ->
- check_utf8(decode(Cs, fun is_path/1, <<>>)).
-
--spec decode_query(binary()) -> binary().
-decode_query(Cs) ->
- check_utf8(decode(Cs, fun is_query/1, <<>>)).
-
--spec decode_fragment(binary()) -> binary().
-decode_fragment(Cs) ->
- check_utf8(decode(Cs, fun is_fragment/1, <<>>)).
-
-
-%% Returns Cs if it is utf8 encoded.
-check_utf8(Cs) ->
- case unicode:characters_to_list(Cs) of
- {incomplete,_,_} ->
- throw({error,invalid_utf8,Cs});
- {error,_,_} ->
- throw({error,invalid_utf8,Cs});
- _ -> Cs
- end.
%%-------------------------------------------------------------------------
%% Percent-encode
@@ -1351,20 +1354,56 @@ encode_fragment(Cs) ->
%%-------------------------------------------------------------------------
%% Helper funtions for percent-decode
%%-------------------------------------------------------------------------
-decode(<<$%,C0,C1,Cs/binary>>, Fun, Acc) ->
+
+-spec decode(list()|binary()) -> list() | binary().
+decode(Cs) ->
+ decode(Cs, <<>>).
+%%
+decode(L, Acc) when is_list(L) ->
+ B0 = unicode:characters_to_binary(L),
+ B1 = decode(B0, Acc),
+ unicode:characters_to_list(B1);
+decode(<<$%,C0,C1,Cs/binary>>, Acc) ->
case is_hex_digit(C0) andalso is_hex_digit(C1) of
true ->
B = ?HEX2DEC(C0)*16+?HEX2DEC(C1),
- decode(Cs, Fun, <<Acc/binary, B>>);
+ case is_reserved(B) of
+ true ->
+ %% [2.2] Characters in the reserved set are protected from
+ %% normalization.
+ %% [2.1] For consistency, URI producers and normalizers should
+ %% use uppercase hexadecimal digits for all percent-
+ %% encodings.
+ H0 = hex_to_upper(C0),
+ H1 = hex_to_upper(C1),
+ decode(Cs, <<Acc/binary,$%,H0,H1>>);
+ false ->
+ decode(Cs, <<Acc/binary, B>>)
+ end;
false -> throw({error,invalid_percent_encoding,<<$%,C0,C1>>})
end;
-decode(<<C,Cs/binary>>, Fun, Acc) ->
- case Fun(C) of
- true -> decode(Cs, Fun, <<Acc/binary, C>>);
- false -> throw({error,invalid_percent_encoding,<<C,Cs/binary>>})
- end;
-decode(<<>>, _Fun, Acc) ->
- Acc.
+decode(<<C,Cs/binary>>, Acc) ->
+ decode(Cs, <<Acc/binary, C>>);
+decode(<<>>, Acc) ->
+ check_utf8(Acc).
+
+%% Returns Cs if it is utf8 encoded.
+check_utf8(Cs) ->
+ case unicode:characters_to_list(Cs) of
+ {incomplete,_,_} ->
+ throw({error,invalid_utf8,Cs});
+ {error,_,_} ->
+ throw({error,invalid_utf8,Cs});
+ _ -> Cs
+ end.
+
+%% Convert hex digit to uppercase form
+hex_to_upper(H) when $a =< H, H =< $f ->
+ H - 32;
+hex_to_upper(H) when $0 =< H, H =< $9;$A =< H, H =< $F->
+ H;
+hex_to_upper(H) ->
+ throw({error,invalid_input, H}).
%% Check if char is allowed in host
-spec is_host(char()) -> boolean().
@@ -1925,9 +1964,10 @@ base10_decode_unicode(<<H,_/binary>>, _, _) ->
%%-------------------------------------------------------------------------
normalize_map(URIMap) ->
- normalize_path_segment(
- normalize_scheme_based(
- normalize_case(URIMap))).
+ normalize_path_segment(
+ normalize_scheme_based(
+ normalize_percent_encoding(
+ normalize_case(URIMap)))).
%% 6.2.2.1. Case Normalization
@@ -1942,6 +1982,18 @@ normalize_case(#{} = Map) ->
Map.
+%% 6.2.2.2. Percent-Encoding Normalization
+normalize_percent_encoding(Map) ->
+ Fun = fun (K,V) when K =:= userinfo; K =:= host; K =:= path;
+ K =:= query; K =:= fragment ->
+ decode(V);
+ %% Handle port and scheme
+ (_,V) ->
+ V
+ end,
+ maps:map(Fun, Map).
+
+
to_lower(Cs) when is_list(Cs) ->
B = convert_to_binary(Cs, utf8, utf8),
convert_to_list(to_lower(B), utf8);
diff --git a/lib/stdlib/test/Makefile b/lib/stdlib/test/Makefile
index ae2e3d0e2b..bbe3cefa42 100644
--- a/lib/stdlib/test/Makefile
+++ b/lib/stdlib/test/Makefile
@@ -88,6 +88,7 @@ MODULES= \
unicode_SUITE \
unicode_util_SUITE \
uri_string_SUITE \
+ uri_string_property_test_SUITE \
win32reg_SUITE \
y2k_SUITE \
select_SUITE \
@@ -152,6 +153,6 @@ release_tests_spec: make_emakefile
$(INSTALL_DATA) stdlib.spec stdlib_bench.spec $(EMAKEFILE) \
$(ERL_FILES) $(COVERFILE) "$(RELSYSDIR)"
chmod -R u+w "$(RELSYSDIR)"
- @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -)
+ @tar cf - *_SUITE_data property_test | (cd "$(RELSYSDIR)"; tar xf -)
release_docs_spec:
diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl
index 574aac96c8..a97fe4a5d9 100644
--- a/lib/stdlib/test/ets_SUITE.erl
+++ b/lib/stdlib/test/ets_SUITE.erl
@@ -6209,20 +6209,23 @@ spawn_logger(Procs) ->
ok;
(Proc) ->
Mon = erlang:monitor(process, Proc),
- receive
+ ok = receive
{'DOWN', Mon, _, _, _} ->
ok
after 0 ->
case Kill of
true -> exit(Proc, kill);
- _ ->
- erlang:display({"Waiting for 'DOWN' from", Proc,
- process_info(Proc),
- pid_status(Proc)})
+ _ -> ok
end,
receive
{'DOWN', Mon, _, _, _} ->
ok
+ after 5000 ->
+ io:format("Waiting for 'DOWN' from ~w, status=~w\n"
+ "info = ~p\n", [Proc,
+ pid_status(Proc),
+ process_info(Proc)]),
+ timeout
end
end
end, Procs),
diff --git a/lib/stdlib/test/io_SUITE.erl b/lib/stdlib/test/io_SUITE.erl
index 13f2cbd27b..91fe1133f6 100644
--- a/lib/stdlib/test/io_SUITE.erl
+++ b/lib/stdlib/test/io_SUITE.erl
@@ -31,7 +31,7 @@
otp_10836/1, io_lib_width_too_small/1,
io_with_huge_message_queue/1, format_string/1,
maps/1, coverage/1, otp_14178_unicode_atoms/1, otp_14175/1,
- otp_14285/1, limit_term/1, otp_14983/1]).
+ otp_14285/1, limit_term/1, otp_14983/1, otp_15103/1]).
-export([pretty/2, trf/3]).
@@ -63,7 +63,7 @@ all() ->
io_lib_print_binary_depth_one, otp_10302, otp_10755, otp_10836,
io_lib_width_too_small, io_with_huge_message_queue,
format_string, maps, coverage, otp_14178_unicode_atoms, otp_14175,
- otp_14285, limit_term, otp_14983].
+ otp_14285, limit_term, otp_14983, otp_15103].
%% Error cases for output.
error_1(Config) when is_list(Config) ->
@@ -2615,3 +2615,21 @@ trf(Format, Args, T) ->
trf(Format, Args, T, Opts) ->
lists:flatten(io_lib:format(Format, Args, [{chars_limit, T}|Opts])).
+
+otp_15103(_Config) ->
+ T = lists:duplicate(5, {a,b,c}),
+
+ S1 = io_lib:format("~0p", [T]),
+ "[{a,b,c},{a,b,c},{a,b,c},{a,b,c},{a,b,c}]" = lists:flatten(S1),
+ S2 = io_lib:format("~-0p", [T]),
+ "[{a,b,c},{a,b,c},{a,b,c},{a,b,c},{a,b,c}]" = lists:flatten(S2),
+ S3 = io_lib:format("~1p", [T]),
+ "[{a,\n b,\n c},\n {a,\n b,\n c},\n {a,\n b,\n c},\n {a,\n b,\n"
+ " c},\n {a,\n b,\n c}]" = lists:flatten(S3),
+
+ S4 = io_lib:format("~0P", [T, 5]),
+ "[{a,b,c},{a,b,...},{a,...},{...}|...]" = lists:flatten(S4),
+ S5 = io_lib:format("~1P", [T, 5]),
+ "[{a,\n b,\n c},\n {a,\n b,...},\n {a,...},\n {...}|...]" =
+ lists:flatten(S5),
+ ok.
diff --git a/lib/stdlib/test/maps_SUITE.erl b/lib/stdlib/test/maps_SUITE.erl
index a75751b31d..6374daa1d3 100644
--- a/lib/stdlib/test/maps_SUITE.erl
+++ b/lib/stdlib/test/maps_SUITE.erl
@@ -108,6 +108,8 @@ t_without_2(_Config) ->
%% error case
?badmap(a,without,[[a,b],a]) = (catch maps:without([a,b],id(a))),
?badmap(a,without,[{a,b},a]) = (catch maps:without({a,b},id(a))),
+ ?badmap({0,<<>>,97},without,[[],{0,<<>>,97}]) = (catch maps:without([], {0,<<>>,97})),
+ ?badmap({0,<<>>,97},without,[[false, -20, -8],{0,<<>>,97}]) = (catch maps:without([false, -20, -8], {0, <<>>, 97})),
?badarg(without,[a,#{}]) = (catch maps:without(a,#{})),
ok.
@@ -120,6 +122,8 @@ t_with_2(_Config) ->
%% error case
?badmap(a,with,[[a,b],a]) = (catch maps:with([a,b],id(a))),
?badmap(a,with,[{a,b},a]) = (catch maps:with({a,b},id(a))),
+ ?badmap({0,<<>>,97},with,[[],{0,<<>>,97}]) = (catch maps:with([], {0,<<>>,97})),
+ ?badmap({0,<<>>,97},with,[[false, -20, -8],{0,<<>>,97}]) = (catch maps:with([false, -20, -8], {0, <<>>, 97})),
?badarg(with,[a,#{}]) = (catch maps:with(a,#{})),
ok.
diff --git a/lib/stdlib/test/proc_lib_SUITE.erl b/lib/stdlib/test/proc_lib_SUITE.erl
index 81bf9020b8..a283aa992f 100644
--- a/lib/stdlib/test/proc_lib_SUITE.erl
+++ b/lib/stdlib/test/proc_lib_SUITE.erl
@@ -542,13 +542,14 @@ system_terminate(Reason,_Parent,_Deb,_State) ->
t_format(_Config) ->
- logger:add_handler_filter(logger_std_h,stop_all,{fun(_,_) -> stop end,ok}),
+ {ok,#{level:=Level}} = logger:get_handler_config(default),
+ logger:set_handler_config(default,level,none),
error_logger:add_report_handler(?MODULE, self()),
try
t_format()
after
error_logger:delete_report_handler(?MODULE),
- logger:remove_handler_filter(logger_std_h,stop_all)
+ logger:set_handler_config(default,level,Level)
end,
ok.
@@ -585,11 +586,12 @@ t_format() ->
ok.
t_format_arbitrary(_Config) ->
- logger:add_handler_filter(logger_std_h,stop_all,{fun(_,_) -> stop end,ok}),
+ {ok,#{level:=Level}} = logger:get_handler_config(default),
+ logger:set_handler_config(default,level,none),
try
t_format_arbitrary()
after
- logger:remove_handler_filter(logger_std_h,stop_all)
+ logger:set_handler_config(default,level,Level)
end,
ok.
diff --git a/lib/stdlib/test/property_test/uri_string_recompose.erl b/lib/stdlib/test/property_test/uri_string_recompose.erl
index e51a671172..35b3a50b9c 100644
--- a/lib/stdlib/test/property_test/uri_string_recompose.erl
+++ b/lib/stdlib/test/property_test/uri_string_recompose.erl
@@ -65,15 +65,29 @@
-define(QUERY, {query, query_map()}).
-define(FRAGMENT, {fragment, fragment_map()}).
+%% Non-unicode
+-define(USER_NU, {userinfo, non_unicode()}).
+-define(HOST_NU, {host, host_map_nu()}).
+-define(PATH_ABE_NU, {path, path_abempty_map_nu()}).
+-define(PATH_ABS_NU, {path, path_absolute_map_nu()}).
+-define(PATH_NOS_NU, {path, path_noscheme_map_nu()}).
+-define(PATH_ROO_NU, {path, path_rootless_map_nu()}).
+-define(QUERY_NU, {query, query_map_nu()}).
+-define(FRAGMENT_NU, {fragment, fragment_map_nu()}).
%%%========================================================================
%%% Properties
%%%========================================================================
prop_recompose() ->
+ ?FORALL(Map, map_no_unicode(),
+ Map =:= uri_string:parse(uri_string:recompose(Map))).
+
+prop_normalize() ->
?FORALL(Map, map(),
- Map =:= uri_string:parse(uri_string:recompose(Map))
- ).
+ uri_string:normalize(Map, [return_map]) =:=
+ uri_string:normalize(uri_string:parse(uri_string:recompose(Map)),
+ [return_map])).
%% Stats
prop_map_key_length_collect() ->
@@ -96,6 +110,9 @@ prop_scheme_collect() ->
map() ->
?LET(Gen, comp_proplist(), proplist_to_map(Gen)).
+map_no_unicode() ->
+ ?LET(Gen, comp_proplist_nu(), proplist_to_map(Gen)).
+
comp_proplist() ->
frequency([
{2, [?SCHEME,?PATH_ABS]},
@@ -166,6 +183,76 @@ comp_proplist() ->
{2, [?USER,?HOST,?PORT,?PATH_ABE,?QUERY,?FRAGMENT]}
]).
+comp_proplist_nu() ->
+ frequency([
+ {2, [?SCHEME,?PATH_ABS_NU]},
+ {2, [?SCHEME,?PATH_ROO_NU]},
+ {2, [?SCHEME,?PATH_EMP]},
+ {2, [?SCHEME,?HOST_NU,?PATH_ABE_NU]},
+ {2, [?SCHEME,?USER_NU,?HOST_NU,?PATH_ABE_NU]},
+ {2, [?SCHEME,?HOST_NU,?PORT,?PATH_ABE_NU]},
+ {2, [?SCHEME,?USER_NU,?HOST_NU,?PORT,?PATH_ABE_NU]},
+
+ {2, [?PATH_ABS_NU]},
+ {2, [?PATH_NOS_NU]},
+ {2, [?PATH_EMP]},
+ {2, [?HOST_NU,?PATH_ABE_NU]},
+ {2, [?USER_NU,?HOST_NU,?PATH_ABE_NU]},
+ {2, [?HOST_NU,?PORT,?PATH_ABE_NU]},
+ {2, [?USER_NU,?HOST_NU,?PORT,?PATH_ABE_NU]},
+
+
+ {2, [?SCHEME,?PATH_ABS_NU,?QUERY_NU]},
+ {2, [?SCHEME,?PATH_ROO_NU,?QUERY_NU]},
+ {2, [?SCHEME,?PATH_EMP,?QUERY_NU]},
+ {2, [?SCHEME,?HOST_NU,?PATH_ABE_NU,?QUERY_NU]},
+ {2, [?SCHEME,?USER_NU,?HOST_NU,?PATH_ABE_NU,?QUERY_NU]},
+ {2, [?SCHEME,?HOST_NU,?PORT,?PATH_ABE_NU,?QUERY_NU]},
+ {2, [?SCHEME,?USER_NU,?HOST_NU,?PORT,?PATH_ABE_NU,?QUERY_NU]},
+
+ {2, [?PATH_ABS_NU,?QUERY_NU]},
+ {2, [?PATH_NOS_NU,?QUERY_NU]},
+ {2, [?PATH_EMP,?QUERY_NU]},
+ {2, [?HOST_NU,?PATH_ABE_NU,?QUERY_NU]},
+ {2, [?USER_NU,?HOST_NU,?PATH_ABE_NU,?QUERY_NU]},
+ {2, [?HOST_NU,?PORT,?PATH_ABE_NU,?QUERY_NU]},
+ {2, [?USER_NU,?HOST_NU,?PORT,?PATH_ABE_NU,?QUERY_NU]},
+
+
+ {2, [?SCHEME,?PATH_ABS_NU,?FRAGMENT_NU]},
+ {2, [?SCHEME,?PATH_ROO_NU,?FRAGMENT_NU]},
+ {2, [?SCHEME,?PATH_EMP,?FRAGMENT_NU]},
+ {2, [?SCHEME,?HOST_NU,?PATH_ABE_NU,?FRAGMENT_NU]},
+ {2, [?SCHEME,?USER_NU,?HOST_NU,?PATH_ABE_NU,?FRAGMENT_NU]},
+ {2, [?SCHEME,?HOST_NU,?PORT,?PATH_ABE_NU,?FRAGMENT_NU]},
+ {2, [?SCHEME,?USER_NU,?HOST_NU,?PORT,?PATH_ABE_NU,?FRAGMENT_NU]},
+
+ {2, [?PATH_ABS_NU,?FRAGMENT_NU]},
+ {2, [?PATH_NOS_NU,?FRAGMENT_NU]},
+ {2, [?PATH_EMP,?FRAGMENT_NU]},
+ {2, [?HOST_NU,?PATH_ABE_NU,?FRAGMENT_NU]},
+ {2, [?USER_NU,?HOST_NU,?PATH_ABE_NU,?FRAGMENT_NU]},
+ {2, [?HOST_NU,?PORT,?PATH_ABE_NU,?FRAGMENT_NU]},
+ {2, [?USER_NU,?HOST_NU,?PORT,?PATH_ABE_NU,?FRAGMENT_NU]},
+
+
+ {2, [?SCHEME,?PATH_ABS_NU,?QUERY_NU,?FRAGMENT_NU]},
+ {2, [?SCHEME,?PATH_ROO_NU,?QUERY_NU,?FRAGMENT_NU]},
+ {2, [?SCHEME,?PATH_EMP,?QUERY_NU,?FRAGMENT_NU]},
+ {2, [?SCHEME,?HOST_NU,?PATH_ABE_NU,?QUERY_NU,?FRAGMENT_NU]},
+ {2, [?SCHEME,?USER_NU,?HOST_NU,?PATH_ABE_NU,?QUERY_NU,?FRAGMENT_NU]},
+ {2, [?SCHEME,?HOST_NU,?PORT,?PATH_ABE_NU,?QUERY_NU,?FRAGMENT_NU]},
+ {2, [?SCHEME,?USER_NU,?HOST_NU,?PORT,?PATH_ABE_NU,?QUERY_NU,?FRAGMENT_NU]},
+
+ {2, [?PATH_ABS_NU,?QUERY_NU,?FRAGMENT_NU]},
+ {2, [?PATH_NOS_NU,?QUERY_NU,?FRAGMENT_NU]},
+ {2, [?PATH_EMP,?QUERY_NU,?FRAGMENT_NU]},
+ {2, [?HOST_NU,?PATH_ABE_NU,?QUERY_NU,?FRAGMENT_NU]},
+ {2, [?USER_NU,?HOST_NU,?PATH_ABE_NU,?QUERY_NU,?FRAGMENT_NU]},
+ {2, [?HOST_NU,?PORT,?PATH_ABE_NU,?QUERY_NU,?FRAGMENT_NU]},
+ {2, [?USER_NU,?HOST_NU,?PORT,?PATH_ABE_NU,?QUERY_NU,?FRAGMENT_NU]}
+ ]).
+
%%-------------------------------------------------------------------------
%% Path
@@ -174,6 +261,11 @@ path_abempty_map() ->
frequency([{90, path_abe_map()},
{10, path_empty_map()}]).
+path_abempty_map_nu() ->
+ frequency([{90, path_abe_map_nu()},
+ {10, path_empty_map()}]).
+
+
path_abe_map() ->
?SIZED(Length, path_abe_map(Length, [])).
%%
@@ -182,6 +274,14 @@ path_abe_map(0, Segments) ->
path_abe_map(N, Segments) ->
path_abe_map(N-1, [slash(),segment()|Segments]).
+path_abe_map_nu() ->
+ ?SIZED(Length, path_abe_map_nu(Length, [])).
+%%
+path_abe_map_nu(0, Segments) ->
+ ?LET(Gen, Segments, lists:append(Gen));
+path_abe_map_nu(N, Segments) ->
+ path_abe_map_nu(N-1, [slash(),segment_nu()|Segments]).
+
path_absolute_map() ->
?SIZED(Length, path_absolute_map(Length, [])).
@@ -191,6 +291,14 @@ path_absolute_map(0, Segments) ->
path_absolute_map(N, Segments) ->
path_absolute_map(N-1, [slash(),segment()|Segments]).
+path_absolute_map_nu() ->
+ ?SIZED(Length, path_absolute_map_nu(Length, [])).
+%%
+path_absolute_map_nu(0, Segments) ->
+ ?LET(Gen, [slash(),segment_nz_nu()|Segments], lists:append(Gen));
+path_absolute_map_nu(N, Segments) ->
+ path_absolute_map_nu(N-1, [slash(),segment_nu()|Segments]).
+
path_noscheme_map() ->
?SIZED(Length, path_noscheme_map(Length, [])).
@@ -200,6 +308,15 @@ path_noscheme_map(0, Segments) ->
path_noscheme_map(N, Segments) ->
path_noscheme_map(N-1, [slash(),segment()|Segments]).
+path_noscheme_map_nu() ->
+ ?SIZED(Length, path_noscheme_map_nu(Length, [])).
+%%
+path_noscheme_map_nu(0, Segments) ->
+ ?LET(Gen, [segment_nz_nc_nu()|Segments], lists:append(Gen));
+path_noscheme_map_nu(N, Segments) ->
+ path_noscheme_map_nu(N-1, [slash(),segment_nu()|Segments]).
+
+
path_rootless_map() ->
?SIZED(Length, path_rootless_map(Length, [])).
%%
@@ -208,24 +325,59 @@ path_rootless_map(0, Segments) ->
path_rootless_map(N, Segments) ->
path_rootless_map(N-1, [slash(),segment()|Segments]).
+path_rootless_map_nu() ->
+ ?SIZED(Length, path_rootless_map_nu(Length, [])).
+%%
+path_rootless_map_nu(0, Segments) ->
+ ?LET(Gen, [segment_nz_nu()|Segments], lists:append(Gen));
+path_rootless_map_nu(N, Segments) ->
+ path_rootless_map_nu(N-1, [slash(),segment_nu()|Segments]).
+
segment_nz() ->
non_empty(segment()).
-segment_nz_nc() ->
- non_empty(list(frequency([{30, unreserved()},
- {10, sub_delims()},
- {10, unicode_char()},
- {5, oneof([$@])}
- ]))).
+segment_nz_nu() ->
+ non_empty(segment_nu()).
+segment_nz_nc() ->
+ ?LET(Gen,
+ non_empty(list(frequency([{30, unreserved()},
+ {10, ptc_encoded_reserved()},
+ {10, sub_delims()},
+ {10, unicode_char()},
+ {5, oneof([$@])}
+ ]))),
+ lists:flatten(Gen)).
+
+segment_nz_nc_nu() ->
+ ?LET(Gen,
+ non_empty(list(frequency([{30, unreserved()},
+ {10, ptc_encoded_reserved()},
+ {10, sub_delims()},
+ {5, oneof([$@])}
+ ]))),
+ lists:flatten(Gen)).
+
segment() ->
- list(frequency([{30, unreserved()},
- {10, sub_delims()},
- {10, unicode_char()},
- {5, oneof([$:, $@])}
- ])).
+ ?LET(Gen,
+ list(frequency([{30, unreserved()},
+ {10, ptc_encoded_reserved()},
+ {10, sub_delims()},
+ {10, unicode_char()},
+ {5, oneof([$:, $@])}
+ ])),
+ lists:flatten(Gen)).
+
+segment_nu() ->
+ ?LET(Gen,
+ list(frequency([{30, unreserved()},
+ {10, ptc_encoded_reserved()},
+ {10, sub_delims()},
+ {5, oneof([$:, $@])}
+ ])),
+ lists:flatten(Gen)).
slash() ->
"/".
@@ -235,19 +387,35 @@ path_empty_map() ->
%%-------------------------------------------------------------------------
-%% Path
+%% Host
%%-------------------------------------------------------------------------
host_map() ->
frequency([{30, reg_name()},
{30, ip_address()}
]).
+host_map_nu() ->
+ frequency([{30, reg_name_nu()},
+ {30, ip_address()}
+ ]).
reg_name() ->
- list(frequency([{30, alpha()},
- {10, sub_delims()},
- {10, unicode_char()}
- ])).
+ ?LET(Gen,
+ list(frequency([{30, alpha()},
+ {10, sub_delims()},
+ {10, ptc_encoded_reserved()},
+ {10, unicode_char()}
+ ])),
+ lists:flatten(Gen)).
+
+reg_name_nu() ->
+ ?LET(Gen,
+ list(frequency([{30, alpha()},
+ {10, sub_delims()},
+ {10, ptc_encoded_reserved()}
+ ])),
+ lists:flatten(Gen)).
+
ip_address() ->
oneof(["127.0.0.1", "::127.0.0.1",
@@ -258,10 +426,13 @@ ip_address() ->
%% Generating only reg-names
host_uri() ->
- non_empty(list(frequency([{30, unreserved()},
- {10, sub_delims()},
- {10, pct_encoded()}
- ]))).
+ ?LET(Gen,
+ non_empty(list(frequency([{30, unreserved()},
+ {10, sub_delims()},
+ {10, ptc_encoded_reserved()},
+ {10, pct_encoded()}
+ ]))),
+ lists:flatten(Gen)).
%%-------------------------------------------------------------------------
%% Port, Query, Fragment
@@ -274,6 +445,9 @@ port() ->
query_map() ->
unicode().
+query_map_nu() ->
+ non_unicode().
+
query_uri() ->
[$?| non_empty(list(frequency([{20, pchar()},
@@ -283,6 +457,10 @@ query_uri() ->
fragment_map() ->
unicode().
+fragment_map_nu() ->
+ non_unicode().
+
+
fragment_uri() ->
[$?| non_empty(list(frequency([{20, pchar()},
{5, oneof([$/, $?])} % punctuation
@@ -311,9 +489,14 @@ scheme(N, L) ->
%%-------------------------------------------------------------------------
unicode() ->
list(frequency([{20, alpha()}, % alpha
- {10, digit()}, % digit
- {10, unicode_char()} % unicode
- ])).
+ {10, digit()}, % digit
+ {10, unicode_char()} % unicode
+ ])).
+
+non_unicode() ->
+ list(frequency([{20, alpha()}, % alpha
+ {10, digit()} % digit
+ ])).
scheme_char() ->
frequency([{20, alpha()}, % alpha
@@ -327,6 +510,7 @@ sub_delims() ->
pchar() ->
frequency([{20, unreserved()},
+ {5, ptc_encoded_reserved()},
{5, pct_encoded()},
{5, sub_delims()},
{1, oneof([$:, $@])} % punctuation
@@ -351,6 +535,22 @@ digit() ->
pct_encoded() ->
oneof(["%C3%A4", "%C3%A5", "%C3%B6"]).
+%%-------------------------------------------------------------------------
+%% [RFC 3986, Chapter 2.2. Reserved Characters]
+%%
+%% reserved = gen-delims / sub-delims
+%%
+%% gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
+%% 3A 2F 3F 23 5B 5D 40
+%% sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
+%% 21 24 26 27 28 29
+%% / "*" / "+" / "," / ";" / "="
+%% 2A 2B 2C 3B 3D
+%%-------------------------------------------------------------------------
+ptc_encoded_reserved() ->
+ oneof(["%3A","%2F","%3F","%23","%5B","%5D","%40",
+ "%21","%24","%26","%27","%28","%29",
+ "%2A","%2B","%2C","%3B","3D"]).
%%%========================================================================
%%% Helpers
@@ -359,3 +559,13 @@ proplist_to_map(L) ->
lists:foldl(fun({K,V},M) -> M#{K => V};
(_,M) -> M
end, #{}, L).
+
+map_scheme_host_to_lower(Map) ->
+ Fun = fun (scheme,V) ->
+ string:to_lower(V);
+ (host,V) ->
+ string:to_lower(V);
+ (_,V) ->
+ V
+ end,
+ maps:map(Fun, Map).
diff --git a/lib/stdlib/test/string_SUITE.erl b/lib/stdlib/test/string_SUITE.erl
index 29fabb4583..ab412dcc70 100644
--- a/lib/stdlib/test/string_SUITE.erl
+++ b/lib/stdlib/test/string_SUITE.erl
@@ -409,8 +409,8 @@ uppercase(_) ->
?TEST("abc", [], "ABC"),
?TEST("ABC", [], "ABC"),
?TEST("abcdefghiljklmnopqrstvxyzåäö",[], "ABCDEFGHILJKLMNOPQRSTVXYZÅÄÖ"),
- ?TEST("åäö", [], "ÅÄÖ"),
- ?TEST("ÅÄÖ", [], "ÅÄÖ"),
+ ?TEST("åäö ", [], "ÅÄÖ "),
+ ?TEST("ÅÄÖ ", [], "ÅÄÖ "),
?TEST("Michał", [], "MICHAŁ"),
?TEST(["Mic",<<"hał"/utf8>>], [], "MICHAŁ"),
?TEST("ljLJ", [], "LJLJ"),
@@ -423,8 +423,8 @@ lowercase(_) ->
?TEST("123", [], "123"),
?TEST("abc", [], "abc"),
?TEST("ABC", [], "abc"),
- ?TEST("åäö", [], "åäö"),
- ?TEST("ÅÄÖ", [], "åäö"),
+ ?TEST("åäö ", [], "åäö "),
+ ?TEST("ÅÄÖ ", [], "åäö "),
?TEST("MICHAŁ", [], "michał"),
?TEST(["Mic",<<"HAŁ"/utf8>>], [], "michał"),
?TEST("ß SHARP S", [], "ß sharp s"),
@@ -449,8 +449,8 @@ casefold(_) ->
?TEST("123", [], "123"),
?TEST("abc", [], "abc"),
?TEST("ABC", [], "abc"),
- ?TEST("åäö", [], "åäö"),
- ?TEST("ÅÄÖ", [], "åäö"),
+ ?TEST("åäö ", [], "åäö "),
+ ?TEST("ÅÄÖ ", [], "åäö "),
?TEST("MICHAŁ", [], "michał"),
?TEST(["Mic",<<"HAŁ"/utf8>>], [], "michał"),
?TEST("ß SHARP S", [], "ss sharp s"),
diff --git a/lib/stdlib/test/uri_string_SUITE.erl b/lib/stdlib/test/uri_string_SUITE.erl
index 92f8bb3292..2aa399525d 100644
--- a/lib/stdlib/test/uri_string_SUITE.erl
+++ b/lib/stdlib/test/uri_string_SUITE.erl
@@ -23,6 +23,12 @@
-export([all/0, suite/0,groups/0,
normalize/1, normalize_map/1, normalize_return_map/1, normalize_negative/1,
+ normalize_binary_pct_encoded_userinfo/1,
+ normalize_binary_pct_encoded_query/1,
+ normalize_binary_pct_encoded_fragment/1,
+ normalize_pct_encoded_userinfo/1,
+ normalize_pct_encoded_query/1,
+ normalize_pct_encoded_fragment/1,
parse_binary_fragment/1, parse_binary_host/1, parse_binary_host_ipv4/1,
parse_binary_host_ipv6/1,
parse_binary_path/1, parse_binary_pct_encoded_fragment/1, parse_binary_pct_encoded_query/1,
@@ -41,7 +47,8 @@
transcode_basic/1, transcode_options/1, transcode_mixed/1, transcode_negative/1,
compose_query/1, compose_query_latin1/1, compose_query_negative/1,
dissect_query/1, dissect_query_negative/1,
- interop_query_latin1/1, interop_query_utf8/1
+ interop_query_latin1/1, interop_query_utf8/1,
+ regression_parse/1, regression_recompose/1, regression_normalize/1
]).
@@ -71,6 +78,12 @@ all() ->
normalize_map,
normalize_return_map,
normalize_negative,
+ normalize_binary_pct_encoded_userinfo,
+ normalize_binary_pct_encoded_query,
+ normalize_binary_pct_encoded_fragment,
+ normalize_pct_encoded_userinfo,
+ normalize_pct_encoded_query,
+ normalize_pct_encoded_fragment,
parse_binary_scheme,
parse_binary_userinfo,
parse_binary_pct_encoded_userinfo,
@@ -120,7 +133,10 @@ all() ->
dissect_query,
dissect_query_negative,
interop_query_latin1,
- interop_query_utf8
+ interop_query_utf8,
+ regression_parse,
+ regression_recompose,
+ regression_normalize
].
groups() ->
@@ -338,20 +354,23 @@ parse_binary_userinfo(_Config) ->
uri_string:parse(<<"foo://user:password@localhost">>).
parse_binary_pct_encoded_userinfo(_Config) ->
- #{scheme := <<"user">>, path := <<"合@気道"/utf8>>} =
+ #{scheme := <<"user">>, path := <<"%E5%90%88@%E6%B0%97%E9%81%93">>} =
uri_string:parse(<<"user:%E5%90%88@%E6%B0%97%E9%81%93">>),
- #{path := <<"合気道@"/utf8>>} = uri_string:parse(<<"%E5%90%88%E6%B0%97%E9%81%93@">>),
- #{path := <<"/合気道@"/utf8>>} = uri_string:parse(<<"/%E5%90%88%E6%B0%97%E9%81%93@">>),
- #{path := <<"合@気道"/utf8>>} = uri_string:parse(<<"%E5%90%88@%E6%B0%97%E9%81%93">>),
- #{userinfo := <<"合"/utf8>>, host := <<"気道"/utf8>>} =
+ #{path := <<"%E5%90%88%E6%B0%97%E9%81%93@">>} =
+ uri_string:parse(<<"%E5%90%88%E6%B0%97%E9%81%93@">>),
+ #{path := <<"/%E5%90%88%E6%B0%97%E9%81%93@">>} =
+ uri_string:parse(<<"/%E5%90%88%E6%B0%97%E9%81%93@">>),
+ #{path := <<"%E5%90%88@%E6%B0%97%E9%81%93">>} =
+ uri_string:parse(<<"%E5%90%88@%E6%B0%97%E9%81%93">>),
+ #{userinfo := <<"%E5%90%88">>, host := <<"%E6%B0%97%E9%81%93">>} =
uri_string:parse(<<"//%E5%90%88@%E6%B0%97%E9%81%93">>),
- #{userinfo := <<"合:気"/utf8>>, host := <<"道"/utf8>>} =
+ #{userinfo := <<"%E5%90%88:%E6%B0%97">>, host := <<"%E9%81%93">>} =
uri_string:parse(<<"//%E5%90%88:%E6%B0%97@%E9%81%93">>),
- #{scheme := <<"foo">>, path := <<"/合気道@"/utf8>>} =
+ #{scheme := <<"foo">>, path := <<"/%E5%90%88%E6%B0%97%E9%81%93@">>} =
uri_string:parse(<<"foo:/%E5%90%88%E6%B0%97%E9%81%93@">>),
- #{scheme := <<"foo">>, userinfo := <<"合"/utf8>>, host := <<"気道"/utf8>>} =
+ #{scheme := <<"foo">>, userinfo := <<"%E5%90%88">>, host := <<"%E6%B0%97%E9%81%93">>} =
uri_string:parse(<<"foo://%E5%90%88@%E6%B0%97%E9%81%93">>),
- #{scheme := <<"foo">>, userinfo := <<"合:気"/utf8>>, host := <<"道"/utf8>>} =
+ #{scheme := <<"foo">>, userinfo := <<"%E5%90%88:%E6%B0%97">>, host := <<"%E9%81%93">>} =
uri_string:parse(<<"foo://%E5%90%88:%E6%B0%97@%E9%81%93">>),
{error,invalid_uri,"@"} = uri_string:parse(<<"//%E5%90%88@%E6%B0%97%E9%81%93@">>),
{error,invalid_uri,":"} = uri_string:parse(<<"foo://%E5%90%88@%E6%B0%97%E9%81%93@">>).
@@ -369,8 +388,8 @@ parse_binary_host_ipv4(_Config) ->
#{host := <<"127.0.0.1">>, query := <<"name=ferret">>} =
uri_string:parse(<<"//127.0.0.1?name=ferret">>),
#{host := <<"127.0.0.1">>, fragment := <<"nose">>} = uri_string:parse(<<"//127.0.0.1#nose">>),
- {error,invalid_uri,"x"} = uri_string:parse(<<"//127.0.0.x">>),
- {error,invalid_uri,"1227.0.0.1"} = uri_string:parse(<<"//1227.0.0.1">>).
+ #{host := <<"127.0.0.x">>,path := <<>>} = uri_string:parse(<<"//127.0.0.x">>),
+ #{host := <<"1227.0.0.1">>,path := <<>>} = uri_string:parse(<<"//1227.0.0.1">>).
parse_binary_host_ipv6(_Config) ->
#{host := <<"::127.0.0.1">>} = uri_string:parse(<<"//[::127.0.0.1]">>),
@@ -439,9 +458,9 @@ parse_binary_query(_Config) ->
parse_binary_pct_encoded_query(_Config) ->
#{scheme := <<"foo">>, host := <<"example.com">>, path := <<"/">>,
- query := <<"name=合気道"/utf8>>} =
+ query := <<"name=%E5%90%88%E6%B0%97%E9%81%93">>} =
uri_string:parse(<<"foo://example.com/?name=%E5%90%88%E6%B0%97%E9%81%93">>),
- #{host := <<"example.com">>, path := <<"/">>, query := <<"name=合気道"/utf8>>} =
+ #{host := <<"example.com">>, path := <<"/">>, query := <<"name=%E5%90%88%E6%B0%97%E9%81%93">>} =
uri_string:parse(<<"//example.com/?name=%E5%90%88%E6%B0%97%E9%81%93">>).
parse_binary_fragment(_Config) ->
@@ -472,9 +491,11 @@ parse_binary_fragment(_Config) ->
uri_string:parse(<<"//example.com/#nose">>).
parse_binary_pct_encoded_fragment(_Config) ->
- #{scheme := <<"foo">>, host := <<"example.com">>, fragment := <<"合気道"/utf8>>} =
+ #{scheme := <<"foo">>, host := <<"example.com">>,
+ fragment := <<"%E5%90%88%E6%B0%97%E9%81%93">>} =
uri_string:parse(<<"foo://example.com#%E5%90%88%E6%B0%97%E9%81%93">>),
- #{host := <<"example.com">>, path := <<"/">>, fragment := <<"合気道"/utf8>>} =
+ #{host := <<"example.com">>, path := <<"/">>,
+ fragment := <<"%E5%90%88%E6%B0%97%E9%81%93">>} =
uri_string:parse(<<"//example.com/#%E5%90%88%E6%B0%97%E9%81%93">>).
parse_scheme(_Config) ->
@@ -506,25 +527,27 @@ parse_userinfo(_Config) ->
uri_string:parse("foo://user:password@localhost").
parse_pct_encoded_userinfo(_Config) ->
- #{scheme := "user", path := "合@気道"} =
+ #{scheme := "user", path := "%E5%90%88@%E6%B0%97%E9%81%93"} =
uri_string:parse("user:%E5%90%88@%E6%B0%97%E9%81%93"),
- #{path := "合気道@"} = uri_string:parse("%E5%90%88%E6%B0%97%E9%81%93@"),
- #{path := "/合気道@"} = uri_string:parse("/%E5%90%88%E6%B0%97%E9%81%93@"),
- #{path := "合@気道"} = uri_string:parse("%E5%90%88@%E6%B0%97%E9%81%93"),
- #{userinfo := "合", host := "気道"} =
+ #{path := "%E5%90%88%E6%B0%97%E9%81%93@"} =
+ uri_string:parse("%E5%90%88%E6%B0%97%E9%81%93@"),
+ #{path := "/%E5%90%88%E6%B0%97%E9%81%93@"} =
+ uri_string:parse("/%E5%90%88%E6%B0%97%E9%81%93@"),
+ #{path := "%E5%90%88@%E6%B0%97%E9%81%93"} =
+ uri_string:parse("%E5%90%88@%E6%B0%97%E9%81%93"),
+ #{userinfo := "%E5%90%88", host := "%E6%B0%97%E9%81%93"} =
uri_string:parse("//%E5%90%88@%E6%B0%97%E9%81%93"),
- #{userinfo := "合:気", host := "道"} =
+ #{userinfo := "%E5%90%88:%E6%B0%97", host := "%E9%81%93"} =
uri_string:parse("//%E5%90%88:%E6%B0%97@%E9%81%93"),
- #{scheme := "foo", path := "/合気道@"} =
+ #{scheme := "foo", path := "/%E5%90%88%E6%B0%97%E9%81%93@"} =
uri_string:parse("foo:/%E5%90%88%E6%B0%97%E9%81%93@"),
- #{scheme := "foo", userinfo := "合", host := "気道"} =
+ #{scheme := "foo", userinfo := "%E5%90%88", host := "%E6%B0%97%E9%81%93"} =
uri_string:parse("foo://%E5%90%88@%E6%B0%97%E9%81%93"),
- #{scheme := "foo", userinfo := "合:気", host := "道"} =
+ #{scheme := "foo", userinfo := "%E5%90%88:%E6%B0%97", host := "%E9%81%93"} =
uri_string:parse("foo://%E5%90%88:%E6%B0%97@%E9%81%93"),
{error,invalid_uri,"@"} = uri_string:parse("//%E5%90%88@%E6%B0%97%E9%81%93@"),
{error,invalid_uri,":"} = uri_string:parse("foo://%E5%90%88@%E6%B0%97%E9%81%93@").
-
parse_host(_Config) ->
#{host := "hostname"} = uri_string:parse("//hostname"),
#{host := "hostname",scheme := "foo"} = uri_string:parse("foo://hostname"),
@@ -538,8 +561,8 @@ parse_host_ipv4(_Config) ->
#{host := "127.0.0.1", path := "/over/there"} = uri_string:parse("//127.0.0.1/over/there"),
#{host := "127.0.0.1", query := "name=ferret"} = uri_string:parse("//127.0.0.1?name=ferret"),
#{host := "127.0.0.1", fragment := "nose"} = uri_string:parse("//127.0.0.1#nose"),
- {error,invalid_uri,"x"} = uri_string:parse("//127.0.0.x"),
- {error,invalid_uri,"1227.0.0.1"} = uri_string:parse("//1227.0.0.1").
+ #{host := "127.0.0.x",path := []} = uri_string:parse("//127.0.0.x"),
+ #{host := "1227.0.0.1",path := []} = uri_string:parse("//1227.0.0.1").
parse_host_ipv6(_Config) ->
#{host := "::127.0.0.1"} = uri_string:parse("//[::127.0.0.1]"),
@@ -602,9 +625,9 @@ parse_query(_Config) ->
parse_pct_encoded_query(_Config) ->
#{scheme := "foo", host := "example.com", path := "/",
- query := "name=合気道"} =
+ query := "name=%E5%90%88%E6%B0%97%E9%81%93"} =
uri_string:parse("foo://example.com/?name=%E5%90%88%E6%B0%97%E9%81%93"),
- #{host := "example.com", path := "/", query := "name=合気道"} =
+ #{host := "example.com", path := "/", query := "name=%E5%90%88%E6%B0%97%E9%81%93"} =
uri_string:parse("//example.com/?name=%E5%90%88%E6%B0%97%E9%81%93").
parse_fragment(_Config) ->
@@ -635,9 +658,11 @@ parse_fragment(_Config) ->
uri_string:parse("//example.com/#nose").
parse_pct_encoded_fragment(_Config) ->
- #{scheme := "foo", host := "example.com", fragment := "合気道"} =
+ #{scheme := "foo", host := "example.com",
+ fragment := "%E5%90%88%E6%B0%97%E9%81%93"} =
uri_string:parse("foo://example.com#%E5%90%88%E6%B0%97%E9%81%93"),
- #{host := "example.com", path := "/", fragment := "合気道"} =
+ #{host := "example.com", path := "/",
+ fragment := "%E5%90%88%E6%B0%97%E9%81%93"} =
uri_string:parse("//example.com/#%E5%90%88%E6%B0%97%E9%81%93").
parse_list(_Config) ->
@@ -711,9 +736,7 @@ parse_negative(_Config) ->
{error,invalid_uri,":"} = uri_string:parse("foo://usär@host"),
{error,invalid_uri,"ö"} = uri_string:parse("//host/path?foö=bar"),
{error,invalid_uri,"ö"} = uri_string:parse("//host/path#foö"),
- {error,invalid_uri,"127.256.0.1"} = uri_string:parse("//127.256.0.1"),
{error,invalid_uri,":::127.0.0.1"} = uri_string:parse("//[:::127.0.0.1]"),
- {error,invalid_utf8,<<0,0,0,246>>} = uri_string:parse("//%00%00%00%F6"),
{error,invalid_uri,"A"} = uri_string:parse("//localhost:A8").
@@ -913,7 +936,9 @@ normalize(_Config) ->
<<"sftp://localhost">> =
uri_string:normalize(<<"sftp://localhost:22">>),
<<"tftp://localhost">> =
- uri_string:normalize(<<"tftp://localhost:69">>).
+ uri_string:normalize(<<"tftp://localhost:69">>),
+ <<"/foo/%2F/bar">> =
+ uri_string:normalize(<<"/foo/%2f/%62ar">>).
normalize_map(_Config) ->
"/a/g" = uri_string:normalize(#{path => "/a/b/c/./../../g"}),
@@ -942,7 +967,9 @@ normalize_map(_Config) ->
host => <<"localhost">>}),
<<"tftp://localhost">> =
uri_string:normalize(#{scheme => <<"tftp">>,port => 69,path => <<>>,
- host => <<"localhost">>}).
+ host => <<"localhost">>}),
+ "/foo/%2F/bar" =
+ uri_string:normalize(#{path => "/foo/%2f/%62ar"}).
normalize_return_map(_Config) ->
#{scheme := "http",path := "/a/g",host := "localhost-örebro"} =
@@ -963,7 +990,82 @@ normalize_negative(_Config) ->
{error,invalid_uri,":"} =
uri_string:normalize("http://[192.168.0.1]", [return_map]),
{error,invalid_uri,":"} =
- uri_string:normalize(<<"http://[192.168.0.1]">>, [return_map]).
+ uri_string:normalize(<<"http://[192.168.0.1]">>, [return_map]),
+ {error,invalid_utf8,<<0,0,0,246>>} = uri_string:normalize("//%00%00%00%F6").
+
+normalize_binary_pct_encoded_userinfo(_Config) ->
+ #{scheme := <<"user">>, path := <<"合@気道"/utf8>>} =
+ uri_string:normalize(<<"user:%E5%90%88@%E6%B0%97%E9%81%93">>, [return_map]),
+ #{path := <<"合気道@"/utf8>>} =
+ uri_string:normalize(<<"%E5%90%88%E6%B0%97%E9%81%93@">>, [return_map]),
+ #{path := <<"/合気道@"/utf8>>} =
+ uri_string:normalize(<<"/%E5%90%88%E6%B0%97%E9%81%93@">>, [return_map]),
+ #{path := <<"合@気道"/utf8>>} =
+ uri_string:normalize(<<"%E5%90%88@%E6%B0%97%E9%81%93">>, [return_map]),
+ #{userinfo := <<"合"/utf8>>, host := <<"気道"/utf8>>} =
+ uri_string:normalize(<<"//%E5%90%88@%E6%B0%97%E9%81%93">>, [return_map]),
+ #{userinfo := <<"合:気"/utf8>>, host := <<"道"/utf8>>} =
+ uri_string:normalize(<<"//%E5%90%88:%E6%B0%97@%E9%81%93">>, [return_map]),
+ #{scheme := <<"foo">>, path := <<"/合気道@"/utf8>>} =
+ uri_string:normalize(<<"foo:/%E5%90%88%E6%B0%97%E9%81%93@">>, [return_map]),
+ #{scheme := <<"foo">>, userinfo := <<"合"/utf8>>, host := <<"気道"/utf8>>} =
+ uri_string:normalize(<<"foo://%E5%90%88@%E6%B0%97%E9%81%93">>, [return_map]),
+ #{scheme := <<"foo">>, userinfo := <<"合:気"/utf8>>, host := <<"道"/utf8>>} =
+ uri_string:normalize(<<"foo://%E5%90%88:%E6%B0%97@%E9%81%93">>, [return_map]),
+ {error,invalid_uri,"@"} =
+ uri_string:normalize(<<"//%E5%90%88@%E6%B0%97%E9%81%93@">>, [return_map]),
+ {error,invalid_uri,":"} =
+ uri_string:normalize(<<"foo://%E5%90%88@%E6%B0%97%E9%81%93@">>, [return_map]).
+
+normalize_binary_pct_encoded_query(_Config) ->
+ #{scheme := <<"foo">>, host := <<"example.com">>, path := <<"/">>,
+ query := <<"name=合気道"/utf8>>} =
+ uri_string:normalize(<<"foo://example.com/?name=%E5%90%88%E6%B0%97%E9%81%93">>, [return_map]),
+ #{host := <<"example.com">>, path := <<"/">>, query := <<"name=合気道"/utf8>>} =
+ uri_string:normalize(<<"//example.com/?name=%E5%90%88%E6%B0%97%E9%81%93">>, [return_map]).
+
+normalize_binary_pct_encoded_fragment(_Config) ->
+ #{scheme := <<"foo">>, host := <<"example.com">>, fragment := <<"合気道"/utf8>>} =
+ uri_string:normalize(<<"foo://example.com#%E5%90%88%E6%B0%97%E9%81%93">>, [return_map]),
+ #{host := <<"example.com">>, path := <<"/">>, fragment := <<"合気道"/utf8>>} =
+ uri_string:normalize(<<"//example.com/#%E5%90%88%E6%B0%97%E9%81%93">>, [return_map]).
+
+normalize_pct_encoded_userinfo(_Config) ->
+ #{scheme := "user", path := "合@気道"} =
+ uri_string:normalize("user:%E5%90%88@%E6%B0%97%E9%81%93", [return_map]),
+ #{path := "合気道@"} =
+ uri_string:normalize("%E5%90%88%E6%B0%97%E9%81%93@", [return_map]),
+ #{path := "/合気道@"} =
+ uri_string:normalize("/%E5%90%88%E6%B0%97%E9%81%93@", [return_map]),
+ #{path := "合@気道"} =
+ uri_string:normalize("%E5%90%88@%E6%B0%97%E9%81%93", [return_map]),
+ #{userinfo := "合", host := "気道"} =
+ uri_string:normalize("//%E5%90%88@%E6%B0%97%E9%81%93", [return_map]),
+ #{userinfo := "合:気", host := "道"} =
+ uri_string:normalize("//%E5%90%88:%E6%B0%97@%E9%81%93", [return_map]),
+ #{scheme := "foo", path := "/合気道@"} =
+ uri_string:normalize("foo:/%E5%90%88%E6%B0%97%E9%81%93@", [return_map]),
+ #{scheme := "foo", userinfo := "合", host := "気道"} =
+ uri_string:normalize("foo://%E5%90%88@%E6%B0%97%E9%81%93", [return_map]),
+ #{scheme := "foo", userinfo := "合:気", host := "道"} =
+ uri_string:normalize("foo://%E5%90%88:%E6%B0%97@%E9%81%93", [return_map]),
+ {error,invalid_uri,"@"} =
+ uri_string:normalize("//%E5%90%88@%E6%B0%97%E9%81%93@", [return_map]),
+ {error,invalid_uri,":"} =
+ uri_string:normalize("foo://%E5%90%88@%E6%B0%97%E9%81%93@", [return_map]).
+
+normalize_pct_encoded_query(_Config) ->
+ #{scheme := "foo", host := "example.com", path := "/",
+ query := "name=合気道"} =
+ uri_string:normalize("foo://example.com/?name=%E5%90%88%E6%B0%97%E9%81%93", [return_map]),
+ #{host := "example.com", path := "/", query := "name=合気道"} =
+ uri_string:normalize("//example.com/?name=%E5%90%88%E6%B0%97%E9%81%93", [return_map]).
+
+normalize_pct_encoded_fragment(_Config) ->
+ #{scheme := "foo", host := "example.com", fragment := "合気道"} =
+ uri_string:normalize("foo://example.com#%E5%90%88%E6%B0%97%E9%81%93", [return_map]),
+ #{host := "example.com", path := "/", fragment := "合気道"} =
+ uri_string:normalize("//example.com/#%E5%90%88%E6%B0%97%E9%81%93", [return_map]).
interop_query_utf8(_Config) ->
Q = uri_string:compose_query([{"foo bar","1"}, {"合", "2"}]),
@@ -977,3 +1079,86 @@ interop_query_latin1(_Config) ->
Uri1 = uri_string:transcode(Uri, [{in_encoding, latin1}]),
#{query := Q1} = uri_string:parse(Uri1),
[{"foo bar","1"}, {"合", "2"}] = uri_string:dissect_query(Q1).
+
+regression_parse(_Config) ->
+ #{host := "Bar",path := [],scheme := "FOo"} =
+ uri_string:parse("FOo://Bar"),
+ #{host := "bar",path := [],scheme := "foo"} =
+ uri_string:parse("foo://bar"),
+ #{host := "A%2f",path := "/%62ar",scheme := "foo"} =
+ uri_string:parse("foo://A%2f/%62ar"),
+ #{host := "a%2F",path := "/bar",scheme := "foo"} =
+ uri_string:parse("foo://a%2F/bar"),
+ #{host := "%C3%B6",path := [],scheme := "FOo"} =
+ uri_string:parse("FOo://%C3%B6").
+
+regression_recompose(_Config) ->
+ "FOo://Bar" =
+ uri_string:recompose(#{host => "Bar",path => [],scheme => "FOo"}),
+ "foo://bar" =
+ uri_string:recompose(#{host => "bar",path => [],scheme => "foo"}),
+ "foo://A%2f/%62ar" =
+ uri_string:recompose(#{host => "A%2f",path => "/%62ar",scheme => "foo"}),
+ "foo://a%2F/bar" =
+ uri_string:recompose(#{host => "a%2F",path => "/bar",scheme => "foo"}),
+ "FOo://%C3%B6" =
+ uri_string:recompose(#{host => "%C3%B6",path => [],scheme => "FOo"}),
+ "FOo://%C3%B6" =
+ uri_string:recompose(#{host => "ö",path => [],scheme => "FOo"}).
+
+regression_normalize(_Config) ->
+ "foo://bar" =
+ uri_string:normalize("FOo://Bar"),
+ #{host := "bar",path := [],scheme := "foo"} =
+ uri_string:normalize("FOo://Bar", [return_map]),
+
+ "foo://bar" =
+ uri_string:normalize("foo://bar"),
+ #{host := "bar",path := [],scheme := "foo"} =
+ uri_string:normalize("foo://bar", [return_map]),
+
+ "foo://a%2F/bar" =
+ uri_string:normalize("foo://A%2f/%62ar"),
+ #{host := "a%2F",path := "/bar",scheme := "foo"} =
+ uri_string:normalize("foo://A%2f/%62ar", [return_map]),
+
+ "foo://a%2F/bar" =
+ uri_string:normalize("foo://a%2F/bar"),
+ #{host := "a%2F",path := "/bar",scheme := "foo"} =
+ uri_string:normalize("foo://a%2F/bar", [return_map]),
+
+ "foo://%C3%B6" =
+ uri_string:normalize("FOo://%C3%B6"),
+ #{host := "ö",path := [],scheme := "foo"} =
+ uri_string:normalize("FOo://%C3%B6", [return_map]),
+
+
+ "foo://bar" =
+ uri_string:normalize(#{host => "Bar",path => [],scheme => "FOo"}),
+ #{host := "bar",path := [],scheme := "foo"} =
+ uri_string:normalize(#{host => "Bar",path => [],scheme => "FOo"}, [return_map]),
+
+ "foo://bar" =
+ uri_string:normalize(#{host => "bar",path => [],scheme => "foo"}),
+ #{host := "bar",path := [],scheme := "foo"} =
+ uri_string:normalize(#{host => "bar",path => [],scheme => "foo"}, [return_map]),
+
+ "foo://a%2F/bar" =
+ uri_string:normalize(#{host => "A%2f",path => "/%62ar",scheme => "foo"}),
+ #{host := "a%2F",path := "/bar",scheme := "foo"} =
+ uri_string:normalize(#{host => "A%2f",path => "/%62ar",scheme => "foo"}, [return_map]),
+
+ "foo://a%2F/bar" =
+ uri_string:normalize(#{host => "a%2F",path => "/bar",scheme => "foo"}),
+ #{host := "a%2F",path := "/bar",scheme := "foo"} =
+ uri_string:normalize(#{host => "a%2F",path => "/bar",scheme => "foo"}, [return_map]),
+
+ "foo://%C3%B6" =
+ uri_string:normalize(#{host => "%C3%B6",path => [],scheme => "FOo"}),
+ #{host := "ö",path := [],scheme := "foo"} =
+ uri_string:normalize(#{host => "%C3%B6",path => [],scheme => "FOo"}, [return_map]),
+
+ "foo://%C3%B6" =
+ uri_string:normalize(#{host => "ö",path => [],scheme => "FOo"}),
+ #{host := "ö",path := [],scheme := "foo"} =
+ uri_string:normalize(#{host => "ö",path => [],scheme => "FOo"}, [return_map]).
diff --git a/lib/stdlib/test/uri_string_property_test_SUITE.erl b/lib/stdlib/test/uri_string_property_test_SUITE.erl
index ae2c61c7aa..b01dd9bf65 100644
--- a/lib/stdlib/test/uri_string_property_test_SUITE.erl
+++ b/lib/stdlib/test/uri_string_property_test_SUITE.erl
@@ -22,7 +22,7 @@
-include_lib("common_test/include/ct.hrl").
-compile(export_all).
-all() -> [recompose].
+all() -> [recompose, normalize].
init_per_suite(Config) ->
ct_property_test:init_per_suite(Config).
@@ -37,3 +37,8 @@ recompose(Config) ->
ct_property_test:quickcheck(
uri_string_recompose:prop_recompose(),
Config).
+
+normalize(Config) ->
+ ct_property_test:quickcheck(
+ uri_string_recompose:prop_normalize(),
+ Config).
diff --git a/lib/stdlib/vsn.mk b/lib/stdlib/vsn.mk
index 0525b2de0b..09a4d6fb50 100644
--- a/lib/stdlib/vsn.mk
+++ b/lib/stdlib/vsn.mk
@@ -1 +1 @@
-STDLIB_VSN = 3.5
+STDLIB_VSN = 3.4.5