diff options
Diffstat (limited to 'lib/stdlib')
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 - =< X < 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 |