diff options
369 files changed, 28142 insertions, 12315 deletions
diff --git a/OTP_VERSION b/OTP_VERSION index b090fe57f6..8869803b66 100644 --- a/OTP_VERSION +++ b/OTP_VERSION @@ -1 +1 @@ -22.0 +22.0.5 diff --git a/bootstrap/bin/no_dot_erlang.boot b/bootstrap/bin/no_dot_erlang.boot Binary files differindex 9a2412e1f0..1f7f088a77 100644 --- a/bootstrap/bin/no_dot_erlang.boot +++ b/bootstrap/bin/no_dot_erlang.boot diff --git a/bootstrap/bin/start.boot b/bootstrap/bin/start.boot Binary files differindex 9a2412e1f0..1f7f088a77 100644 --- a/bootstrap/bin/start.boot +++ b/bootstrap/bin/start.boot diff --git a/bootstrap/bin/start_clean.boot b/bootstrap/bin/start_clean.boot Binary files differindex 9a2412e1f0..1f7f088a77 100644 --- a/bootstrap/bin/start_clean.boot +++ b/bootstrap/bin/start_clean.boot diff --git a/bootstrap/lib/compiler/ebin/beam_asm.beam b/bootstrap/lib/compiler/ebin/beam_asm.beam Binary files differindex f6e7f0d68e..07acbb1da7 100644 --- a/bootstrap/lib/compiler/ebin/beam_asm.beam +++ b/bootstrap/lib/compiler/ebin/beam_asm.beam diff --git a/bootstrap/lib/compiler/ebin/beam_except.beam b/bootstrap/lib/compiler/ebin/beam_except.beam Binary files differindex 5daead0cab..1894483f71 100644 --- a/bootstrap/lib/compiler/ebin/beam_except.beam +++ b/bootstrap/lib/compiler/ebin/beam_except.beam diff --git a/bootstrap/lib/compiler/ebin/beam_ssa_codegen.beam b/bootstrap/lib/compiler/ebin/beam_ssa_codegen.beam Binary files differindex afba2693b4..e59340de4f 100644 --- a/bootstrap/lib/compiler/ebin/beam_ssa_codegen.beam +++ b/bootstrap/lib/compiler/ebin/beam_ssa_codegen.beam diff --git a/bootstrap/lib/compiler/ebin/beam_ssa_dead.beam b/bootstrap/lib/compiler/ebin/beam_ssa_dead.beam Binary files differindex f3dbfc42ff..f6bd1b7a69 100644 --- a/bootstrap/lib/compiler/ebin/beam_ssa_dead.beam +++ b/bootstrap/lib/compiler/ebin/beam_ssa_dead.beam diff --git a/bootstrap/lib/compiler/ebin/beam_ssa_pre_codegen.beam b/bootstrap/lib/compiler/ebin/beam_ssa_pre_codegen.beam Binary files differindex f7c9c34c9a..3de363dbb6 100644 --- a/bootstrap/lib/compiler/ebin/beam_ssa_pre_codegen.beam +++ b/bootstrap/lib/compiler/ebin/beam_ssa_pre_codegen.beam diff --git a/bootstrap/lib/compiler/ebin/beam_ssa_type.beam b/bootstrap/lib/compiler/ebin/beam_ssa_type.beam Binary files differindex 029de3d4a5..c13b25bac3 100644 --- a/bootstrap/lib/compiler/ebin/beam_ssa_type.beam +++ b/bootstrap/lib/compiler/ebin/beam_ssa_type.beam diff --git a/bootstrap/lib/compiler/ebin/beam_validator.beam b/bootstrap/lib/compiler/ebin/beam_validator.beam Binary files differindex 79d64b800b..9d0c34a94a 100644 --- a/bootstrap/lib/compiler/ebin/beam_validator.beam +++ b/bootstrap/lib/compiler/ebin/beam_validator.beam diff --git a/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam b/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam Binary files differindex 023d0bda6d..7ff507242b 100644 --- a/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam +++ b/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam diff --git a/bootstrap/lib/kernel/ebin/inet_db.beam b/bootstrap/lib/kernel/ebin/inet_db.beam Binary files differindex e53e183312..20d795e75f 100644 --- a/bootstrap/lib/kernel/ebin/inet_db.beam +++ b/bootstrap/lib/kernel/ebin/inet_db.beam diff --git a/bootstrap/lib/stdlib/ebin/erl_lint.beam b/bootstrap/lib/stdlib/ebin/erl_lint.beam Binary files differindex c5ea3ee70f..62838ed771 100644 --- a/bootstrap/lib/stdlib/ebin/erl_lint.beam +++ b/bootstrap/lib/stdlib/ebin/erl_lint.beam diff --git a/bootstrap/lib/stdlib/ebin/io_lib.beam b/bootstrap/lib/stdlib/ebin/io_lib.beam Binary files differindex c52efe1b44..45692978e4 100644 --- a/bootstrap/lib/stdlib/ebin/io_lib.beam +++ b/bootstrap/lib/stdlib/ebin/io_lib.beam diff --git a/bootstrap/lib/stdlib/ebin/io_lib_format.beam b/bootstrap/lib/stdlib/ebin/io_lib_format.beam Binary files differindex 5556dc733b..66047b5070 100644 --- a/bootstrap/lib/stdlib/ebin/io_lib_format.beam +++ b/bootstrap/lib/stdlib/ebin/io_lib_format.beam diff --git a/bootstrap/lib/stdlib/ebin/io_lib_pretty.beam b/bootstrap/lib/stdlib/ebin/io_lib_pretty.beam Binary files differindex ba644430da..57c700cb31 100644 --- a/bootstrap/lib/stdlib/ebin/io_lib_pretty.beam +++ b/bootstrap/lib/stdlib/ebin/io_lib_pretty.beam diff --git a/bootstrap/lib/stdlib/ebin/queue.beam b/bootstrap/lib/stdlib/ebin/queue.beam Binary files differindex 24b8582188..94728e30cc 100644 --- a/bootstrap/lib/stdlib/ebin/queue.beam +++ b/bootstrap/lib/stdlib/ebin/queue.beam diff --git a/bootstrap/lib/stdlib/ebin/string.beam b/bootstrap/lib/stdlib/ebin/string.beam Binary files differindex e6d68ea84e..5d1d66892b 100644 --- a/bootstrap/lib/stdlib/ebin/string.beam +++ b/bootstrap/lib/stdlib/ebin/string.beam diff --git a/erts/configure.in b/erts/configure.in index 506ce0d0fb..3a043c940d 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -676,7 +676,7 @@ elif test "X$PROFILE_INSTR_GENERATE" = "Xtrue" -a "X$PROFILE_INSTR_USE" = "Xtrue PROFILE_COMPILER=clang AC_MSG_RESULT([yes, using -fprofile-instr-generate]) else - if $enable_pgo = yes; then + if test $enable_pgo = yes; then AC_MSG_ERROR(cannot use PGO with this compiler) else AC_MSG_RESULT([no]) @@ -1362,6 +1362,13 @@ else USE_ESOCK=no fi fi + +if test "x$USE_ESOCK" = "xyes"; then + if test "x$USE_ESOCK" = "xyes"; then + AC_CHECK_FUNCS([localtime_r strftime]) + fi +fi + AC_SUBST(USE_ESOCK) diff --git a/erts/doc/src/alt_dist.xml b/erts/doc/src/alt_dist.xml index 7c997cae20..f72e8acd2c 100644 --- a/erts/doc/src/alt_dist.xml +++ b/erts/doc/src/alt_dist.xml @@ -680,7 +680,10 @@ of the distribution controller, <c>Received</c> is received packets, <c>Sent</c> is sent packets, and <c>PendSend</c> is - amount of packets in queue to be sent + amount of data in queue to be sent + (typically in bytes, but <c>dist_util</c> + only checks whether the value is non-zero + to know there is data in queue) or a <c>boolean()</c> indicating whether there are packets in queue to be sent. </p> diff --git a/erts/doc/src/crash_dump.xml b/erts/doc/src/crash_dump.xml index 33d0903622..876834307a 100644 --- a/erts/doc/src/crash_dump.xml +++ b/erts/doc/src/crash_dump.xml @@ -289,7 +289,7 @@ Slogan: <reason></pre> <marker id="memory"></marker> <title>Memory Information</title> <p>Under the tag <em>=memory</em> is shown information similar - to what can be obtainted on a living node with + to what can be obtained on a living node with <seealso marker="erts:erlang#memory/0"> <c>erlang:memory()</c></seealso>.</p> </section> diff --git a/erts/doc/src/driver.xml b/erts/doc/src/driver.xml index 8f31df4cad..a873bf9931 100644 --- a/erts/doc/src/driver.xml +++ b/erts/doc/src/driver.xml @@ -391,7 +391,7 @@ void encode_result(ei_x_buff* x, PGresult* res, PGconn* conn) <p>Before a driver can be called from Erlang, it must be loaded and opened. Loading is done using the <c><![CDATA[erl_ddll]]></c> module (the <c><![CDATA[erl_ddll]]></c> driver that loads dynamic - driver is actually a driver itself). If loading is successfull, + driver is actually a driver itself). If loading is successful, the port can be opened with <c><![CDATA[open_port/2]]></c>. The port name must match the name of the shared library and the name in the driver entry structure.</p> diff --git a/erts/doc/src/erl_ext_dist.xml b/erts/doc/src/erl_ext_dist.xml index 3730f0e8ac..2ba5994557 100644 --- a/erts/doc/src/erl_ext_dist.xml +++ b/erts/doc/src/erl_ext_dist.xml @@ -128,7 +128,7 @@ are deprecated and are only kept for backward compatibility when decoding terms encoded by older nodes.</p> <p>Support for UTF-8 encoded atoms in the external format has been - available since ERTS 5.10 (OTP R16). This abillity allows such old nodes + available since ERTS 5.10 (OTP R16). This ability allows such old nodes to decode, store and encode any Unicode atoms received from a new OTP 20 node.</p> <p>The maximum number of allowed characters in an atom is 255. In the @@ -913,7 +913,7 @@ <tcaption>SMALL_BIG_EXT</tcaption></table> <p> Bignums are stored in unary form with a <c>Sign</c> byte, - that is, 0 if the binum is positive and 1 if it is negative. The + that is, 0 if the bignum is positive and 1 if it is negative. The digits are stored with the least significant byte stored first. To calculate the integer, the following formula can be used: </p> diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index f88d255296..d74ae23a93 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -1495,6 +1495,9 @@ enif_free_iovec(iovec);]]></code> <c>term</c>.</p> <p>Returns <c>true</c> on success, or <c>false</c> if <c>term</c> is not a handle to a resource object of type <c>type</c>.</p> + <p><c>enif_get_resource</c> does not add a reference to the resource + object. However, the pointer received in <c>*objp</c> is guaranteed to + be valid at least as long as the resource handle <c>term</c> is valid.</p> </desc> </func> @@ -2947,6 +2950,10 @@ enif_map_iterator_destroy(env, &iter);</code> References made by <seealso marker="#enif_make_resource"> <c>enif_make_resource</c></seealso> can only be removed by the garbage collector.</p> + <p>There are no guarantees exactly when the destructor of an + unreferenced resource is called. It could be called directly by + <c>enif_release_resource</c> but it could also be scheduled to be + called at a later time possibly by another thread.</p> </desc> </func> diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index a879cce840..0e82ceba7d 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -3105,6 +3105,7 @@ false</code> Map = #{42 => value_two,1337 => "value one","a" => 1}, map_get(Key,Map). "value one"</code> + <p>Allowed in guard tests.</p> </desc> </func> @@ -9412,6 +9413,18 @@ Metadata = #{ pid => pid(), <p>Returns the previous system monitor settings just like <seealso marker="#system_monitor/0"> <c>erlang:system_monitor/0</c></seealso>.</p> + <p>The arguments to <c>system_monitor/2</c> specifies how all + system monitoring on the node should be done, not how it should be + changed. This means only one process at a time + (<c><anno>MonitorPid</anno></c>) can be the receiver of system monitor + messages. Also, the way to clear a specific monitor option + is to not include it in the list <c><anno>Options</anno></c>. All + system monitoring will, however, be cleared if the process identified by + <c><anno>MonitorPid</anno></c> terminates.</p> + <p>There are no special option values (like zero) to clear an option. + Some of the options have a unspecified minimum value. Lower values + will be adjusted to the minimum value. For example, it is currently not + possible to monitor all garbage collections with <c>{long_gc, 0}</c>.</p> <note> <p>If a monitoring process gets so large that it itself starts to cause system monitor messages when garbage @@ -10116,6 +10129,11 @@ timestamp() -> time stamp flags are remembered, so if two are passed and the one with highest precedence later is disabled, the other one becomes active.</p> + <p>If a match specification (applicable only for <c>call</c>, <c>send</c> + and <c>'receive'</c> tracing) contains a <c>{message}</c> action + function with a non-boolean value, that value is added as an extra + element to the message tuple either in the last position or before + the timestamp (if it is present).</p> <p>Trace messages:</p> <marker id="trace_3_trace_messages"></marker> <taglist> diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index aad7e27f80..5ca387ffd8 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -31,6 +31,132 @@ </header> <p>This document describes the changes made to the ERTS application.</p> +<section><title>Erts 10.4.4</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + An invalid value test caused the socket:setopt(Socket, + ip, add_membership, ip_mreq()) to fail with badarg. The + same for drop_membership.</p> + <p> + Own Id: OTP-15908 Aux Id: ERL-980 </p> + </item> + <item> + <p> + Fixed bug causing VM crash when doing textual dump of a + process containing an unhandled monitor down signal. + Textual process dumps can be done with + <c>erlang:system_info(procs)</c>, trace feature + <c>process_dump</c>, Erlang shell break menu and a + crashdump. Bug exist since OTP 21.0.</p> + <p> + Own Id: OTP-15909 Aux Id: ERL-979 </p> + </item> + <item> + <p><c>lists:subtract/2</c> would produce incorrect + results for some inputs on 64-bit platforms.</p> + <p> + Own Id: OTP-15938 Aux Id: ERL-986 </p> + </item> + <item> + <p>Fixed a bug in the loader that was similar to + <c>OTP-15938</c>, yielding incorrect code for some inputs + on 64-bit platforms.</p> + <p> + Own Id: OTP-15939</p> + </item> + <item> + <p> + Fixed bug causing scheduler threads in rare cases to + block spinnning indefinitely. Bug exists since OTP 21.0.</p> + <p> + Own Id: OTP-15941 Aux Id: PR-2313 </p> + </item> + </list> + </section> + +</section> + +<section><title>Erts 10.4.3</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>Fixed a buffer overflow when + <c>binary_to_existing_atom/2</c> and + <c>list_to_existing_atom/2</c> was used with the + <c>latin1</c> encoding.</p> + <p> + Own Id: OTP-15819 Aux Id: ERL-944 </p> + </item> + <item> + <p> + The runtime system disconnected a connection if it + received an <c>exit/2</c> signal where the recipient was + a process on an old incarnation of the current node. That + is, the receiving node had the same node name, but + another "creation" number. The signal will now just be + dropped since the receiving process no longer exists.</p> + <p> + Own Id: OTP-15867 Aux Id: ERIERL-373 </p> + </item> + </list> + </section> + +</section> + +<section><title>Erts 10.4.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fixed <c>process_info(Pid,reductions)</c> to not + categorically increase reduction count of the measured + process <c>Pid</c>. Repeated reduction measure of an idle + process will most often (but not guaranteed) return the + same value, like it behaved before OTP 21.3.8.</p> + <p> + Own Id: OTP-15865 Aux Id: ERL-964 </p> + </item> + <item> + <p>Fixed an incorrect load-time optimization that could + cause a crash when extracting deeply nested tuple + elements.</p> + <p> + Own Id: OTP-15871 Aux Id: ERIERL-374 </p> + </item> + <item> + <p> + Fix bug causing VM crash when pressing P for "proc info" + in Erlang shell break menu. Bug exists since OTP 22.0.</p> + <p> + Own Id: OTP-15873 Aux Id: ERL-965 </p> + </item> + </list> + </section> + +</section> + +<section><title>Erts 10.4.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>In nested use of <c>try</c>/<c>catch</c>, rethrowing + an exception using <c>erlang:raise/3</c> with a different + class would not always be able to change the class of the + exception.</p> + <p> + Own Id: OTP-15834 Aux Id: ERIERL-367 </p> + </item> + </list> + </section> + +</section> + <section><title>Erts 10.4</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -514,6 +640,152 @@ </section> +<section><title>Erts 10.3.5.4</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fixed bug causing VM crash when doing textual dump of a + process containing an unhandled monitor down signal. + Textual process dumps can be done with + <c>erlang:system_info(procs)</c>, trace feature + <c>process_dump</c>, Erlang shell break menu and a + crashdump. Bug exist since OTP 21.0.</p> + <p> + Own Id: OTP-15909 Aux Id: ERL-979 </p> + </item> + <item> + <p><c>lists:subtract/2</c> would produce incorrect + results for some inputs on 64-bit platforms.</p> + <p> + Own Id: OTP-15938 Aux Id: ERL-986 </p> + </item> + <item> + <p>Fixed a bug in the loader that was similar to + <c>OTP-15938</c>, yielding incorrect code for some inputs + on 64-bit platforms.</p> + <p> + Own Id: OTP-15939</p> + </item> + <item> + <p> + Fixed bug causing scheduler threads in rare cases to + block spinnning indefinitely. Bug exists since OTP 21.0.</p> + <p> + Own Id: OTP-15941 Aux Id: PR-2313 </p> + </item> + </list> + </section> + +</section> + +<section><title>Erts 10.3.5.3</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + If you set <c>{linger,{true,0}}</c> on a <c>gen_tcp</c> + listen socket, accept a connection on that socket, and + then close the accepted socket, now the linger zero + setting is transferred to the accepted socket. Before + this correction that information was lost and the close + behaviour on the accepted socket incorrect.</p> + <p> + Own Id: OTP-15370 Aux Id: ERIERL-353 </p> + </item> + <item> + <p> + Fixed <c>process_info(Pid,reductions)</c> to not + categorically increase reduction count of the measured + process <c>Pid</c>. Repeated reduction measure of an idle + process will most often (but not guaranteed) return the + same value, like it behaved before OTP 21.3.8.</p> + <p> + Own Id: OTP-15865 Aux Id: ERL-964 </p> + </item> + <item> + <p> + The runtime system disconnected a connection if it + received an <c>exit/2</c> signal where the recipient was + a process on an old incarnation of the current node. That + is, the receiving node had the same node name, but + another "creation" number. The signal will now just be + dropped since the receiving process no longer exists.</p> + <p> + Own Id: OTP-15867 Aux Id: ERIERL-373 </p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + The possibility to send ancillary data, in particular the + TOS field, has been added to <c>gen_udp:send/4,5</c>.</p> + <p> + Own Id: OTP-15747 Aux Id: ERIERL-294 </p> + </item> + </list> + </section> + +</section> + +<section><title>Erts 10.3.5.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>In nested use of <c>try</c>/<c>catch</c>, rethrowing + an exception using <c>erlang:raise/3</c> with a different + class would not always be able to change the class of the + exception.</p> + <p> + Own Id: OTP-15834 Aux Id: ERIERL-367 </p> + </item> + <item> + <p> + Fixed bug in <c>seq_trace:set_token(label,Term)</c> which + could cause VM crash if <c>Term</c> was heap allocated + (not an atom, small integer, local pid or port). Bug + exists since OTP 21.0 when terms other than small + integers were first allowed as labels.</p> + <p> + Own Id: OTP-15849 Aux Id: ERL-700 </p> + </item> + <item> + <p> + Fix <c>seq_trace:print/2</c> not to raise <c>badarg</c> + exception if label is not a small integer. Bug exists + since OTP 21.0.</p> + <p> + Own Id: OTP-15859 Aux Id: ERL-700 </p> + </item> + </list> + </section> + +</section> + +<section><title>Erts 10.3.5.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>Fixed a buffer overflow when + <c>binary_to_existing_atom/2</c> and + <c>list_to_existing_atom/2</c> was used with the + <c>latin1</c> encoding.</p> + <p> + Own Id: OTP-15819 Aux Id: ERL-944 </p> + </item> + </list> + </section> + +</section> + <section><title>Erts 10.3.5</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -2523,6 +2795,23 @@ </section> +<section><title>Erts 9.3.3.11</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>Fixed a buffer overflow when + <c>binary_to_existing_atom/2</c> and + <c>list_to_existing_atom/2</c> was used with the + <c>latin1</c> encoding.</p> + <p> + Own Id: OTP-15819 Aux Id: ERL-944 </p> + </item> + </list> + </section> + +</section> + <section><title>Erts 9.3.3.10</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/erts/doc/src/socket.xml b/erts/doc/src/socket.xml index 343b61d4aa..b4e22e86a8 100644 --- a/erts/doc/src/socket.xml +++ b/erts/doc/src/socket.xml @@ -41,6 +41,38 @@ e.g. <seealso marker="#recv/3"><c>recv/3</c></seealso>, has a timeout argument. </p> <note> + <p>Some functions allow for an <i>asynchronous</i> call. + This is achieved by setting the <c>Timeout</c> argument to + <c>nowait</c>. For instance, if calling the + <seealso marker="#recv_async"><c>recv/3</c></seealso> + function with Timeout set to <c>nowait</c> (<c>recv(Sock, 0, nowait)</c>) + when there is actually nothing to read, it will return with + <c>{ok, </c> + <seealso marker="#type-select_info"><c>SelectInfo</c></seealso><c>}</c>. + When data eventually arrives a 'select' message + will be sent to the caller: </p> + <taglist> + <!-- NOTE THAT THE EMPTY TAG IS INTENTIONAL --> + <tag></tag> + <item><c>{'$socket', socket(), select, select_ref()}</c></item> + </taglist> + <p>The caller can now make another + call to the recv function and now expect data.</p> + <p>Note that all other users are <em>locked out</em> until the + 'current user' has called the function (recv in this case).</p> + <p>Another message the user must be prepared for (when making asynchronous + calls) is the <c>abort</c> message:</p> + <taglist> + <!-- NOTE THAT THE EMPTY TAG IS INTENTIONAL --> + <tag></tag> + <item><c>{'$socket', socket(), abort, Info}</c></item> + </taglist> + <p>This message indicates + that the (asynchronous) operation has been aborted. + If, for instance, the socket has been closed (by another process), + <c>Info</c> will be <c>{select_ref(), closed}</c>. </p> + </note> + <note> <p>There is currently <em>no</em> support for Windows. </p> <p>Support for IPv6 has been implemented but <em>not</em> tested. </p> <p>SCTP has only been partly implemented (and not tested). </p> @@ -65,6 +97,21 @@ </desc> </datatype> <datatype> + <name name="select_tag"/> + <desc> + <p>A tag that describes the (select) operation.</p> + </desc> + </datatype> + <datatype> + <name name="select_ref"/> + <desc> + <p>A reference that uniquely identifies the (select) operation.</p> + </desc> + </datatype> + <datatype> + <name name="select_info"/> + </datatype> + <datatype> <name name="ip4_address"/> </datatype> <datatype> @@ -257,7 +304,7 @@ <funcs> <func> <name name="accept" arity="1" since="OTP 22.0"/> - <name name="accept" arity="2" since="OTP 22.0"/> + <name name="accept" arity="2" clause_i="2" since="OTP 22.0"/> <fsummary>Accept a connection on a socket.</fsummary> <desc> <p>Accept a connection on a socket.</p> @@ -269,6 +316,26 @@ </func> <func> + <name name="accept" arity="2" clause_i="1" anchor="accept_async" since="OTP @OTP-15731@"/> + <fsummary>Accept a connection on a socket.</fsummary> + <desc> + <p>Accept a connection on a socket.</p> + + <p>This call is used with connection-based socket types + (<c>stream</c> or <c>seqpacket</c>). It extracs the first pending + connection request for the listen socket and returns the (newly) + connected socket.</p> + + <p>In the case when there is no connections waiting, the function + will return with the <c>SelectInfo</c>. The caller can then await a + select message, <c>{'$socket', Socket, select, Info}</c> (where + <c>Info</c> is the <c>select_ref()</c> from the <c>SelectInfo</c>), + when a client connects (a subsequent call to accept will then return + the socket). </p> + </desc> + </func> + + <func> <name name="bind" arity="2" since="OTP 22.0"/> <fsummary>Bind a name to a socket.</fsummary> <desc> @@ -282,6 +349,18 @@ </func> <func> + <name name="cancel" arity="2" since="OTP @OTP-15731@"/> + <fsummary>Cancel an asynchronous request.</fsummary> + <desc> + <p>Cancel an asynchronous request.</p> + + <p>Call this function in order to cancel a previous + asynchronous call to, e.g. + <seealso marker="#recv/3"><c>recv/3</c></seealso>. </p> + </desc> + </func> + + <func> <name name="close" arity="1" since="OTP 22.0"/> <fsummary>Close a socket.</fsummary> <desc> @@ -302,7 +381,7 @@ <func> <name name="connect" arity="2" since="OTP 22.0"/> - <name name="connect" arity="3" since="OTP 22.0"/> + <name name="connect" arity="3" clause_i="2" since="OTP 22.0"/> <fsummary>Initiate a connection on a socket.</fsummary> <desc> <p>This function connects the socket to the address @@ -311,6 +390,24 @@ </func> <func> + <name name="connect" arity="3" clause_i="1" anchor="connect_async" since="OTP @OTP-15731@"/> + <fsummary>Initiate a connection on a socket.</fsummary> + <desc> + <p>This function connects the socket to the address + specied by the <c>SockAddr</c> argument.</p> + + <p>In the case when its not possible to immediately establish a + connection, the function will return with the + <seealso marker="#type-select_info"><c>SelectInfo</c></seealso>. + The caller can then await a + select message, <c>{'$socket', Socket, select, Info}</c> (where + <c>Info</c> is the <c>select_ref()</c> from the <c>SelectInfo</c>, + a subsequent call to connect will then + establish the connection). </p> + </desc> + </func> + + <func> <name name="getopt" arity="3" clause_i="1" since="OTP 22.0"/> <name name="getopt" arity="3" clause_i="2" since="OTP 22.0"/> <name name="getopt" arity="3" clause_i="3" since="OTP 22.0"/> @@ -373,19 +470,24 @@ <name name="open" arity="4" since="OTP 22.0"/> <fsummary>Create an endpoint for communication.</fsummary> <desc> - <p>Creates an endpoint (socket) for communication.</p> - <p>For some <c>types</c> there is a default protocol, which will - be used if no protocol is specified: </p> + <p>Creates an endpoint (socket) for communication.</p> - <list> - <item><p><c>stream</c>: <c>tcp</c></p></item> - <item><p><c>dgram</c>: <c>udp</c></p></item> - <item><p><c>seqpacket</c>: <c>sctp</c></p></item> - </list> + <p>For some <c>types</c> there is a default protocol, + indicated by <c>default</c>, which it <em>may</em> be + possible to specify. + And for <c>Domain = local</c>, if a protocol <em>is</em> pecified, + it <em>must</em> be <c>default</c>. </p> <p>The <c>Extra</c> argument is intended for "obscure" options. Currently the only supported option is <c>netns</c>, which is only supported on the linux platform.</p> + + <note> + <p>It may not be possible to specify the default protocol (except + when <c>Domain = local</c>). We need to be able to retreive + the resulting protocol, which is <em>not</em> possble on all + platforms. </p> + </note> </desc> </func> @@ -401,33 +503,59 @@ <name name="recv" arity="1" since="OTP 22.0"/> <name name="recv" arity="2" since="OTP 22.0"/> <name name="recv" arity="3" clause_i="1" since="OTP 22.0"/> - <name name="recv" arity="3" clause_i="2" since="OTP 22.0"/> - <name name="recv" arity="4" since="OTP 22.0"/> + <name name="recv" arity="3" clause_i="3" since="OTP 22.0"/> + <name name="recv" arity="4" clause_i="2" since="OTP 22.0"/> + <fsummary>Receive a message from a socket.</fsummary> + <desc> + <p>Receive a message from a socket.</p> + <p>There is a special case for the argument <c>Length</c>. + If it is set to zero (0), it means "give me everything you + currently have".</p> + </desc> + </func> + + <func> + <name name="recv" arity="3" clause_i="2" anchor="recv_async" since="OTP @OTP-15731@"/> + <name name="recv" arity="4" clause_i="1" since="OTP @OTP-15731@"/> <fsummary>Receive a message from a socket.</fsummary> <desc> <p>Receive a message from a socket.</p> + <p>There is a special case for the argument <c>Length</c>. If it is set to zero (0), it means "give me everything you currently have".</p> + + <p>In the case when there is no data waiting, the function + will return with the <c>SelectInfo</c>. The caller can then await a + select message, <c>{'$socket', Socket, select, Info}</c> (where + <c>Info</c> is the <c>select_ref()</c> from the <c>SelectInfo</c>), + when data has arrived (a subsequent call to recv will then return + the data). </p> + <p>Note that if a length (<c>> 0</c>) is specified, and only part + of that amount of data is available, the function will return with + that data <em>and</em> the <c>SelectInfo</c> (if the caller don't + want to wait for the remaining data, it must immediately call + the <seealso marker="#cancel/2"><c>cancel/2</c></seealso> function.)</p> </desc> </func> <func> <name name="recvfrom" arity="1" since="OTP 22.0"/> <name name="recvfrom" arity="2" since="OTP 22.0"/> - <name name="recvfrom" arity="3" clause_i="1" since="OTP 22.0"/> <name name="recvfrom" arity="3" clause_i="2" since="OTP 22.0"/> <name name="recvfrom" arity="3" clause_i="3" since="OTP 22.0"/> - <name name="recvfrom" arity="4" since="OTP 22.0"/> + <name name="recvfrom" arity="3" clause_i="5" since="OTP 22.0"/> + <name name="recvfrom" arity="4" clause_i="2" since="OTP 22.0"/> <fsummary>Receive a message from a socket.</fsummary> <desc> <p>Receive a message from a socket.</p> <p>This function reads "messages", which means that regardless of - how much we want to read, it returns when we get a message.</p> + how much we want to read, it returns when we get a message + (if the buffer size is to small, the message will be truncated).</p> <p>The <c>BufSz</c> argument basically defines the size of the receive buffer. By setting the value to zero (0), the configured - size (setopt with <c>Level</c> = <c>otp</c> and <c>Key</c> = <c>rcvbuf</c>) - is used.</p> + size (setopt with <c>Level</c> = <c>otp</c> and + <c>Key</c> = <c>rcvbuf</c>) is used.</p> <p>It may be impossible to know what (buffer) size is appropriate "in advance", and in those cases it may be convenient to use the (recv) 'peek' flag. When this flag is provided, the message is *not* @@ -437,12 +565,74 @@ </func> <func> + <name name="recvfrom" arity="3" clause_i="1" anchor="recvfrom_async" since="OTP @OTP-15731@"/> + <name name="recvfrom" arity="3" clause_i="4" since="OTP @OTP-15731@"/> + <name name="recvfrom" arity="4" clause_i="1" since="OTP @OTP-15731@"/> + <fsummary>Receive a message from a socket.</fsummary> + <desc> + <p>Receive a message from a socket.</p> + <p>This function reads "messages", which means that regardless of + how much we want to read, it returns when we get a message + (if the buffer size is to small, the message will be truncated).</p> + <p>The <c>BufSz</c> argument basically defines the size of the + receive buffer. By setting the value to zero (0), the configured + size (setopt with <c>Level</c> = <c>otp</c> and + <c>Key</c> = <c>rcvbuf</c>) is used.</p> + <p>It may be impossible to know what (buffer) size is appropriate + "in advance", and in those cases it may be convenient to use the + (recv) 'peek' flag. When this flag is provided, the message is *not* + "consumed" from the underlying buffers, so another recvfrom call + is needed, possibly with a then adjusted buffer size.</p> + + <p>In the case when there is no data waiting, the function + will return with the <c>SelectInfo</c>. The caller can then await a + select message, <c>{'$socket', Socket, select, Info}</c> (where + <c>Info</c> is the <c>select_ref()</c> from the <c>SelectInfo</c>), + when data has arrived (a subsequent call to recvfrom will then return + the data). </p> + </desc> + </func> + + <func> <name name="recvmsg" arity="1" since="OTP 22.0"/> <name name="recvmsg" arity="2" clause_i="1" since="OTP 22.0"/> - <name name="recvmsg" arity="2" clause_i="2" since="OTP 22.0"/> - <name name="recvmsg" arity="3" clause_i="1" since="OTP 22.0"/> + <name name="recvmsg" arity="2" clause_i="3" since="OTP 22.0"/> <name name="recvmsg" arity="3" clause_i="2" since="OTP 22.0"/> - <name name="recvmsg" arity="5" since="OTP 22.0"/> + <name name="recvmsg" arity="3" clause_i="3" since="OTP 22.0"/> + <name name="recvmsg" arity="5" clause_i="2" since="OTP 22.0"/> + <fsummary>Receive a message from a socket.</fsummary> + <desc> + <p>Receive a message from a socket.</p> + <p>This function reads "messages", which means that regardless of + how much we want to read, it returns when we get a message.</p> + <p>The message will be delivered in the form of a <c>msghdr()</c>, + which may contain the source address (if socket not connected), + a list of <c>cmsghdr_recv()</c> (depends on what socket options have + been set and what the protocol and platform supports) and + also a set of flags, providing further info about the read. </p> + + <p>The <c>BufSz</c> argument basically defines the size of the + receive buffer. By setting the value to zero (0), the configured + size (setopt with <c>Level</c> = <c>otp</c> and + <c>Key</c> = <c>rcvbuf</c>) is used.</p> + + <p>The <c>CtrlSz</c> argument basically defines the size of the + receive buffer for the control messages. + By setting the value to zero (0), the configured size (setopt + with <c>Level</c> = <c>otp</c>) is used.</p> + + <p>It may be impossible to know what (buffer) size is appropriate + "in advance", and in those cases it may be convenient to use the + (recv) 'peek' flag. When this flag is provided, the message is *not* + "consumed" from the underlying buffers, so another recvmsg call + is needed, possibly with a then adjusted buffer size.</p> + </desc> + </func> + + <func> + <name name="recvmsg" arity="2" clause_i="2" anchor="recvmsg_async" since="OTP @OTP-15731@"/> + <name name="recvmsg" arity="3" clause_i="1" since="OTP @OTP-15731@"/> + <name name="recvmsg" arity="5" clause_i="1" since="OTP @OTP-15731@"/> <fsummary>Receive a message from a socket.</fsummary> <desc> <p>Receive a message from a socket.</p> @@ -456,8 +646,8 @@ <p>The <c>BufSz</c> argument basically defines the size of the receive buffer. By setting the value to zero (0), the configured - size (setopt with <c>Level</c> = <c>otp</c> and <c>Key</c> = <c>rcvbuf</c>) - is used.</p> + size (setopt with <c>Level</c> = <c>otp</c> and + <c>Key</c> = <c>rcvbuf</c>) is used.</p> <p>The <c>CtrlSz</c> argument basically defines the size of the receive buffer for the control messages. @@ -469,14 +659,21 @@ (recv) 'peek' flag. When this flag is provided, the message is *not* "consumed" from the underlying buffers, so another recvmsg call is needed, possibly with a then adjusted buffer size.</p> + + <p>In the case when there is no data waiting, the function + will return with the <c>SelectInfo</c>. The caller can then await a + select message, <c>{'$socket', Socket, select, Info}</c> (where + <c>Info</c> is the <c>select_ref()</c> from the <c>SelectInfo</c>), + when data has arrived (a subsequent call to recvmsg will then return + the data). </p> </desc> </func> <func> <name name="send" arity="2" since="OTP 22.0"/> <name name="send" arity="3" clause_i="1" since="OTP 22.0"/> - <name name="send" arity="3" clause_i="2" since="OTP 22.0"/> - <name name="send" arity="4" since="OTP 22.0"/> + <name name="send" arity="3" clause_i="3" since="OTP 22.0"/> + <name name="send" arity="4" clause_i="2" since="OTP 22.0"/> <fsummary>Send a message on a socket.</fsummary> <desc> <p>Send a message on a connected socket.</p> @@ -484,10 +681,32 @@ </func> <func> + <name name="send" arity="3" clause_i="2" anchor="send_async" since="OTP @OTP-15731@"/> + <name name="send" arity="4" clause_i="1" since="OTP @OTP-15731@"/> + <fsummary>Send a message on a socket.</fsummary> + <desc> + <p>Send a message on a connected socket.</p> + + <p>In the case when there is no room in the (system-) buffers, + the function will return with the <c>SelectInfo</c>. The caller + can then await a select message, + <c>{'$socket', Socket, select, Info}</c> + (where <c>Info</c> is the <c>select_ref()</c> from the + <c>SelectInfo</c>), when there is room for more data (a subsequent + call to send will then send the data). </p> + <p>Note that if not all the data was sent, the function will return + with the remaining data <em>and</em> the <c>SelectInfo</c> + (if the caller don't + want to wait to be able to send the rest, it should immediately call + the <seealso marker="#cancel/2"><c>cancel/2</c></seealso> function.)</p> + </desc> + </func> + + <func> <name name="sendmsg" arity="2" since="OTP 22.0"/> <name name="sendmsg" arity="3" clause_i="1" since="OTP 22.0"/> - <name name="sendmsg" arity="3" clause_i="2" since="OTP 22.0"/> - <name name="sendmsg" arity="4" since="OTP 22.0"/> + <name name="sendmsg" arity="3" clause_i="3" since="OTP 22.0"/> + <name name="sendmsg" arity="4" clause_i="2" since="OTP 22.0"/> <fsummary>Send a message on a socket.</fsummary> <desc> <p>Send a message on a socket. The destination, if needed @@ -508,10 +727,39 @@ </func> <func> + <name name="sendmsg" arity="3" clause_i="2" anchor="sendmsg_async" since="OTP @OTP-15731@"/> + <name name="sendmsg" arity="4" clause_i="1" since="OTP @OTP-15731@"/> + <fsummary>Send a message on a socket.</fsummary> + <desc> + <p>Send a message on a socket. The destination, if needed + (socket <em>not</em> connected) is provided in the <c>MsgHdr</c>, + which also contains the message to send, + The <c>MsgHdr</c> may also contain an list of optional <c>cmsghdr_send()</c> + (depends on what the protocol and platform supports).</p> + + <p>Unlike the <seealso marker="#send/2"><c>send</c></seealso> function, + this one sends <em>one message</em>. + This means that if, for whatever reason, its not possible to send the + message in one go, the function will instead return with the + <em>remaining</em> data (<c>{ok, Remaining}</c>). Thereby leaving it + up to the caller to decide what to do (retry with the remaining data + of give up). </p> + + <p>In the case when there is no room in the (system-) buffers, + the function will return with the <c>SelectInfo</c>. The caller + can then await a select message, + <c>{'$socket', Socket, select, Info}</c> + (where <c>Info</c> is the <c>select_ref()</c> from the + <c>SelectInfo</c>), when there is room for more data (a subsequent + call to sendmsg will then send the data). </p> + </desc> + </func> + + <func> <name name="sendto" arity="3" since="OTP 22.0"/> <name name="sendto" arity="4" clause_i="1" since="OTP 22.0"/> - <name name="sendto" arity="4" clause_i="2" since="OTP 22.0"/> - <name name="sendto" arity="5" since="OTP 22.0"/> + <name name="sendto" arity="4" clause_i="3" since="OTP 22.0"/> + <name name="sendto" arity="5" clause_i="2" since="OTP 22.0"/> <fsummary>Send a message on a socket.</fsummary> <desc> <p>Send a message on a socket, to the specified destination.</p> @@ -519,6 +767,23 @@ </func> <func> + <name name="sendto" arity="4" clause_i="2" anchor="sendto_async" since="OTP @OTP-15731@"/> + <name name="sendto" arity="5" clause_i="1" since="OTP @OTP-15731@"/> + <fsummary>Send a message on a socket.</fsummary> + <desc> + <p>Send a message on a socket, to the specified destination.</p> + + <p>In the case when there is no room in the (system-) buffers, + the function will return with the <c>SelectInfo</c>. The caller + can then await a select message, + <c>{'$socket', Socket, select, Info}</c> + (where <c>Info</c> is the <c>select_ref()</c> from the + <c>SelectInfo</c>), when there is room for more data (a subsequent + call to sendto will then send the data). </p> + </desc> + </func> + + <func> <name name="setopt" arity="4" clause_i="1" since="OTP 22.0"/> <name name="setopt" arity="4" clause_i="2" since="OTP 22.0"/> <name name="setopt" arity="4" clause_i="3" since="OTP 22.0"/> diff --git a/erts/doc/src/socket_usage.xml b/erts/doc/src/socket_usage.xml index e0f006e618..7e65bcbf70 100644 --- a/erts/doc/src/socket_usage.xml +++ b/erts/doc/src/socket_usage.xml @@ -34,11 +34,84 @@ <title>Introduction</title> <p>The socket interface (module) is basically an "thin" layer on top of the OS socket interface. It is assumed that, unless you have special needs, - gen_[tcp|udp|sctp] should be sufficent. </p> + gen_[tcp|udp|sctp] should be sufficent (when they become available). </p> <p>Note that just because we have a documented and described option, it does <em>not</em> mean that the OS supports it. So its recommended that the user reads the platform specific documentation for the option used. </p> + <section> + <title>Asynchronous calls</title> + <p>Some functions allow for an <i>asynchronous</i> call + (<seealso marker="socket#accept_async"><c>accept/2</c></seealso>, + <seealso marker="socket#connect_async"><c>connect/3</c></seealso>, + <seealso marker="socket#recv_async"><c>recv/3,4</c></seealso>, + <seealso marker="socket#recvfrom_async"><c>recvfrom/3,4</c></seealso>, + <seealso marker="socket#recvmsg_async"><c>recvmsg/2,3,5</c></seealso>, + <seealso marker="socket#send_async"><c>send/3,4</c></seealso>, + <seealso marker="socket#sendmsg_async"><c>sendmsg/3,4</c></seealso> and + <seealso marker="socket#sendto_async"><c>sendto/4,5</c></seealso>). + This is achieved by setting the <c>Timeout</c> argument to + <c>nowait</c>. For instance, if calling the + <seealso marker="socket#recv_async"><c>recv/3</c></seealso> + function with Timeout set to <c>nowait</c> (i.e. + <c>recv(Sock, 0, nowait)</c>) + when there is actually nothing to read, it will return with + <c>{ok, </c> + <seealso marker="socket#type-select_info"><c>SelectInfo</c></seealso><c>}</c>. + When data eventually arrives a 'select message' + will be sent to the caller:</p> + <taglist> + <!-- NOTE THAT THE EMPTY TAG IS INTENTIONAL --> + <tag></tag> + <item><c>{'$socket', socket(), select, select_ref()}</c></item> + </taglist> + <p>The caller can then make another + call to the recv function and now expect data.</p> + <p>The user must also be prepared to receive an abort message: </p> + <!-- + <list type="ordered"> + <item><c>{'$socket', Sock, abort, Info}</c></item> + </list> + --> + <taglist> + <!-- NOTE THAT THE EMPTY TAG IS INTENTIONAL --> + <tag></tag> + <item><c>{'$socket', socket(), abort, Info}</c></item> + </taglist> + <p>If the operation is aborted + for whatever reason (e.g. if the socket is closed "by someone else"). + The <c>Info</c> part contains the abort reason (in this case that + the socket has been closed <c>Info = {select_ref(), closed}</c>). </p> + <p>Note that all other users are <em>locked out</em> until the + 'current user' has called the function (recv in this case). So either + immediately call the function or + <seealso marker="socket#cancel/2"><c>cancel</c></seealso>. </p> + + <p>The general form of the 'socket' message is: </p> + <taglist> + <!-- NOTE THAT THE EMPTY TAG IS INTENTIONAL --> + <tag></tag> + <item><c>{'$socket', Sock :: socket(), Tag :: atom(), Info :: term()}</c></item> + </taglist> + <p>Where the format of <c>Info</c> is a function of <c>Tag</c>:</p> + <table> + <row> + <cell><em>Tag</em></cell> + <cell><em>Info value type</em></cell> + </row> + <row> + <cell>select</cell> + <cell>select_ref()</cell> + </row> + <row> + <cell>abort</cell> + <cell>{select_ref(), Reason :: term()}</cell> + </row> + <tcaption>socket message info value type</tcaption> + </table> + <p>The <c>select_ref()</c> is the same as was received in the + <seealso marker="socket#type-select_info"><c>SelectInfo</c></seealso>. </p> + </section> </section> <section> @@ -205,7 +278,7 @@ <cell>protocol()</cell> <cell>no</cell> <cell>yes</cell> - <cell>none</cell> + <cell><em>Not</em> on (some) Darwin (for instance)</cell> </row> <row> <cell>rcvbuf</cell> diff --git a/erts/doc/src/zlib.xml b/erts/doc/src/zlib.xml index 38229456c9..8a580c8d7b 100644 --- a/erts/doc/src/zlib.xml +++ b/erts/doc/src/zlib.xml @@ -411,7 +411,7 @@ list_to_binary([B1,B2])</pre> <func> <name name="deflateParams" arity="3" since=""/> - <fsummary>Dynamicly update deflate parameters.</fsummary> + <fsummary>Dynamically update deflate parameters.</fsummary> <desc> <p>Dynamically updates the compression level and compression strategy. The interpretation of <c><anno>Level</anno></c> and diff --git a/erts/emulator/beam/atom.c b/erts/emulator/beam/atom.c index 59b51fd15e..5a70509ffd 100644 --- a/erts/emulator/beam/atom.c +++ b/erts/emulator/beam/atom.c @@ -200,11 +200,15 @@ atom_free(Atom* obj) ASSERT(obj->slot.index == atom_val(am_ErtsSecretAtom)); } -static void latin1_to_utf8(byte* conv_buf, const byte** srcp, int* lenp) +static void latin1_to_utf8(byte* conv_buf, Uint buf_sz, + const byte** srcp, Uint* lenp) { byte* dst; const byte* src = *srcp; - int i, len = *lenp; + Uint i, len = *lenp; + + ASSERT(len <= MAX_ATOM_CHARACTERS); + ASSERT(buf_sz >= MAX_ATOM_SZ_FROM_LATIN1); for (i=0 ; i < len; ++i) { if (src[i] & 0x80) { @@ -234,11 +238,11 @@ need_convertion: * erts_atom_put_index() may fail. Returns negative indexes for errors. */ int -erts_atom_put_index(const byte *name, int len, ErtsAtomEncoding enc, int trunc) +erts_atom_put_index(const byte *name, Sint len, ErtsAtomEncoding enc, int trunc) { byte utf8_copy[MAX_ATOM_SZ_FROM_LATIN1]; const byte *text = name; - int tlen = len; + Uint tlen; Sint no_latin1_chars; Atom a; int aix; @@ -247,13 +251,16 @@ erts_atom_put_index(const byte *name, int len, ErtsAtomEncoding enc, int trunc) erts_atomic_inc_nob(&atom_put_ops); #endif - if (tlen < 0) { - if (trunc) - tlen = 0; - else - return ATOM_MAX_CHARS_ERROR; + if (len < 0) { + if (trunc) { + len = 0; + } else { + return ATOM_MAX_CHARS_ERROR; + } } + tlen = len; + switch (enc) { case ERTS_ATOM_ENC_7BIT_ASCII: if (tlen > MAX_ATOM_CHARACTERS) { @@ -277,7 +284,7 @@ erts_atom_put_index(const byte *name, int len, ErtsAtomEncoding enc, int trunc) return ATOM_MAX_CHARS_ERROR; } no_latin1_chars = tlen; - latin1_to_utf8(utf8_copy, &text, &tlen); + latin1_to_utf8(utf8_copy, sizeof(utf8_copy), &text, &tlen); break; case ERTS_ATOM_ENC_UTF8: /* First sanity check; need to verify later */ @@ -338,7 +345,7 @@ erts_atom_put_index(const byte *name, int len, ErtsAtomEncoding enc, int trunc) * erts_atom_put() may fail. If it fails THE_NON_VALUE is returned! */ Eterm -erts_atom_put(const byte *name, int len, ErtsAtomEncoding enc, int trunc) +erts_atom_put(const byte *name, Sint len, ErtsAtomEncoding enc, int trunc) { int aix = erts_atom_put_index(name, len, enc, trunc); if (aix >= 0) @@ -348,7 +355,7 @@ erts_atom_put(const byte *name, int len, ErtsAtomEncoding enc, int trunc) } Eterm -am_atom_put(const char* name, int len) +am_atom_put(const char* name, Sint len) { /* Assumes 7-bit ascii; use erts_atom_put() for other encodings... */ return erts_atom_put((byte *) name, len, ERTS_ATOM_ENC_7BIT_ASCII, 1); @@ -379,23 +386,57 @@ int atom_table_sz(void) } int -erts_atom_get(const char *name, int len, Eterm* ap, ErtsAtomEncoding enc) +erts_atom_get(const char *name, Uint len, Eterm* ap, ErtsAtomEncoding enc) { byte utf8_copy[MAX_ATOM_SZ_FROM_LATIN1]; Atom a; int i; int res; - a.len = (Sint16) len; - a.name = (byte *)name; - if (enc == ERTS_ATOM_ENC_LATIN1) { - latin1_to_utf8(utf8_copy, (const byte**)&a.name, &len); - a.len = (Sint16) len; + switch (enc) { + case ERTS_ATOM_ENC_LATIN1: + if (len > MAX_ATOM_CHARACTERS) { + return 0; + } + + latin1_to_utf8(utf8_copy, sizeof(utf8_copy), (const byte**)&name, &len); + + a.name = (byte*)name; + a.len = (Sint16)len; + break; + case ERTS_ATOM_ENC_7BIT_ASCII: + if (len > MAX_ATOM_CHARACTERS) { + return 0; + } + + for (i = 0; i < len; i++) { + if (name[i] & 0x80) { + return 0; + } + } + + a.len = (Sint16)len; + a.name = (byte*)name; + break; + case ERTS_ATOM_ENC_UTF8: + if (len > MAX_ATOM_SZ_LIMIT) { + return 0; + } + + /* We don't need to check whether the encoding is legal as all atom + * names are stored as UTF-8 and we know a lookup with a badly encoded + * name will fail. */ + + a.len = (Sint16)len; + a.name = (byte*)name; + break; } + atom_read_lock(); i = index_get(&erts_atom_table, (void*) &a); res = i < 0 ? 0 : (*ap = make_atom(i), 1); atom_read_unlock(); + return res; } diff --git a/erts/emulator/beam/atom.h b/erts/emulator/beam/atom.h index ca920679c6..f51c5a8c62 100644 --- a/erts/emulator/beam/atom.h +++ b/erts/emulator/beam/atom.h @@ -133,14 +133,14 @@ typedef enum { int atom_table_size(void); /* number of elements */ int atom_table_sz(void); /* table size in bytes, excluding stored objects */ -Eterm am_atom_put(const char*, int); /* ONLY 7-bit ascii! */ -Eterm erts_atom_put(const byte *name, int len, ErtsAtomEncoding enc, int trunc); -int erts_atom_put_index(const byte *name, int len, ErtsAtomEncoding enc, int trunc); +Eterm am_atom_put(const char*, Sint); /* ONLY 7-bit ascii! */ +Eterm erts_atom_put(const byte *name, Sint len, ErtsAtomEncoding enc, int trunc); +int erts_atom_put_index(const byte *name, Sint len, ErtsAtomEncoding enc, int trunc); void init_atom_table(void); void atom_info(fmtfn_t, void *); void dump_atoms(fmtfn_t, void *); Uint erts_get_atom_limit(void); -int erts_atom_get(const char* name, int len, Eterm* ap, ErtsAtomEncoding enc); +int erts_atom_get(const char* name, Uint len, Eterm* ap, ErtsAtomEncoding enc); void erts_atom_get_text_space_sizes(Uint *reserved, Uint *used); #endif diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index bae64afb97..07c16e3415 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -414,6 +414,7 @@ static Eterm add_stacktrace(Process* c_p, Eterm Value, Eterm exc); static void save_stacktrace(Process* c_p, BeamInstr* pc, Eterm* reg, ErtsCodeMFA *bif_mfa, Eterm args); static struct StackTrace * get_trace_from_exc(Eterm exc); +static Eterm *get_freason_ptr_from_exc(Eterm exc); static Eterm make_arglist(Process* c_p, Eterm* reg, int a); void @@ -1902,6 +1903,25 @@ static int is_raised_exc(Eterm exc) { } } +static Eterm *get_freason_ptr_from_exc(Eterm exc) { + static Eterm dummy_freason; + struct StackTrace* s; + + if (exc == NIL) { + /* + * Is is not exactly clear when exc can be NIL. Probably only + * when the exception has been generated from native code. + * Return a pointer to an Eterm that can be safely written and + * ignored. + */ + return &dummy_freason; + } else { + ASSERT(is_list(exc)); + s = (struct StackTrace *) big_val(CDR(list_val(exc))); + return &s->freason; + } +} + /* * Creating a list with the argument registers */ diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index 941c3ebbbe..35f2ea6688 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -4612,7 +4612,15 @@ typedef struct SortGenOpArg { static int genopargtermcompare(SortGenOpArg* a, SortGenOpArg* b) { - return CMP_TERM(a->term, b->term); + Sint res = CMP_TERM(a->term, b->term); + + if (res < 0) { + return -1; + } else if (res > 0) { + return 1; + } + + return 0; } static int diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index db9c258cb7..602db106b1 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -413,6 +413,7 @@ bif re:compile/1 bif re:compile/2 bif re:run/2 bif re:run/3 +bif re:internal_run/4 # # Bifs in lists module. diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c index 80e871aaf6..6379e4e04d 100644 --- a/erts/emulator/beam/break.c +++ b/erts/emulator/beam/break.c @@ -84,8 +84,9 @@ process_info(fmtfn_t to, void *to_arg) * they are most likely just created and has invalid data */ if (p->heap != NULL) { - ErtsProcLocks locks = (p == esdp->current_process || - p == esdp->free_process) ? ERTS_PROC_LOCK_MAIN : 0; + ErtsProcLocks locks = ((esdp && (p == esdp->current_process || + p == esdp->free_process)) + ? ERTS_PROC_LOCK_MAIN : 0); print_process_info(to, to_arg, p, locks); } } diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c index ff19ef018e..4537e3e569 100644 --- a/erts/emulator/beam/dist.c +++ b/erts/emulator/beam/dist.c @@ -80,7 +80,7 @@ dist_msg_dbg(ErtsDistExternal *edep, char *what, byte *buf, int sz) byte *extp = edep->data->extp; Eterm msg; Sint ctl_len; - Sint size = ctl_len = erts_decode_dist_ext_size(edep, 0); + Sint size = ctl_len = erts_decode_dist_ext_size(edep, 0, 0); if (size < 0) { erts_fprintf(dbg_file, "DIST MSG DEBUG: erts_decode_dist_ext_size(%s) failed:\n", @@ -1462,7 +1462,7 @@ int erts_net_message(Port *prt, #endif goto data_error; case ERTS_PREP_DIST_EXT_SUCCESS: - ctl_len = erts_decode_dist_ext_size(&ede, 1); + ctl_len = erts_decode_dist_ext_size(&ede, 1, 0); if (ctl_len < 0) { #ifdef ERTS_DIST_MSG_DBG erts_fprintf(dbg_file, "DIST MSG DEBUG: erts_decode_dist_ext_size(CTL) failed:\n"); @@ -1543,39 +1543,6 @@ int erts_net_message(Port *prt, edep = erts_get_dist_ext(&seq->hfrag); ede_hfrag = &seq->hfrag; - /* If the sequence consisted of more than 1 fragment we create one large - binary out of all of the fragments. This because erts_decode_ext - cannot handle a segmented buffer. - TODO: Move this copy to as late as possible, preferably in in the - erts_decode_dist_ext in the receiving process. - */ - if (edep->data->frag_id > 1) { - Uint sz = 0; - Binary *bin; - int i; - byte *ep; - - for (i = 0; i < edep->data->frag_id; i++) - sz += edep->data[i].ext_endp - edep->data[i].extp; - - bin = erts_bin_nrml_alloc(sz); - ep = (byte*)bin->orig_bytes; - - for (i = 0; i < edep->data->frag_id; i++) { - sys_memcpy(ep, edep->data[i].extp, edep->data[i].ext_endp - edep->data[i].extp); - ep += edep->data[i].ext_endp - edep->data[i].extp; - erts_bin_release(edep->data[i].binp); - edep->data[i].binp = NULL; - edep->data[i].extp = NULL; - edep->data[i].ext_endp = NULL; - } - - edep->data->frag_id = 1; - edep->data->extp = (byte*)bin->orig_bytes; - edep->data->ext_endp = ep; - edep->data->binp = bin; - } - break; } default: @@ -2055,10 +2022,17 @@ int erts_net_message(Port *prt, token = tuple[4]; } if (is_not_pid(from) - || dep != external_pid_dist_entry(from) - || is_not_internal_pid(to)) { + || dep != external_pid_dist_entry(from)) { goto invalid_message; } + if (is_not_internal_pid(to)) { + if (is_external_pid(to)) { + DistEntry *dep = external_pid_dist_entry(to); + if (dep == erts_this_dist_entry) + break; /* Old incarnation of this node... */ + } + goto invalid_message; + } if (!erts_proc_lookup(to)) { if (ede_hfrag != NULL) { @@ -3368,7 +3342,7 @@ dist_get_stat_1(BIF_ALIST_1) am_ok, erts_bld_sint64(hpp, szp, read), erts_bld_sint64(hpp, szp, write), - pend ? am_true : am_false); + erts_bld_sint64(hpp, szp, pend)); if (hpp) break; hp = HAlloc(BIF_P, sz); diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 2704b99aa4..0339589b79 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -768,7 +768,7 @@ static ErtsProcessInfoArgs pi_args[] = { {am_memory, 0, ERTS_PI_FLAG_NEED_MSGQ_LEN|ERTS_PI_FLAG_FORCE_SIG_SEND, ERTS_PROC_LOCK_MAIN}, {am_garbage_collection, 3+2 + 3+2 + 3+2 + 3+2 + 3+2 + ERTS_MAX_HEAP_SIZE_MAP_SZ, 0, ERTS_PROC_LOCK_MAIN}, {am_group_leader, 0, 0, ERTS_PROC_LOCK_MAIN}, - {am_reductions, 0, ERTS_PI_FLAG_FORCE_SIG_SEND, ERTS_PROC_LOCK_MAIN}, + {am_reductions, 0, 0, ERTS_PROC_LOCK_MAIN}, {am_priority, 0, 0, 0}, {am_trace, 0, 0, ERTS_PROC_LOCK_MAIN}, {am_binary, 0, ERTS_PI_FLAG_FORCE_SIG_SEND, ERTS_PROC_LOCK_MAIN}, diff --git a/erts/emulator/beam/erl_bif_lists.c b/erts/emulator/beam/erl_bif_lists.c index b23fa77f5f..fa2edfef1e 100644 --- a/erts/emulator/beam/erl_bif_lists.c +++ b/erts/emulator/beam/erl_bif_lists.c @@ -413,12 +413,25 @@ typedef struct { #define ERTS_RBT_GET_LEFT(T) ((T)->left) #define ERTS_RBT_SET_LEFT(T, L) ((T)->left = (L)) #define ERTS_RBT_GET_KEY(T) ((T)->key) -#define ERTS_RBT_CMP_KEYS(KX, KY) CMP_TERM(KX, KY) +#define ERTS_RBT_CMP_KEYS(KX, KY) subtract_term_cmp((KX), (KY)) #define ERTS_RBT_WANT_LOOKUP_INSERT #define ERTS_RBT_WANT_LOOKUP #define ERTS_RBT_WANT_DELETE #define ERTS_RBT_UNDEF +/* erl_rbtree expects comparisons to return an int */ +static int subtract_term_cmp(Eterm a, Eterm b) { + Sint res = CMP_TERM(a, b); + + if (res < 0) { + return -1; + } else if (res > 0) { + return 1; + } + + return 0; +} + #include "erl_rbtree.h" static int subtract_continue(Process *p, ErtsSubtractContext *context); diff --git a/erts/emulator/beam/erl_bif_re.c b/erts/emulator/beam/erl_bif_re.c index e0b9202fe7..b3bf1c7ee3 100644 --- a/erts/emulator/beam/erl_bif_re.c +++ b/erts/emulator/beam/erl_bif_re.c @@ -46,7 +46,7 @@ static Export *urun_trap_exportp = NULL; static Export *ucompile_trap_exportp = NULL; static BIF_RETTYPE re_exec_trap(BIF_ALIST_3); -static BIF_RETTYPE re_run(Process *p, Eterm arg1, Eterm arg2, Eterm arg3); +static BIF_RETTYPE re_run(Process *p, Eterm arg1, Eterm arg2, Eterm arg3, int first); static void *erts_erts_pcre_malloc(size_t size) { return erts_alloc(ERTS_ALC_T_RE_HEAP,size); @@ -1094,7 +1094,7 @@ build_capture(Eterm capture_spec[CAPSPEC_SIZE], const pcre *code) * The actual re:run/2,3 BIFs */ static BIF_RETTYPE -re_run(Process *p, Eterm arg1, Eterm arg2, Eterm arg3) +re_run(Process *p, Eterm arg1, Eterm arg2, Eterm arg3, int first) { const pcre *code_tmp; RestartContext restart; @@ -1120,6 +1120,14 @@ re_run(Process *p, Eterm arg1, Eterm arg2, Eterm arg3) < 0) { BIF_ERROR(p,BADARG); } + if (!first) { + /* + * 'first' is false when re:grun() previously has called re:internal_run() + * with the same subject; i.e., no need to do yet another validation of + * the subject regarding utf8 encoding... + */ + options |= PCRE_NO_UTF8_CHECK; + } is_list_cap = ((pflags & PARSE_FLAG_CAPTURE_OPT) && (capture[CAPSPEC_TYPE] == am_list)); @@ -1360,15 +1368,28 @@ handle_iolist: } BIF_RETTYPE +re_internal_run_4(BIF_ALIST_4) +{ + int first; + if (BIF_ARG_4 == am_false) + first = 0; + else if (BIF_ARG_4 == am_true) + first = !0; + else + BIF_ERROR(BIF_P,BADARG); + return re_run(BIF_P,BIF_ARG_1, BIF_ARG_2, BIF_ARG_3, first); +} + +BIF_RETTYPE re_run_3(BIF_ALIST_3) { - return re_run(BIF_P,BIF_ARG_1, BIF_ARG_2, BIF_ARG_3); + return re_run(BIF_P,BIF_ARG_1, BIF_ARG_2, BIF_ARG_3, !0); } BIF_RETTYPE re_run_2(BIF_ALIST_2) { - return re_run(BIF_P,BIF_ARG_1, BIF_ARG_2, NIL); + return re_run(BIF_P,BIF_ARG_1, BIF_ARG_2, NIL, !0); } /* @@ -1407,6 +1428,7 @@ static BIF_RETTYPE re_exec_trap(BIF_ALIST_3) loop_count = 0xFFFFFFFF; #endif rc = erts_pcre_exec(NULL, &(restartp->extra), NULL, 0, 0, 0, NULL, 0); + ASSERT(loop_count != 0xFFFFFFFF); BUMP_REDS(BIF_P, loop_count / LOOP_FACTOR); if (rc == PCRE_ERROR_LOOP_LIMIT) { diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c index 711e62c795..b31d5b86cb 100644 --- a/erts/emulator/beam/erl_bif_trace.c +++ b/erts/emulator/beam/erl_bif_trace.c @@ -74,7 +74,7 @@ static void smp_bp_finisher(void* arg); static BIF_RETTYPE system_monitor(Process *p, Eterm monitor_pid, Eterm list); -static void new_seq_trace_token(Process* p); /* help func for seq_trace_2*/ +static void new_seq_trace_token(Process* p, int); /* help func for seq_trace_2*/ static Eterm trace_info_pid(Process* p, Eterm pid_spec, Eterm key); static Eterm trace_info_func(Process* p, Eterm pid_spec, Eterm key); static Eterm trace_info_on_load(Process* p, Eterm key); @@ -1874,7 +1874,7 @@ Eterm erts_seq_trace(Process *p, Eterm arg1, Eterm arg2, if (current_flag && ( (arg2 == am_true) || (arg2 == am_false)) ) { /* Flags */ - new_seq_trace_token(p); + new_seq_trace_token(p, 0); flags = unsigned_val(SEQ_TRACE_TOKEN_FLAGS(p)); if (build_result) { old_value = flags & current_flag ? am_true : am_false; @@ -1889,11 +1889,11 @@ Eterm erts_seq_trace(Process *p, Eterm arg1, Eterm arg2, return old_value; } else if (arg1 == am_label) { - new_seq_trace_token(p); + new_seq_trace_token(p, is_not_immed(arg2)); if (build_result) { old_value = SEQ_TRACE_TOKEN_LABEL(p); } - SEQ_TRACE_TOKEN_LABEL(p) = arg2; + SEQ_TRACE_TOKEN_LABEL(p) = arg2; return old_value; } else if (arg1 == am_serial) { @@ -1905,7 +1905,7 @@ Eterm erts_seq_trace(Process *p, Eterm arg1, Eterm arg2, if ((*tp != make_arityval(2)) || is_not_small(*(tp+1)) || is_not_small(*(tp+2))) { return THE_NON_VALUE; } - new_seq_trace_token(p); + new_seq_trace_token(p, 0); if (build_result) { hp = HAlloc(p,3); old_value = TUPLE2(hp, SEQ_TRACE_TOKEN_LASTCNT(p), @@ -1940,8 +1940,8 @@ Eterm erts_seq_trace(Process *p, Eterm arg1, Eterm arg2, } } -void -new_seq_trace_token(Process* p) +static void +new_seq_trace_token(Process* p, int ensure_new_heap) { Eterm* hp; @@ -1953,6 +1953,16 @@ new_seq_trace_token(Process* p) p->common.id, /* Internal pid */ /* From */ make_small(p->seq_trace_lastcnt)); } + else if (ensure_new_heap) { + Eterm* tpl = tuple_val(SEQ_TRACE_TOKEN(p)); + ASSERT(arityval(tpl[0]) == 5); + if (ErtsInArea(tpl, OLD_HEAP(p), + (OLD_HEND(p) - OLD_HEAP(p))*sizeof(Eterm))) { + hp = HAlloc(p, 6); + sys_memcpy(hp, tpl, 6*sizeof(Eterm)); + SEQ_TRACE_TOKEN(p) = make_tuple(hp); + } + } } BIF_RETTYPE erl_seq_trace_info(Process *p, Eterm item) @@ -2050,10 +2060,7 @@ BIF_RETTYPE seq_trace_print_2(BIF_ALIST_2) if (have_no_seqtrace(SEQ_TRACE_TOKEN(BIF_P))) { BIF_RET(am_false); } - if (!(is_atom(BIF_ARG_1) || is_small(BIF_ARG_1))) { - BIF_ERROR(BIF_P, BADARG); - } - if (SEQ_TRACE_TOKEN_LABEL(BIF_P) != BIF_ARG_1) + if (!EQ(BIF_ARG_1, SEQ_TRACE_TOKEN_LABEL(BIF_P))) BIF_RET(am_false); seq_trace_update_send(BIF_P); seq_trace_output(SEQ_TRACE_TOKEN(BIF_P), BIF_ARG_2, diff --git a/erts/emulator/beam/erl_db_catree.c b/erts/emulator/beam/erl_db_catree.c index e0d5e44f58..fed4b44a9b 100644 --- a/erts/emulator/beam/erl_db_catree.c +++ b/erts/emulator/beam/erl_db_catree.c @@ -166,8 +166,17 @@ static void split_catree(DbTableCATree *tb, static void join_catree(DbTableCATree *tb, DbTableCATreeNode *thiz, DbTableCATreeNode *parent); - - +static ERTS_INLINE +int try_wlock_base_node(DbTableCATreeBaseNode *base_node); +static ERTS_INLINE +void wunlock_base_node(DbTableCATreeNode *base_node); +static ERTS_INLINE +void wlock_base_node_no_stats(DbTableCATreeNode *base_node); +static ERTS_INLINE +void wunlock_adapt_base_node(DbTableCATree* tb, + DbTableCATreeNode* node, + DbTableCATreeNode* parent, + int current_level); /* ** External interface */ @@ -210,12 +219,16 @@ DbTableMethod db_catree = * Constants */ -#define ERL_DB_CATREE_LOCK_FAILURE_CONTRIBUTION 200 +#define ERL_DB_CATREE_LOCK_FAILURE_CONTRIBUTION 250 #define ERL_DB_CATREE_LOCK_SUCCESS_CONTRIBUTION (-1) +#define ERL_DB_CATREE_LOCK_GRAVITY_CONTRIBUTION (-500) +#define ERL_DB_CATREE_LOCK_GRAVITY_PATTERN (0xFF800000) #define ERL_DB_CATREE_LOCK_MORE_THAN_ONE_CONTRIBUTION (-10) #define ERL_DB_CATREE_HIGH_CONTENTION_LIMIT 1000 #define ERL_DB_CATREE_LOW_CONTENTION_LIMIT (-1000) -#define ERL_DB_CATREE_MAX_ROUTE_NODE_LAYER_HEIGHT 14 +#define ERL_DB_CATREE_MAX_ROUTE_NODE_LAYER_HEIGHT 16 +#define ERL_DB_CATREE_LOCK_LOW_NO_CONTRIBUTION_LIMIT (-20000) +#define ERL_DB_CATREE_LOCK_HIGH_NO_CONTRIBUTION_LIMIT (20000) /* * Internal CA tree related helper functions and macros @@ -245,6 +258,27 @@ DbTableMethod db_catree = #define SET_LEFT_RELB(ca_tree_route_node, v) erts_atomic_set_relb(&(ca_tree_route_node->u.route.left), (erts_aint_t)(v)); #define SET_RIGHT_RELB(ca_tree_route_node, v) erts_atomic_set_relb(&(ca_tree_route_node->u.route.right), (erts_aint_t)(v)); +/* Change base node lock statistics */ +#define BASE_NODE_STAT_SET(NODE, VALUE) erts_atomic_set_nob(&(NODE)->u.base.lock_statistics, VALUE) +#define BASE_NODE_STAT_READ(NODE) erts_atomic_read_nob(&(NODE)->u.base.lock_statistics) +#define BASE_NODE_STAT_ADD(NODE, VALUE) \ + do { \ + Sint v = erts_atomic_read_nob(&((NODE)->u.base.lock_statistics)); \ + ASSERT(VALUE > 0); \ + if(v < ERL_DB_CATREE_LOCK_HIGH_NO_CONTRIBUTION_LIMIT) { \ + erts_atomic_set_nob(&(NODE->u.base.lock_statistics), v + VALUE); \ + } \ + }while(0); +#define BASE_NODE_STAT_SUB(NODE, VALUE) \ + do { \ + Sint v = erts_atomic_read_nob(&((NODE)->u.base.lock_statistics)); \ + ASSERT(VALUE < 0); \ + if(v > ERL_DB_CATREE_LOCK_LOW_NO_CONTRIBUTION_LIMIT) { \ + erts_atomic_set_nob(&(NODE->u.base.lock_statistics), v + VALUE); \ + } \ + }while(0); + + /* Compares a key to the key in a route node */ static ERTS_INLINE Sint cmp_key_route(Eterm key, DbTableCATreeNode *obj) @@ -653,10 +687,10 @@ static void dbg_provoke_random_splitjoin(DbTableCATree* tb, switch (dbg_fastrand() % 8) { case 1: - base_node->u.base.lock_statistics = 1+ERL_DB_CATREE_HIGH_CONTENTION_LIMIT; + BASE_NODE_STAT_ADD(base_node, 1+ERL_DB_CATREE_HIGH_CONTENTION_LIMIT); break; case 2: - base_node->u.base.lock_statistics = -1+ERL_DB_CATREE_LOW_CONTENTION_LIMIT; + BASE_NODE_STAT_SUB(base_node, -1+ERL_DB_CATREE_LOW_CONTENTION_LIMIT); break; } } @@ -664,6 +698,48 @@ static void dbg_provoke_random_splitjoin(DbTableCATree* tb, # define dbg_provoke_random_splitjoin(T,N) #endif /* PROVOKE_RANDOM_SPLIT_JOIN */ +static ERTS_NOINLINE +void do_random_join(DbTableCATree* tb, Uint rand) +{ + DbTableCATreeNode* node = GET_ROOT_ACQB(tb); + DbTableCATreeNode* parent = NULL; + int level = 0; + Sint stat; + while (!node->is_base_node) { + parent = node; + if ((rand & (1 << level)) == 0) { + node = GET_LEFT_ACQB(node); + } else { + node = GET_RIGHT_ACQB(node); + } + level++; + } + BASE_NODE_STAT_SUB(node, ERL_DB_CATREE_LOCK_GRAVITY_CONTRIBUTION); + stat = BASE_NODE_STAT_READ(node); + if (stat >= ERL_DB_CATREE_LOW_CONTENTION_LIMIT && + stat <= ERL_DB_CATREE_HIGH_CONTENTION_LIMIT) { + return; /* No adaptation */ + } + if (parent != NULL && !try_wlock_base_node(&node->u.base)) { + if (!node->u.base.is_valid) { + wunlock_base_node(node); + return; + } + wunlock_adapt_base_node(tb, node, parent, level); + } +} + +static ERTS_INLINE +void do_random_join_with_low_probability(DbTableCATree* tb, Uint seed) +{ +#ifndef ERTS_DB_CA_TREE_NO_RANDOM_JOIN_WITH_LOW_PROBABILITY + Uint32 rand = erts_sched_local_random(seed); + if (((rand & ERL_DB_CATREE_LOCK_GRAVITY_PATTERN)) == 0) { + do_random_join(tb, rand); + } +#endif +} + static ERTS_INLINE int try_wlock_base_node(DbTableCATreeBaseNode *base_node) { @@ -691,9 +767,9 @@ void wlock_base_node(DbTableCATreeNode *base_node) if (try_wlock_base_node(&base_node->u.base)) { /* The lock is contended */ wlock_base_node_no_stats(base_node); - base_node->u.base.lock_statistics += ERL_DB_CATREE_LOCK_FAILURE_CONTRIBUTION; + BASE_NODE_STAT_ADD(base_node, ERL_DB_CATREE_LOCK_FAILURE_CONTRIBUTION); } else { - base_node->u.base.lock_statistics += ERL_DB_CATREE_LOCK_SUCCESS_CONTRIBUTION; + BASE_NODE_STAT_SUB(base_node, ERL_DB_CATREE_LOCK_SUCCESS_CONTRIBUTION); } } @@ -709,13 +785,14 @@ void wunlock_adapt_base_node(DbTableCATree* tb, DbTableCATreeNode* parent, int current_level) { + Sint base_node_lock_stat = BASE_NODE_STAT_READ(node); dbg_provoke_random_splitjoin(tb,node); if ((!node->u.base.root && parent && !(tb->common.status & DB_CATREE_FORCE_SPLIT)) - || node->u.base.lock_statistics < ERL_DB_CATREE_LOW_CONTENTION_LIMIT) { + || base_node_lock_stat < ERL_DB_CATREE_LOW_CONTENTION_LIMIT) { join_catree(tb, node, parent); } - else if (node->u.base.lock_statistics > ERL_DB_CATREE_HIGH_CONTENTION_LIMIT + else if (base_node_lock_stat > ERL_DB_CATREE_HIGH_CONTENTION_LIMIT && current_level < ERL_DB_CATREE_MAX_ROUTE_NODE_LAYER_HEIGHT) { split_catree(tb, node, parent); } @@ -728,11 +805,23 @@ static ERTS_INLINE void rlock_base_node(DbTableCATreeNode *base_node) { ASSERT(base_node->is_base_node); - erts_rwmtx_rlock(&base_node->u.base.lock); + if (EBUSY == erts_rwmtx_tryrlock(&base_node->u.base.lock)) { + /* The lock is contended */ + BASE_NODE_STAT_ADD(base_node, ERL_DB_CATREE_LOCK_FAILURE_CONTRIBUTION); + erts_rwmtx_rlock(&base_node->u.base.lock); + } +} + +static ERTS_INLINE +void runlock_base_node(DbTableCATreeNode *base_node, DbTableCATree* tb) +{ + ASSERT(base_node->is_base_node); + erts_rwmtx_runlock(&base_node->u.base.lock); + do_random_join_with_low_probability(tb, (Uint)base_node); } static ERTS_INLINE -void runlock_base_node(DbTableCATreeNode *base_node) +void runlock_base_node_no_rand(DbTableCATreeNode *base_node) { ASSERT(base_node->is_base_node); erts_rwmtx_runlock(&base_node->u.base.lock); @@ -814,7 +903,7 @@ void unlock_iter_base_node(CATreeRootIterator* iter) { ASSERT(iter->locked_bnode); if (iter->read_only) - runlock_base_node(iter->locked_bnode); + runlock_base_node(iter->locked_bnode, iter->tb); else if (iter->locked_bnode->u.base.is_valid) { wunlock_adapt_base_node(iter->tb, iter->locked_bnode, iter->bnode_parent, iter->bnode_level); @@ -874,7 +963,7 @@ DbTableCATreeNode* find_rlock_valid_base_node(DbTableCATree* tb, Eterm key) rlock_base_node(base_node); if (base_node->u.base.is_valid) break; - runlock_base_node(base_node); + runlock_base_node_no_rand(base_node); } return base_node; } @@ -923,8 +1012,8 @@ static DbTableCATreeNode *create_base_node(DbTableCATree *tb, "erl_db_catree_base_node", NIL, ERTS_LOCK_FLAGS_CATEGORY_DB); - p->u.base.lock_statistics = ((tb->common.status & DB_CATREE_FORCE_SPLIT) - ? INT_MAX : 0); + BASE_NODE_STAT_SET(p, ((tb->common.status & DB_CATREE_FORCE_SPLIT) + ? INT_MAX : 0)); p->u.base.is_valid = 1; return p; } @@ -1094,7 +1183,7 @@ static void join_catree(DbTableCATree *tb, ASSERT(thiz->is_base_node); if (parent == NULL) { - thiz->u.base.lock_statistics = 0; + BASE_NODE_STAT_SET(thiz, 0); wunlock_base_node(thiz); return; } @@ -1103,11 +1192,11 @@ static void join_catree(DbTableCATree *tb, neighbor = leftmost_base_node(GET_RIGHT_ACQB(parent)); if (try_wlock_base_node(&neighbor->u.base)) { /* Failed to acquire lock */ - thiz->u.base.lock_statistics = 0; + BASE_NODE_STAT_SET(thiz, 0); wunlock_base_node(thiz); return; } else if (!neighbor->u.base.is_valid) { - thiz->u.base.lock_statistics = 0; + BASE_NODE_STAT_SET(thiz, 0); wunlock_base_node(thiz); wunlock_base_node(neighbor); return; @@ -1153,11 +1242,11 @@ static void join_catree(DbTableCATree *tb, neighbor = rightmost_base_node(GET_LEFT_ACQB(parent)); if (try_wlock_base_node(&neighbor->u.base)) { /* Failed to acquire lock */ - thiz->u.base.lock_statistics = 0; + BASE_NODE_STAT_SET(thiz, 0); wunlock_base_node(thiz); return; } else if (!neighbor->u.base.is_valid) { - thiz->u.base.lock_statistics = 0; + BASE_NODE_STAT_SET(thiz, 0); wunlock_base_node(thiz); wunlock_base_node(neighbor); return; @@ -1241,7 +1330,7 @@ static void split_catree(DbTableCATree *tb, if (less_than_two_elements(base->u.base.root)) { if (!(tb->common.status & DB_CATREE_FORCE_SPLIT)) - base->u.base.lock_statistics = 0; + BASE_NODE_STAT_SET(base, 0); wunlock_base_node(base); return; } else { @@ -1521,7 +1610,7 @@ static int db_get_catree(Process *p, DbTable *tbl, Eterm key, Eterm *ret) int result = db_get_tree_common(p, &tb->common, node->u.base.root, key, ret, NULL); - runlock_base_node(node); + runlock_base_node(node, tb); return result; } @@ -1804,7 +1893,7 @@ static int db_member_catree(DbTable *tbl, Eterm key, Eterm *ret) int result = db_member_tree_common(&tb->common, node->u.base.root, key, ret, NULL); - runlock_base_node(node); + runlock_base_node(node, tb); return result; } @@ -1816,7 +1905,7 @@ static int db_get_element_catree(Process *p, DbTable *tbl, int result = db_get_element_tree_common(p, &tb->common, node->u.base.root, key, ndex, ret, NULL); - runlock_base_node(node); + runlock_base_node(node, tb); return result; } @@ -2250,7 +2339,7 @@ void db_catree_force_split(DbTableCATree* tb, int on) init_root_iterator(tb, &iter, 1); root = catree_find_first_root(&iter); do { - iter.locked_bnode->u.base.lock_statistics = (on ? INT_MAX : 0); + BASE_NODE_STAT_SET(iter.locked_bnode, (on ? INT_MAX : 0)); root = catree_find_next_root(&iter, NULL); } while (root); destroy_root_iterator(&iter); diff --git a/erts/emulator/beam/erl_db_catree.h b/erts/emulator/beam/erl_db_catree.h index cf3498dabb..c2c884eee3 100644 --- a/erts/emulator/beam/erl_db_catree.h +++ b/erts/emulator/beam/erl_db_catree.h @@ -42,7 +42,7 @@ typedef struct { typedef struct { erts_rwmtx_t lock; /* The lock for this base node */ - Sint lock_statistics; + erts_atomic_t lock_statistics; int is_valid; /* If this base node is still valid */ TreeDbTerm *root; /* The root of the sequential tree */ ErtsThrPrgrLaterOp free_item; /* Used when freeing using thread progress */ diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index 67a73e4d57..7ab8034606 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -151,6 +151,7 @@ static void grow_new_heap(Process *p, Uint new_sz, Eterm* objv, int nobj); static void sweep_off_heap(Process *p, int fullsweep); static void offset_heap(Eterm* hp, Uint sz, Sint offs, char* area, Uint area_size); static void offset_heap_ptr(Eterm* hp, Uint sz, Sint offs, char* area, Uint area_size); +static void offset_heap_ptr_nstack(Eterm* hp, Uint sz, Sint offs, char* area, Uint area_size); static void offset_rootset(Process *p, Sint offs, char* area, Uint area_size, Eterm* objv, int nobj); static void offset_off_heap(Process* p, Sint offs, char* area, Uint area_size); @@ -1054,9 +1055,10 @@ erts_garbage_collect_hibernate(Process* p) n_htop = tmp_n_htop; \ } while(0) + /* * offset_nstack() can ignore the descriptor-based traversal the other - * nstack procedures use and simply call offset_heap_ptr() instead. + * nstack procedures use and do a simpler word by word traversal instead. * This relies on two facts: * 1. The only live non-Erlang terms on an nstack are return addresses, * and they will be skipped thanks to the low/high range check. @@ -1071,14 +1073,51 @@ static ERTS_INLINE void offset_nstack(Process* p, Sint offs, { if (p->hipe.nstack) { ASSERT(p->hipe.nsp && p->hipe.nstend); - offset_heap_ptr(hipe_nstack_start(p), hipe_nstack_used(p), - offs, area, area_size); + offset_heap_ptr_nstack(hipe_nstack_start(p), hipe_nstack_used(p), + offs, area, area_size); } else { ASSERT(!p->hipe.nsp && !p->hipe.nstend); } } +/* + * This is the same as offset_heap_ptr() + * + * Except for VALGRIND. It allows benign offsetting of undefined (dead) words + * on the nstack while also retaining them as undefined. This suppresses + * valgrinds "Conditional jump or move depends on uninitialised value(s)". + */ +static void +offset_heap_ptr_nstack(Eterm* hp, Uint sz, Sint offs, + char* area, Uint area_size) +{ + while (sz--) { + Eterm val = *hp; +#ifdef VALGRIND + Eterm val_vbits; + VALGRIND_GET_VBITS(&val, &val_vbits, sizeof(val)); + VALGRIND_MAKE_MEM_DEFINED(&val, sizeof(val)); +#endif + switch (primary_tag(val)) { + case TAG_PRIMARY_LIST: + case TAG_PRIMARY_BOXED: + if (ErtsInArea(ptr_val(val), area, area_size)) { +#ifdef VALGRIND + VALGRIND_SET_VBITS(&val, val_vbits, sizeof(val)); +#endif + *hp = offset_ptr(val, offs); + } + hp++; + break; + default: + hp++; + break; + } + } +} + + #else /* !HIPE */ #define fullsweep_nstack(p,n_htop) (n_htop) diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c index 6645341512..1bebf6efe2 100644 --- a/erts/emulator/beam/erl_message.c +++ b/erts/emulator/beam/erl_message.c @@ -527,7 +527,7 @@ erts_msg_attached_data_size_aux(ErtsMessage *msg) if (edep->heap_size < 0) { - sz = erts_decode_dist_ext_size(edep, 1); + sz = erts_decode_dist_ext_size(edep, 1, 1); if (sz < 0) { /* Bad external * We leave the message intact in this case as it's not worth the trouble diff --git a/erts/emulator/beam/erl_proc_sig_queue.c b/erts/emulator/beam/erl_proc_sig_queue.c index f58a606d57..fb900ca7ba 100644 --- a/erts/emulator/beam/erl_proc_sig_queue.c +++ b/erts/emulator/beam/erl_proc_sig_queue.c @@ -3028,7 +3028,7 @@ erts_proc_sig_decode_dist(Process *proc, ErtsProcLocks proc_locks, if (edep->heap_size >= 0) need = edep->heap_size; else { - need = erts_decode_dist_ext_size(edep, 1); + need = erts_decode_dist_ext_size(edep, 1, 1); if (need < 0) { /* bad signal; remove it... */ return 0; @@ -4051,6 +4051,7 @@ erts_proc_sig_signal_size(ErtsSignal *sig) case ERTS_MON_TYPE_DIST_PROC: case ERTS_MON_TYPE_NODE: size = erts_monitor_size((ErtsMonitor *) sig); + break; default: ERTS_INTERNAL_ERROR("Unexpected sig type"); break; diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 1f6adb98ef..de0564292d 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -8568,9 +8568,6 @@ erts_start_schedulers(void) { ethr_tid tid; int res = 0; - Uint actual; - Uint wanted = erts_no_schedulers; - Uint wanted_no_schedulers = erts_no_schedulers; char name[16]; ethr_thr_opts opts = ETHR_THR_OPTS_DEFAULT_INITER; int ix; @@ -8584,40 +8581,34 @@ erts_start_schedulers(void) erts_snprintf(opts.name, 16, "runq_supervisor"); erts_atomic_init_nob(&runq_supervisor_sleeping, 0); if (0 != ethr_event_init(&runq_supervision_event)) - erts_exit(ERTS_ERROR_EXIT, "Failed to create run-queue supervision event\n"); + erts_exit(ERTS_ABORT_EXIT, "Failed to create run-queue supervision event\n"); res = ethr_thr_create(&runq_supervisor_tid, runq_supervisor, NULL, &opts); if (0 != res) - erts_exit(ERTS_ERROR_EXIT, "Failed to create run-queue supervision thread, " + erts_exit(ERTS_ABORT_EXIT, "Failed to create run-queue supervision thread, " "error = %d\n", res); } opts.suggested_stack_size = erts_sched_thread_suggested_stack_size; - if (wanted < 1) - wanted = 1; - if (wanted > ERTS_MAX_NO_OF_SCHEDULERS) { - wanted = ERTS_MAX_NO_OF_SCHEDULERS; - res = ENOTSUP; - } - - for (actual = 0; actual < wanted; actual++) { - ErtsSchedulerData *esdp = ERTS_SCHEDULER_IX(actual); - - ASSERT(actual == esdp->no - 1); - - erts_snprintf(opts.name, 16, "%lu_scheduler", actual + 1); + ASSERT(erts_no_schedulers > 0 && erts_no_schedulers <= ERTS_MAX_NO_OF_SCHEDULERS); + for (ix = 0; ix < erts_no_schedulers; ix++) { + ErtsSchedulerData *esdp = ERTS_SCHEDULER_IX(ix); + ASSERT(ix == esdp->no - 1); + erts_snprintf(opts.name, 16, "%lu_scheduler", ix + 1); res = ethr_thr_create(&esdp->tid, sched_thread_func, (void*)esdp, &opts); - if (res != 0) { - break; + erts_exit(ERTS_ABORT_EXIT, "Failed to create scheduler thread %d, error = %d\n", ix, res); } } - erts_no_schedulers = actual; + + /* Probably not needed as thread create will imply a memory barrier, + but we do one just to be safe. */ + ERTS_THR_MEMORY_BARRIER; { for (ix = 0; ix < erts_no_dirty_cpu_schedulers; ix++) { @@ -8626,7 +8617,7 @@ erts_start_schedulers(void) opts.suggested_stack_size = erts_dcpu_sched_thread_suggested_stack_size; res = ethr_thr_create(&esdp->tid,sched_dirty_cpu_thread_func,(void*)esdp,&opts); if (res != 0) - erts_exit(ERTS_ERROR_EXIT, "Failed to create dirty cpu scheduler thread %d, error = %d\n", ix, res); + erts_exit(ERTS_ABORT_EXIT, "Failed to create dirty cpu scheduler thread %d, error = %d\n", ix, res); } for (ix = 0; ix < erts_no_dirty_io_schedulers; ix++) { ErtsSchedulerData *esdp = ERTS_DIRTY_IO_SCHEDULER_IX(ix); @@ -8634,40 +8625,22 @@ erts_start_schedulers(void) opts.suggested_stack_size = erts_dio_sched_thread_suggested_stack_size; res = ethr_thr_create(&esdp->tid,sched_dirty_io_thread_func,(void*)esdp,&opts); if (res != 0) - erts_exit(ERTS_ERROR_EXIT, "Failed to create dirty io scheduler thread %d, error = %d\n", ix, res); + erts_exit(ERTS_ABORT_EXIT, "Failed to create dirty io scheduler thread %d, error = %d\n", ix, res); } } - ERTS_THR_MEMORY_BARRIER; - erts_snprintf(opts.name, 16, "aux"); res = ethr_thr_create(&tid, aux_thread, NULL, &opts); if (res != 0) - erts_exit(ERTS_ERROR_EXIT, "Failed to create aux thread, error = %d\n", res); + erts_exit(ERTS_ABORT_EXIT, "Failed to create aux thread, error = %d\n", res); for (ix = 0; ix < erts_no_poll_threads; ix++) { erts_snprintf(opts.name, 16, "%d_poller", ix); res = ethr_thr_create(&tid, poll_thread, (void*)(UWord)ix, &opts); if (res != 0) - erts_exit(ERTS_ERROR_EXIT, "Failed to create poll thread\n"); - } - - if (actual < 1) - erts_exit(ERTS_ERROR_EXIT, - "Failed to create any scheduler-threads: %s (%d)\n", - erl_errno_id(res), - res); - if (res != 0) { - erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf(); - ASSERT(actual != wanted_no_schedulers); - erts_dsprintf(dsbufp, - "Failed to create %beu scheduler-threads (%s:%d); " - "only %beu scheduler-thread%s created.\n", - wanted_no_schedulers, erl_errno_id(res), res, - actual, actual == 1 ? " was" : "s were"); - erts_send_error_to_logger_nogl(dsbufp); + erts_exit(ERTS_ABORT_EXIT, "Failed to create poll thread\n"); } } @@ -12097,6 +12070,7 @@ erts_proc_exit_handle_dist_monitor(ErtsMonitor *mon, void *vctxt, Sint reds) ErtsHeapFactory factory; Sint reds_consumed = 0; + ASSERT(c_p->flags & F_DISABLE_GC); ASSERT(erts_monitor_is_target(mon) && mon->type == ERTS_MON_TYPE_DIST_PROC); mdp = erts_monitor_to_data(mon); @@ -12144,7 +12118,6 @@ erts_proc_exit_handle_dist_monitor(ErtsMonitor *mon, void *vctxt, Sint reds) switch (code) { case ERTS_DSIG_SEND_CONTINUE: case ERTS_DSIG_SEND_YIELD: - erts_set_gc_state(c_p, 0); ctxt->dist_state = erts_dsend_export_trap_context(c_p, &ctx); reds_consumed = reds; /* force yield */ break; @@ -12152,7 +12125,6 @@ erts_proc_exit_handle_dist_monitor(ErtsMonitor *mon, void *vctxt, Sint reds) break; case ERTS_DSIG_SEND_TOO_LRG: erts_kill_dist_connection(dep, dist->connection_id); - erts_set_gc_state(c_p, 1); break; default: ASSERT(! "Invalid dsig send exit monitor result"); @@ -12356,6 +12328,7 @@ erts_proc_exit_handle_dist_link(ErtsLink *lnk, void *vctxt, Sint reds) ErtsHeapFactory factory; Sint reds_consumed = 0; + ASSERT(c_p->flags & F_DISABLE_GC); ASSERT(lnk->type == ERTS_LNK_TYPE_DIST_PROC); dlnk = erts_link_to_other(lnk, &ldp); dist = ((ErtsLinkDataExtended *) ldp)->dist; @@ -12395,7 +12368,6 @@ erts_proc_exit_handle_dist_link(ErtsLink *lnk, void *vctxt, Sint reds) switch (code) { case ERTS_DSIG_SEND_YIELD: case ERTS_DSIG_SEND_CONTINUE: - erts_set_gc_state(c_p, 0); ctxt->dist_state = erts_dsend_export_trap_context(c_p, &ctx); reds_consumed = reds; /* force yield */ break; @@ -12403,7 +12375,6 @@ erts_proc_exit_handle_dist_link(ErtsLink *lnk, void *vctxt, Sint reds) break; case ERTS_DSIG_SEND_TOO_LRG: erts_kill_dist_connection(dep, dist->connection_id); - erts_set_gc_state(c_p, 1); break; default: ASSERT(! "Invalid dsig send exit monitor result"); @@ -12951,6 +12922,8 @@ restart: yield_allowed = 0; #endif + /* Enable GC again, through strictly not needed it puts + the process in a consistent state. */ erts_set_gc_state(p, 1); /* Set state to not active as we don't want this process diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 6118c671ee..745a2a482c 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -1230,9 +1230,10 @@ void erts_check_for_holes(Process* p); /* The sequential tracing token is a tuple of size 5: * - * {Flags, Label, Serial, Sender} + * {Flags, Label, Serial, Sender, LastCnt} + * + * WARNING: The top 5-tuple is *MUTABLE* and thus INTERNAL ONLY. */ - #define SEQ_TRACE_TOKEN_ARITY(p) (arityval(*(tuple_val(SEQ_TRACE_TOKEN(p))))) #define SEQ_TRACE_TOKEN_FLAGS(p) (*(tuple_val(SEQ_TRACE_TOKEN(p)) + 1)) #define SEQ_TRACE_TOKEN_LABEL(p) (*(tuple_val(SEQ_TRACE_TOKEN(p)) + 2)) @@ -2352,6 +2353,8 @@ erts_try_change_runq_proc(Process *p, ErtsRunQueue *rq) old_rqint); if (act_rqint == old_rqint) return !0; + + old_rqint = act_rqint; } } @@ -2631,6 +2634,9 @@ void erts_notify_inc_runq(ErtsRunQueue *runq); void erts_sched_finish_poke(ErtsSchedulerSleepInfo *, erts_aint32_t); ERTS_GLB_INLINE void erts_sched_poke(ErtsSchedulerSleepInfo *ssi); void erts_aux_thread_poke(void); +ERTS_GLB_INLINE Uint32 erts_sched_local_random_hash_64_to_32_shift(Uint64 key); +ERTS_GLB_INLINE Uint32 erts_sched_local_random(Uint additional_seed); + #if ERTS_GLB_INLINE_INCL_FUNC_DEF @@ -2647,6 +2653,39 @@ erts_sched_poke(ErtsSchedulerSleepInfo *ssi) } +/* + * Source: https://gist.github.com/badboy/6267743 + * http://web.archive.org/web/20071223173210/http://www.concentric.net/~Ttwang/tech/inthash.htm + */ +ERTS_GLB_INLINE +Uint32 erts_sched_local_random_hash_64_to_32_shift(Uint64 key) +{ + key = (~key) + (key << 18); /* key = (key << 18) - key - 1; */ + key = key ^ (key >> 31); + key = (key + (key << 2)) + (key << 4); + key = key ^ (key >> 11); + key = key + (key << 6); + key = key ^ (key >> 22); + return (Uint32) key; +} + +/* + * This function attempts to return a random number based on the state + * of the scheduler, the current process and the additional_seed + * parameter. + */ +ERTS_GLB_INLINE +Uint32 erts_sched_local_random(Uint additional_seed) +{ + ErtsSchedulerData *esdp = erts_get_scheduler_data(); + Uint64 seed = + additional_seed + + esdp->reductions + + esdp->current_process->fcalls + + (((Uint64)esdp->no) << 32); + return erts_sched_local_random_hash_64_to_32_shift(seed); +} + #endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */ diff --git a/erts/emulator/beam/erl_sched_spec_pre_alloc.h b/erts/emulator/beam/erl_sched_spec_pre_alloc.h index 74cc966cbe..6f715ae80d 100644 --- a/erts/emulator/beam/erl_sched_spec_pre_alloc.h +++ b/erts/emulator/beam/erl_sched_spec_pre_alloc.h @@ -49,7 +49,7 @@ do { \ #endif #ifdef DEBUG -extern Uint erts_no_schedulers; +extern Uint ERTS_WRITE_UNLIKELY(erts_no_schedulers); #endif #define ERTS_SSPA_FORCE_THR_CHECK_PROGRESS 10 diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index c85a7df5ec..9c835ac357 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -713,7 +713,9 @@ trace_sched(Process *p, ErtsProcLocks locks, Eterm what) trace_sched_aux(p, locks, what); } -/* Send {trace_ts, Pid, Send, Msg, DestPid, Timestamp} +/* Send {trace_ts, Pid, Send, Msg, DestPid, PamResult, Timestamp} + * or {trace_ts, Pid, Send, Msg, DestPid, Timestamp} + * or {trace, Pid, Send, Msg, DestPid, PamResult} * or {trace, Pid, Send, Msg, DestPid} * * where 'Send' is 'send' or 'send_to_non_existing_process'. @@ -773,7 +775,9 @@ trace_send(Process *p, Eterm to, Eterm msg) erts_match_set_release_result_trace(p, pam_result); } -/* Send {trace_ts, Pid, receive, Msg, Timestamp} +/* Send {trace_ts, Pid, receive, Msg, PamResult, Timestamp} + * or {trace_ts, Pid, receive, Msg, Timestamp} + * or {trace, Pid, receive, Msg, PamResult} * or {trace, Pid, receive, Msg} */ void diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index ec67ab2aed..ce61cdf040 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -1062,11 +1062,38 @@ bad_dist_ext(ErtsDistExternal *edep) } Sint -erts_decode_dist_ext_size(ErtsDistExternal *edep, int kill_connection) +erts_decode_dist_ext_size(ErtsDistExternal *edep, int kill_connection, int payload) { Sint res; byte *ep; + if (edep->data->frag_id > 1 && payload) { + Uint sz = 0; + Binary *bin; + int i; + byte *ep; + + for (i = 0; i < edep->data->frag_id; i++) + sz += edep->data[i].ext_endp - edep->data[i].extp; + + bin = erts_bin_nrml_alloc(sz); + ep = (byte*)bin->orig_bytes; + + for (i = 0; i < edep->data->frag_id; i++) { + sys_memcpy(ep, edep->data[i].extp, edep->data[i].ext_endp - edep->data[i].extp); + ep += edep->data[i].ext_endp - edep->data[i].extp; + erts_bin_release(edep->data[i].binp); + edep->data[i].binp = NULL; + edep->data[i].extp = NULL; + edep->data[i].ext_endp = NULL; + } + + edep->data->frag_id = 1; + edep->data->extp = (byte*)bin->orig_bytes; + edep->data->ext_endp = ep; + edep->data->binp = bin; + } + if (edep->data->extp >= edep->data->ext_endp) goto fail; #ifndef ERTS_DEBUG_USE_DIST_SEP @@ -1164,6 +1191,7 @@ Eterm erts_decode_ext(ErtsHeapFactory* factory, byte **ext, Uint32 flags) if (flags) { ASSERT(flags == ERTS_DIST_EXT_BTT_SAFE); ede.flags = flags; /* a dummy struct just for the flags */ + ede.data = NULL; edep = &ede; } else { edep = NULL; @@ -1233,8 +1261,10 @@ BIF_RETTYPE erts_debug_dist_ext_to_term_2(BIF_ALIST_2) ede.data->extp = binary_bytes(real_bin)+offset; ede.data->ext_endp = ede.data->extp + size; + ede.data->frag_id = 1; + ede.data->binp = NULL; - hsz = erts_decode_dist_ext_size(&ede, 1); + hsz = erts_decode_dist_ext_size(&ede, 1, 1); if (hsz < 0) goto badarg; @@ -1765,6 +1795,7 @@ static BIF_RETTYPE binary_to_term_int(Process* p, Eterm bin, B2TContext *ctx) case B2TDecodeBinary: { ErtsDistExternal fakedep; fakedep.flags = ctx->flags; + fakedep.data = NULL; dec_term(&fakedep, NULL, NULL, NULL, ctx); break; } @@ -3762,6 +3793,30 @@ dec_term_atom_common: hp += heap_bin_size(n); sys_memcpy(hb->data, ep, n); *objp = make_binary(hb); + } else if (edep && edep->data && edep->data->binp && + n > (edep->data->binp->orig_size / 4)) { + /* If we decode a refc binary from a distribution data + entry we know that it is a refc binary to begin with + so we just increment it and use the reference. This + means that the entire distribution data entry will + remain until this binary is de-allocated so we only + do it if a substantial part (> 25%) of the data + is a binary. */ + ProcBin* pb = (ProcBin *) hp; + Binary* bptr = edep->data->binp; + erts_refc_inc(&bptr->intern.refc, 1); + pb->thing_word = HEADER_PROC_BIN; + pb->size = n; + pb->next = factory->off_heap->first; + factory->off_heap->first = (struct erl_off_heap_header*)pb; + pb->val = bptr; + pb->bytes = (byte*) ep; + ERTS_ASSERT((byte*)(bptr->orig_bytes) < ep && + ep+n <= (byte*)(bptr->orig_bytes+bptr->orig_size)); + pb->flags = 0; + OH_OVERHEAD(factory->off_heap, pb->size / sizeof(Eterm)); + hp += PROC_BIN_SIZE; + *objp = make_binary(pb); } else { Binary* dbin = erts_bin_nrml_alloc(n); diff --git a/erts/emulator/beam/external.h b/erts/emulator/beam/external.h index b556c9076c..e362a6c81f 100644 --- a/erts/emulator/beam/external.h +++ b/erts/emulator/beam/external.h @@ -199,7 +199,7 @@ typedef enum { ErtsPrepDistExtRes erts_prepare_dist_ext(ErtsDistExternal *, byte *, Uint, struct binary *, DistEntry *, Uint32, ErtsAtomCache *); -Sint erts_decode_dist_ext_size(ErtsDistExternal *, int); +Sint erts_decode_dist_ext_size(ErtsDistExternal *, int, int); Eterm erts_decode_dist_ext(ErtsHeapFactory*, ErtsDistExternal *, int); Sint erts_decode_ext_size(byte*, Uint); diff --git a/erts/emulator/beam/instrs.tab b/erts/emulator/beam/instrs.tab index 462ee77e6f..7cffe7fb5c 100644 --- a/erts/emulator/beam/instrs.tab +++ b/erts/emulator/beam/instrs.tab @@ -1064,19 +1064,30 @@ raw_raise() { Eterm class = x(0); Eterm value = x(1); Eterm stacktrace = x(2); + Eterm* freason_ptr; + + /* + * Note that the i_raise instruction will override c_p->freason + * with the freason field stored inside the StackTrace struct in + * ftrace. Therefore, we must take care to store the class both + * inside the StackTrace struct and in c_p->freason (important if + * the class is different from the class of the original + * exception). + */ + freason_ptr = get_freason_ptr_from_exc(stacktrace); if (class == am_error) { - c_p->freason = EXC_ERROR & ~EXF_SAVETRACE; + *freason_ptr = c_p->freason = EXC_ERROR & ~EXF_SAVETRACE; c_p->fvalue = value; c_p->ftrace = stacktrace; goto find_func_info; } else if (class == am_exit) { - c_p->freason = EXC_EXIT & ~EXF_SAVETRACE; + *freason_ptr = c_p->freason = EXC_EXIT & ~EXF_SAVETRACE; c_p->fvalue = value; c_p->ftrace = stacktrace; goto find_func_info; } else if (class == am_throw) { - c_p->freason = EXC_THROWN & ~EXF_SAVETRACE; + *freason_ptr = c_p->freason = EXC_THROWN & ~EXF_SAVETRACE; c_p->fvalue = value; c_p->ftrace = stacktrace; goto find_func_info; diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 10ca74cd60..b9d4f6afcc 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2018. All Rights Reserved. +# Copyright Ericsson AB 1997-2019. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -699,13 +699,18 @@ is_tuple NotTupleFail Tuple=x | is_tagged_tuple WrongRecordFail Tuple Arity Atom is_tagged_tuple_ff f? f? rx A a -get_tuple_element Reg=x P1 D1=x | get_tuple_element Reg=x P2 D2=x | \ +get_tuple_element Reg=x P1 D1=x | \ + get_tuple_element Reg=x P2 D2=x | \ get_tuple_element Reg=x P3 D3=x | \ - succ(P1, P2) | succ(P2, P3) | \ - succ(D1, D2) | succ(D2, D3) => i_get_tuple_element3 Reg P1 D1 + succ(P1, P2) | succ(P2, P3) | succ(D1, D2) | succ(D2, D3) | \ + distinct(D1, Reg) | distinct(D2, Reg) => \ + i_get_tuple_element3 Reg P1 D1 -get_tuple_element Reg=x P1 D1=x | get_tuple_element Reg=x P2 D2=x | \ - succ(P1, P2) | succ(D1, D2) => i_get_tuple_element2 Reg P1 D1 +get_tuple_element Reg=x P1 D1=x | \ + get_tuple_element Reg=x P2 D2=x | \ + succ(P1, P2) | succ(D1, D2) | \ + distinct(D1, Reg) => \ + i_get_tuple_element2 Reg P1 D1 get_tuple_element Reg=x P1 D1=x | get_tuple_element Reg=x P2 D2=x | \ succ(P1, P2) | distinct(D1, Reg) => i_get_tuple_element2_dst Reg P1 D1 D2 @@ -1687,6 +1692,7 @@ i_increment rxy W d # Handle unoptimized code. i_plus S1=c S2=c Fail Dst => move S1 x | i_plus x S2 Fail Dst +i_plus S1=c S2=xy Fail Dst => i_plus S2 S1 Fail Dst i_plus xy xyc j? d diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index c93966d24f..311c5fdd6a 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1997-2018. All Rights Reserved. + * Copyright Ericsson AB 1997-2019. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -1077,6 +1077,7 @@ typedef struct { long forced_events; /* Mask of events that are forcefully signalled on windows see winsock_event_select for details */ + int err; /* Keeps error code for closed socket */ int send_would_block; /* Last send attempt failed with "WOULDBLOCK" */ #endif ErlDrvPort port; /* the port identifier */ @@ -3379,6 +3380,71 @@ static int udp_parse_ancillary_data(ErlDrvTermData *spec, int i, i = LOAD_NIL(spec, i); return LOAD_LIST(spec, i, n+1); } + +static int compile_ancillary_data(struct msghdr *mhdr, + char *ptr, ErlDrvSizeT anc_len) { + struct cmsghdr *cmsg; + size_t controllen = 0; + cmsg = CMSG_FIRSTHDR(mhdr); + for (;;) { + if (anc_len == 0) { + /* End of options to compile */ + mhdr->msg_controllen = controllen; + return 0; + } + if (cmsg == NULL) { + /* End of destination before end of options */ + return 1; + } + +#define COMPILE_ANCILLARY_DATA_ITEM(Level, Opt, Type, Get, Size) \ + do { \ + if (anc_len < (Size)) return 1; \ + sys_memset(cmsg, '\0', CMSG_SPACE(sizeof(Type))); \ + cmsg->cmsg_level = Level; \ + cmsg->cmsg_type = Opt; \ + cmsg->cmsg_len = CMSG_LEN(sizeof(Type)); \ + *((Type *) CMSG_DATA(cmsg)) = Get(ptr); \ + controllen += CMSG_SPACE(sizeof(Type)); \ + cmsg = CMSG_NXTHDR(mhdr, cmsg); \ + ptr += 4; \ + anc_len -= 4; \ + } while (0) +#define SIZEOF_ANCILLARY_DATA (2 * CMSG_SPACE(sizeof(int))) + /* (IP_TOS | IPV6_TCLASS) + IP_TTL */ + + switch (anc_len--, *ptr++) { + case INET_OPT_TOS: { +#if defined(IPPROTO_IP) && defined(IP_TOS) + COMPILE_ANCILLARY_DATA_ITEM(IPPROTO_IP, IP_TOS, int, get_int32, 4); +#else + return 1; /* Socket option not implemented */ +#endif + break; + } + case INET_OPT_TTL: { +#if defined(IPPROTO_IP) && defined(IP_TTL) + COMPILE_ANCILLARY_DATA_ITEM(IPPROTO_IP, IP_TTL, int, get_int32, 4); +#else + return 1; /* Socket option not implemented */ +#endif + break; + } + case INET_OPT_TCLASS: { +#if defined(IPPROTO_IPV6) && defined(IPV6_TCLASS) + COMPILE_ANCILLARY_DATA_ITEM(IPPROTO_IPV6, IPV6_TCLASS, int, get_int32, 4); +#else + return 1; /* Socket option not implemented */ +#endif + break; + } + default: + /* Unknow socket option */ + return 1; + } +#undef COMPILE_ANCILLARY_DATA_ITEM + } +} #endif /* ifndef __WIN32__ */ /* @@ -9063,6 +9129,7 @@ static ErlDrvData inet_start(ErlDrvPort port, int size, int protocol) desc->event_mask = 0; #ifdef __WIN32__ desc->forced_events = 0; + desc->err = 0; desc->send_would_block = 0; #endif desc->port = port; @@ -9846,10 +9913,8 @@ static tcp_descriptor* tcp_inet_copy(tcp_descriptor* desc,SOCKET s, copy_desc->send_timeout = desc->send_timeout; copy_desc->send_timeout_close = desc->send_timeout_close; - if (desc->tcp_add_flags & TCP_ADDF_SHOW_ECONNRESET) - copy_desc->tcp_add_flags |= TCP_ADDF_SHOW_ECONNRESET; - else - copy_desc->tcp_add_flags &= ~TCP_ADDF_SHOW_ECONNRESET; + copy_desc->tcp_add_flags = desc->tcp_add_flags + & (TCP_ADDF_SHOW_ECONNRESET | TCP_ADDF_LINGER_ZERO); /* The new port will be linked and connected to the original caller */ port = driver_create_port(port, owner, "tcp_inet", (ErlDrvData) copy_desc); @@ -10914,7 +10979,7 @@ static int winsock_event_select(inet_descriptor *desc, int flags, int on) { int save_event_mask = desc->event_mask; - desc->forced_events = 0; + desc->forced_events &= FD_CLOSE; if (on) desc->event_mask |= flags; else @@ -10966,7 +11031,7 @@ static int winsock_event_select(inet_descriptor *desc, int flags, int on) TIMEVAL tmo = {0,0}; FD_SET fds; int ret; - + FD_ZERO(&fds); FD_SET(desc->s,&fds); do_force = (select(desc->s+1,0,&fds,0,&tmo) > 0); @@ -10983,7 +11048,7 @@ static int winsock_event_select(inet_descriptor *desc, int flags, int on) FD_SET fds; int ret; unsigned long arg; - + FD_ZERO(&fds); FD_SET(desc->s,&fds); ret = select(desc->s+1,&fds,0,0,&tmo); @@ -11022,13 +11087,16 @@ static void tcp_inet_event(ErlDrvData e, ErlDrvEvent event) goto error; } - DEBUGF((" => event=%02X, mask=%02X\r\n", - netEv.lNetworkEvents, desc->inet.event_mask)); + DEBUGF((" => event=%02X, mask=%02X, forced=%02X\r\n", + netEv.lNetworkEvents, desc->inet.event_mask, + desc->inet.forced_events)); /* Add the forced events. */ - netEv.lNetworkEvents |= desc->inet.forced_events; + if (desc->inet.forced_events & FD_CLOSE) + netEv.iErrorCode[FD_CLOSE_BIT] = desc->inet.err; + /* * Calling WSAEventSelect() with a mask of 0 doesn't always turn off * all events. To avoid acting on events we don't want, we mask @@ -11048,16 +11116,29 @@ static void tcp_inet_event(ErlDrvData e, ErlDrvEvent event) goto error; } if (netEv.lNetworkEvents & FD_CLOSE) { - /* + + /* We may not get any more FD_CLOSE events so we + keep this event and always signal it from + this moment on. */ + if ((desc->inet.forced_events & FD_CLOSE) == 0) { + desc->inet.forced_events |= FD_CLOSE; + desc->inet.err = netEv.iErrorCode[FD_CLOSE_BIT]; + } + + /* * We must loop to read out the remaining packets (if any). */ for (;;) { - DEBUGF(("Retrying read due to closed port\r\n")); - /* XXX The buffer will be thrown away on error (empty que). - Possible SMP FIXME. */ - if (!desc->inet.active && (desc->inet.opt) == NULL) { - goto error; - } + + /* if passive and no subscribers, break loop */ + if (!desc->inet.active && desc->inet.opt == NULL) { + /* do not trigger close event when socket is + transitioned to passive */ + netEv.lNetworkEvents &= ~FD_CLOSE; + break; + } + + DEBUGF(("Retrying read due to FD_CLOSE\r\n")); if (tcp_inet_input(desc, event) < 0) { goto error; } @@ -11386,7 +11467,7 @@ static int tcp_shutdown_error(tcp_descriptor* desc, int err) static void tcp_inet_delay_send(ErlDrvData data, ErlDrvTermData dummy) { tcp_descriptor *desc = (tcp_descriptor*)data; - (void)tcp_inet_output(desc, INETP(desc)->s); + (void)tcp_inet_output(desc, (HANDLE) INETP(desc)->s); } /* @@ -12553,7 +12634,7 @@ static void packet_inet_timeout(ErlDrvData e) sock_select(desc, FD_READ, 0); async_error_am (desc, am_timeout); } else { - (void)packet_inet_input(udesc, desc->s); + (void)packet_inet_input(udesc, (HANDLE) desc->s); } } @@ -12597,11 +12678,8 @@ static void packet_inet_command(ErlDrvData e, char* buf, ErlDrvSizeT len) char ancd[CMSG_SPACE(sizeof(*sri))]; } cmsg; - if (len < SCTP_GET_SENDPARAMS_LEN) { - inet_reply_error(desc, EINVAL); - return; - } - + if (len < SCTP_GET_SENDPARAMS_LEN) goto return_einval; + /* The ancillary data */ sri = (struct sctp_sndrcvinfo *) (CMSG_DATA(&cmsg.hdr)); /* Get the "sndrcvinfo" from the buffer, advancing the "ptr": */ @@ -12634,28 +12712,85 @@ static void packet_inet_command(ErlDrvData e, char* buf, ErlDrvSizeT len) goto check_result_code; } #endif - /* UDP socket. Even if it is connected, there is an address prefix - here -- ignored for connected sockets: */ - sz = len; - qtr = ptr; - xerror = inet_set_faddress(desc->sfamily, &other, &qtr, &sz); - if (xerror != NULL) { - inet_reply_error_am(desc, driver_mk_atom(xerror)); - return; - } - len -= (qtr - ptr); - ptr = qtr; - /* Now "ptr" is the user data ptr, "len" is data length: */ - inet_output_count(desc, len); - - if (desc->state & INET_F_ACTIVE) { /* connected (ignore address) */ - code = sock_send(desc->s, ptr, len, 0); - } - else { - code = sock_sendto(desc->s, ptr, len, 0, &other.sa, sz); + { + ErlDrvSizeT anc_len; + + /* UDP socket. Even if it is connected, there is an address prefix + here -- ignored for connected sockets: */ + sz = len; + qtr = ptr; + xerror = inet_set_faddress(desc->sfamily, &other, &qtr, &sz); + if (xerror != NULL) { + inet_reply_error_am(desc, driver_mk_atom(xerror)); + return; + } + len -= (qtr - ptr); + ptr = qtr; + + /* Here comes ancillary data */ + if (len < 4) goto return_einval; + anc_len = get_int32(ptr); + len -= 4; ptr += 4; + if (len < anc_len) goto return_einval; + + if (anc_len == 0 && !!0/*XXX-short-circuit-for-testing*/) { + /* Empty ancillary data */ + /* Now "ptr" is the user data ptr, "len" is data length: */ + inet_output_count(desc, len); + if (desc->state & INET_F_ACTIVE) { + /* connected (ignore address) */ + code = sock_send(desc->s, ptr, len, 0); + } + else { + code = sock_sendto(desc->s, ptr, len, 0, &other.sa, sz); + } + } + else { +#ifdef __WIN32__ + goto return_einval; /* Can not send ancillary data on Windows */ +#else + struct iovec iov[1]; + struct msghdr mhdr; + union { /* For ancillary data */ + struct cmsghdr hdr; + char ancd[SIZEOF_ANCILLARY_DATA]; + } cmsg; + sys_memset(&iov, '\0', sizeof(iov)); + sys_memset(&mhdr, '\0', sizeof(mhdr)); + sys_memset(&cmsg, '\0', sizeof(cmsg)); + if (desc->state & INET_F_ACTIVE) { + /* connected (ignore address) */ + mhdr.msg_name = NULL; + mhdr.msg_namelen = 0; + } + else { + mhdr.msg_name = &other; + mhdr.msg_namelen = sz; + } + mhdr.msg_control = cmsg.ancd; + mhdr.msg_controllen = sizeof(cmsg.ancd); + if (compile_ancillary_data(&mhdr, ptr, anc_len) != 0) { + goto return_einval; + } + if (mhdr.msg_controllen == 0) { + /* XXX Testing - only possible for anc_len == 0 */ + mhdr.msg_control = NULL; + } + len -= anc_len; + ptr += anc_len; + /* Now "ptr" is the user data ptr, "len" is data length: */ + iov[0].iov_len = len; + iov[0].iov_base = ptr; + mhdr.msg_iov = iov; + mhdr.msg_iovlen = 1; + mhdr.msg_flags = 0; + inet_output_count(desc, len); + code = sock_sendmsg(desc->s, &mhdr, 0); +#endif + } } -#ifdef HAVE_SCTP +#ifdef HAVE_SCTP check_result_code: /* "code" analysis is the same for both SCTP and UDP cases above: */ #endif @@ -12665,8 +12800,15 @@ static void packet_inet_command(ErlDrvData e, char* buf, ErlDrvSizeT len) } else inet_reply_ok(desc); + return; + + return_einval: + inet_reply_error(desc, EINVAL); + return; } -#endif + +#endif /* HAVE_UDP */ + #ifdef __WIN32__ static void packet_inet_event(ErlDrvData e, ErlDrvEvent event) diff --git a/erts/emulator/hipe/hipe_arm.c b/erts/emulator/hipe/hipe_arm.c index b61939724c..3e1d7e4d5e 100644 --- a/erts/emulator/hipe/hipe_arm.c +++ b/erts/emulator/hipe/hipe_arm.c @@ -18,6 +18,7 @@ * %CopyrightEnd% */ +#ifdef __arm__ #include <stddef.h> /* offsetof() */ #ifdef HAVE_CONFIG_H @@ -30,24 +31,39 @@ #include "hipe_native_bif.h" /* nbif_callemu() */ #include "hipe_bif0.h" +#ifndef __has_builtin +# define __has_builtin(x) 0 +#endif + /* Flush dcache and invalidate icache for a range of addresses. */ void hipe_flush_icache_range(void *address, unsigned int nbytes) { -#if defined(__ARM_EABI__) + void* end = (char*)address + nbytes; + +#if ERTS_AT_LEAST_GCC_VSN__(4, 3, 0) || __has_builtin(__builtin___clear_cache) + __builtin___clear_cache(address, end); +#elif defined(__clang__) + void __clear_cache(void *start, void *end); + __clear_cache(address, end); +#elif defined(__linux__) +# if defined(__ARM_EABI__) register unsigned long beg __asm__("r0") = (unsigned long)address; - register unsigned long end __asm__("r1") = (unsigned long)address + nbytes; + register unsigned long end __asm__("r1") = (unsigned long)end; register unsigned long flg __asm__("r2") = 0; register unsigned long scno __asm__("r7") = 0xf0002; __asm__ __volatile__("swi 0" /* sys_cacheflush() */ : "=r"(beg) : "0"(beg), "r"(end), "r"(flg), "r"(scno)); -#else +# else register unsigned long beg __asm__("r0") = (unsigned long)address; - register unsigned long end __asm__("r1") = (unsigned long)address + nbytes; + register unsigned long end __asm__("r1") = (unsigned long)end; register unsigned long flg __asm__("r2") = 0; __asm__ __volatile__("swi 0x9f0002" /* sys_cacheflush() */ : "=r"(beg) : "0"(beg), "r"(end), "r"(flg)); +# endif +#else +# error "Don't know how to flush instruction cache" #endif } @@ -270,3 +286,5 @@ void hipe_arch_print_pcb(struct hipe_process_state *p) U("narity ", narity); #undef U } + +#endif /*__arm__*/ diff --git a/erts/emulator/hipe/hipe_x86.h b/erts/emulator/hipe/hipe_x86.h index 8967793171..a1fe75e792 100644 --- a/erts/emulator/hipe/hipe_x86.h +++ b/erts/emulator/hipe/hipe_x86.h @@ -22,17 +22,28 @@ #ifndef HIPE_X86_H #define HIPE_X86_H -static __inline__ void hipe_flush_icache_word(void *address) -{ - /* Do nothing. This works as long as compiled code is - executed by a single CPU thread. */ -} +#ifndef __has_builtin +# define __has_builtin(x) 0 +#endif static __inline__ void hipe_flush_icache_range(void *address, unsigned int nbytes) { - /* Do nothing. This works as long as compiled code is - executed by a single CPU thread. */ + void* end = (char*)address + nbytes; + +#if ERTS_AT_LEAST_GCC_VSN__(4, 3, 0) || __has_builtin(__builtin___clear_cache) + __builtin___clear_cache(address, end); +#elif defined(__clang__) + void __clear_cache(void *start, void *end); + __clear_cache(address, end); +#else +# warning "Don't know how to flush instruction cache" +#endif +} + +static __inline__ void hipe_flush_icache_word(void *address) +{ + hipe_flush_icache_range(address, sizeof(void*)); } /* for stack descriptor hash lookup */ diff --git a/erts/emulator/nifs/common/socket_dbg.c b/erts/emulator/nifs/common/socket_dbg.c index 96f75a328f..7dfc4b77bc 100644 --- a/erts/emulator/nifs/common/socket_dbg.c +++ b/erts/emulator/nifs/common/socket_dbg.c @@ -30,6 +30,7 @@ #include <time.h> #include <erl_nif.h> +#include "socket_util.h" #include "socket_dbg.h" #define TSELF() enif_thread_self() @@ -38,12 +39,6 @@ static FILE* dbgout = NULL; -#if defined(CLOCK_REALTIME) -static int realtime(struct timespec* tsP); -static int timespec2str(char *buf, unsigned int len, struct timespec *ts); -#endif - - extern void esock_dbg_init(char* filename) { @@ -73,10 +68,7 @@ void esock_dbg_printf( const char* prefix, const char* format, ... ) { va_list args; char f[512 + sizeof(format)]; // This has to suffice... -#if defined(CLOCK_REALTIME) char stamp[30]; - struct timespec ts; -#endif int res; /* @@ -85,64 +77,21 @@ void esock_dbg_printf( const char* prefix, const char* format, ... ) * But then I must change the API....something for later. */ -#if defined(CLOCK_REALTIME) - if (!realtime(&ts) && - (timespec2str(stamp, sizeof(stamp), &ts) == 0)) { + if (esock_timestamp(stamp, sizeof(stamp))) { res = enif_snprintf(f, sizeof(f), "%s [%s] [%s] %s", prefix, stamp, TSNAME(), format); } else { res = enif_snprintf(f, sizeof(f), "%s [%s] %s", prefix, TSNAME(), format); } -#else - res = enif_snprintf(f, sizeof(f), "%s [%s] %s", - prefix, TSNAME(), format); -#endif if (res > 0) { va_start (args, format); enif_vfprintf (dbgout, f, args); va_end (args); - fflush(stdout); + fflush(dbgout); } return; } - -#if defined(CLOCK_REALTIME) -static -int realtime(struct timespec* tsP) -{ - return clock_gettime(CLOCK_REALTIME, tsP); -} - - - - -/* - * Convert a timespec struct into a readable/printable string - */ -static -int timespec2str(char *buf, unsigned int len, struct timespec *ts) -{ - int ret, buflen; - struct tm t; - - tzset(); - if (localtime_r(&(ts->tv_sec), &t) == NULL) - return 1; - - ret = strftime(buf, len, "%F %T", &t); - if (ret == 0) - return 2; - len -= ret - 1; - buflen = strlen(buf); - - ret = snprintf(&buf[buflen], len, ".%06ld", ts->tv_nsec/1000); - if (ret >= len) - return 3; - - return 0; -} -#endif diff --git a/erts/emulator/nifs/common/socket_int.h b/erts/emulator/nifs/common/socket_int.h index 38c28a6de5..d6977be5aa 100644 --- a/erts/emulator/nifs/common/socket_int.h +++ b/erts/emulator/nifs/common/socket_int.h @@ -139,6 +139,7 @@ typedef unsigned int BOOLEAN_T; GLOBAL_ATOM_DEF(ctrunc); \ GLOBAL_ATOM_DEF(data); \ GLOBAL_ATOM_DEF(debug); \ + GLOBAL_ATOM_DEF(default); \ GLOBAL_ATOM_DEF(default_send_params); \ GLOBAL_ATOM_DEF(delayed_ack_time); \ GLOBAL_ATOM_DEF(dgram); \ diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index ee3b9f2a98..56a16a87a1 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -429,6 +429,7 @@ static void (*esock_sctp_freepaddrs)(struct sockaddr *addrs) = NULL; #define SOCKET_STATE_CONNECTING (SOCKET_STATE_OPEN | SOCKET_FLAG_CON) #define SOCKET_STATE_ACCEPTING (SOCKET_STATE_LISTENING | SOCKET_FLAG_ACC) #define SOCKET_STATE_CLOSING (SOCKET_FLAG_CLOSE) +#define SOCKET_STATE_DTOR (0xFFFF) #define IS_CLOSED(d) \ ((d)->state == SOCKET_STATE_CLOSED) @@ -485,6 +486,9 @@ static void (*esock_sctp_freepaddrs)(struct sockaddr *addrs) = NULL; #define SOCKET_OPT_VALUE_TYPE_INT 1 #define SOCKET_OPT_VALUE_TYPE_BOOL 2 +#define ESOCK_DESC_PATTERN_CREATED 0x03030303 +#define ESOCK_DESC_PATTERN_DTOR 0xC0C0C0C0 + typedef union { struct { // 0 = not open, 1 = open @@ -528,6 +532,7 @@ typedef union { #define SOCKET_TYPE_SEQPACKET 5 /* protocol */ +#define SOCKET_PROTOCOL_DEFAULT 0 #define SOCKET_PROTOCOL_IP 1 #define SOCKET_PROTOCOL_TCP 2 #define SOCKET_PROTOCOL_UDP 3 @@ -658,6 +663,10 @@ typedef union { #define SOCKET_SUPPORTS_OPTIONS 0x0001 #define SOCKET_SUPPORTS_SCTP 0x0002 #define SOCKET_SUPPORTS_IPV6 0x0003 +#define SOCKET_SUPPORTS_LOCAL 0x0004 + +#define ESOCK_WHICH_PROTO_ERROR -1 +#define ESOCK_WHICH_PROTO_UNSUP -2 @@ -803,6 +812,14 @@ typedef struct { typedef struct { + /* + * +++ This is a way to, possibly, detect memory overrides "and stuff" +++ + * + * We have two patterns. One is set when the descriptor is created (allocated) + * and one is set when the descriptor is dtor'ed. + */ + Uint32 pattern; + /* +++ The actual socket +++ */ SOCKET sock; HANDLE event; @@ -820,6 +837,10 @@ typedef struct { ErlNifPid ctrlPid; ESockMonitor ctrlMon; + /* +++ Connector process +++ */ + ErlNifPid connPid; + ESockMonitor connMon; + /* +++ Write stuff +++ */ ErlNifMutex* writeMtx; ESockRequestor currentWriter; @@ -994,12 +1015,15 @@ static ERL_NIF_TERM nsupports_options_udp(ErlNifEnv* env); static ERL_NIF_TERM nsupports_options_sctp(ErlNifEnv* env); static ERL_NIF_TERM nsupports_sctp(ErlNifEnv* env); static ERL_NIF_TERM nsupports_ipv6(ErlNifEnv* env); +static ERL_NIF_TERM nsupports_local(ErlNifEnv* env); static ERL_NIF_TERM nopen(ErlNifEnv* env, int domain, int type, int protocol, char* netns); +static BOOLEAN_T nopen_which_protocol(SOCKET sock, int* proto); + static ERL_NIF_TERM nbind(ErlNifEnv* env, ESockDescriptor* descP, ESockAddress* sockAddrP, @@ -2369,6 +2393,8 @@ static int socket_setopt(int sock, const void* optVal, const socklen_t optLen); +static BOOLEAN_T is_connector(ErlNifEnv* env, + ESockDescriptor* descP); static BOOLEAN_T verify_is_connected(ESockDescriptor* descP, int* err); static ESockDescriptor* alloc_descriptor(SOCKET sock, HANDLE event); @@ -2644,6 +2670,7 @@ static char str_exsend[] = "exsend"; // failed send GLOBAL_ATOM_DECL(ctrunc); \ GLOBAL_ATOM_DECL(data); \ GLOBAL_ATOM_DECL(debug); \ + GLOBAL_ATOM_DECL(default); \ GLOBAL_ATOM_DECL(default_send_params); \ GLOBAL_ATOM_DECL(delayed_ack_time); \ GLOBAL_ATOM_DECL(dgram); \ @@ -3046,6 +3073,9 @@ ERL_NIF_TERM nif_info(ErlNifEnv* env, * {tcp, [{Opt, boolean()}]}, * {udp, [{Opt, boolean()}]}, * {sctp, [{Opt, boolean()}]}] + * sctp boolean() + * ipv6 boolean() + * local boolean() */ static @@ -3073,13 +3103,10 @@ ERL_NIF_TERM nif_supports(ErlNifEnv* env, -/* nopen - create an endpoint for communication +/* nsupports - what features do we support * - * Assumes the input has been validated. - * - * Normally we want debugging on (individual) sockets to be controlled - * by the sockets own debug flag. But since we don't even have a socket - * yet, we must use the global debug flag. + * This is to prove information about what features actually + * work on the current platform. */ #if !defined(__WIN32__) static @@ -3102,6 +3129,10 @@ ERL_NIF_TERM nsupports(ErlNifEnv* env, int key) result = nsupports_ipv6(env); break; + case SOCKET_SUPPORTS_LOCAL: + result = nsupports_local(env); + break; + default: result = esock_atom_false; break; @@ -4004,7 +4035,7 @@ ERL_NIF_TERM nsupports_options_tcp(ErlNifEnv* env) /* *** SOCKET_OPT_TCP_MAXSEG => TCP_MAXSEG *** */ -#if defined(TCP_) +#if defined(TCP_MAXSEG) tmp = MKT2(env, esock_atom_maxseg, esock_atom_true); #else tmp = MKT2(env, esock_atom_maxseg, esock_atom_false); @@ -4018,7 +4049,7 @@ ERL_NIF_TERM nsupports_options_tcp(ErlNifEnv* env) /* *** SOCKET_OPT_TCP_NODELAY => TCP_NODELAY *** */ -#if defined(TCP_) +#if defined(TCP_NODELAY) tmp = MKT2(env, esock_atom_nodelay, esock_atom_true); #else tmp = MKT2(env, esock_atom_nodelay, esock_atom_false); @@ -4324,6 +4355,24 @@ ERL_NIF_TERM nsupports_ipv6(ErlNifEnv* env) +#if !defined(__WIN32__) +static +ERL_NIF_TERM nsupports_local(ErlNifEnv* env) +{ + ERL_NIF_TERM supports; + +#if defined(AF_LOCAL) + supports = esock_atom_true; +#else + supports = esock_atom_false; +#endif + + return supports; +} +#endif + + + /* ---------------------------------------------------------------------- * nif_open * @@ -4421,6 +4470,7 @@ ERL_NIF_TERM nif_open(ErlNifEnv* env, * yet, we must use the global debug flag. */ #if !defined(__WIN32__) + static ERL_NIF_TERM nopen(ErlNifEnv* env, int domain, int type, int protocol, @@ -4428,7 +4478,7 @@ ERL_NIF_TERM nopen(ErlNifEnv* env, { ESockDescriptor* descP; ERL_NIF_TERM res; - int save_errno = 0; + int proto = protocol, save_errno = 0; SOCKET sock; HANDLE event; #ifdef HAVE_SETNS @@ -4448,11 +4498,35 @@ ERL_NIF_TERM nopen(ErlNifEnv* env, return esock_make_error_errno(env, save_errno); #endif - if ((sock = sock_open(domain, type, protocol)) == INVALID_SOCKET) + if ((sock = sock_open(domain, type, proto)) == INVALID_SOCKET) return esock_make_error_errno(env, sock_errno()); SGDBG( ("SOCKET", "nopen -> open success: %d\r\n", sock) ); + + /* NOTE that if the protocol = 0 (default) and the domain is not + * local (AF_LOCAL) we need to explicitly get the protocol here! + */ + + if ((proto == 0) +#if defined(AF_LOCAL) + && (domain != AF_LOCAL) +#endif + ) + if (!nopen_which_protocol(sock, &proto)) { + if (proto == ESOCK_WHICH_PROTO_ERROR) { + save_errno = sock_errno(); + while ((sock_close(sock) == INVALID_SOCKET) && + (sock_errno() == EINTR)); + return esock_make_error_errno(env, save_errno); + } else { + while ((sock_close(sock) == INVALID_SOCKET) && + (sock_errno() == EINTR)); + return esock_make_error(env, esock_atom_eafnosupport); + } + } + + #ifdef HAVE_SETNS if ((netns != NULL) && !restore_network_namespace(current_ns, sock, &save_errno)) @@ -4484,7 +4558,7 @@ ERL_NIF_TERM nopen(ErlNifEnv* env, descP->state = SOCKET_STATE_OPEN; descP->domain = domain; descP->type = type; - descP->protocol = protocol; + descP->protocol = proto; /* Does this apply to other types? Such as RAW? * Also, is this really correct? Should we not wait for bind? @@ -4521,6 +4595,32 @@ ERL_NIF_TERM nopen(ErlNifEnv* env, return esock_make_ok2(env, res); } + + +static +BOOLEAN_T nopen_which_protocol(SOCKET sock, int* proto) +{ +#if defined(SO_PROTOCOL) + int val; + SOCKOPTLEN_T valSz = sizeof(val); + int res; + + res = sock_getopt(sock, SOL_SOCKET, SO_PROTOCOL, &val, &valSz); + + if (res != 0) { + *proto = ESOCK_WHICH_PROTO_ERROR; + return FALSE; + } else { + *proto = val; + return TRUE; + } +#else + *proto = ESOCK_WHICH_PROTO_UNSUP; + return FALSE; +#endif +} + + #endif // if !defined(__WIN32__) @@ -4813,17 +4913,17 @@ ERL_NIF_TERM nconnect(ErlNifEnv* env, return esock_make_error(env, atom_closed); if (!IS_OPEN(descP)) { - SSDBG( descP, ("SOCKET", "nif_connect -> not open\r\n") ); + SSDBG( descP, ("SOCKET", "nconnect -> not open\r\n") ); return esock_make_error(env, atom_exbadstate); } if (IS_CONNECTED(descP)) { - SSDBG( descP, ("SOCKET", "nif_connect -> already connected\r\n") ); + SSDBG( descP, ("SOCKET", "nconnect -> already connected\r\n") ); return esock_make_error(env, atom_eisconn); } - if (IS_CONNECTING(descP)) { - SSDBG( descP, ("SOCKET", "nif_connect -> already connecting\r\n") ); + if (IS_CONNECTING(descP) && !is_connector(env, descP)) { + SSDBG( descP, ("SOCKET", "nconnect -> already connecting\r\n") ); return esock_make_error(env, esock_atom_einval); } @@ -4837,31 +4937,93 @@ ERL_NIF_TERM nconnect(ErlNifEnv* env, descP->addrLen); save_errno = sock_errno(); - SSDBG( descP, ("SOCKET", "nif_connect -> connect result: %d, %d\r\n", + SSDBG( descP, ("SOCKET", "nconnect -> connect result: %d, %d\r\n", code, save_errno) ); - if (IS_SOCKET_ERROR(code) && - ((save_errno == ERRNO_BLOCK) || /* Winsock2 */ - (save_errno == EINPROGRESS))) { /* Unix & OSE!! */ - ref = MKREF(env); - descP->state = SOCKET_STATE_CONNECTING; - if ((sres = esock_select_write(env, descP->sock, descP, NULL, - sockRef, ref)) < 0) { - res = esock_make_error(env, - MKT2(env, - esock_atom_select_failed, - MKI(env, sres))); - } else { - res = esock_make_ok2(env, ref); + if (IS_SOCKET_ERROR(code)) { + switch (save_errno) { + case ERRNO_BLOCK: /* Winsock2 */ + case EINPROGRESS: /* Unix & OSE!! */ + SSDBG( descP, ("SOCKET", "nconnect -> would block => select\r\n") ); + + ref = MKREF(env); + + if (IS_CONNECTING(descP)) { + /* Glitch */ + res = esock_make_ok2(env, ref); + } else { + + /* First time here */ + + if (enif_self(env, &descP->connPid) == NULL) + return esock_make_error(env, atom_exself); + + if (MONP("nconnect -> conn", + env, descP, + &descP->connPid, + &descP->connMon) != 0) + return esock_make_error(env, atom_exmon); + + descP->state = SOCKET_STATE_CONNECTING; + + if ((sres = esock_select_write(env, descP->sock, descP, NULL, + sockRef, ref)) < 0) { + res = esock_make_error(env, + MKT2(env, + esock_atom_select_failed, + MKI(env, sres))); + } else { + res = esock_make_ok2(env, ref); + } + } + break; + + case EISCONN: + SSDBG( descP, ("SOCKET", "nconnect -> *already* connected\r\n") ); + { + /* This is ***strange*** so make sure */ + int err = 0; + if (!verify_is_connected(descP, &err)) { + descP->state = SOCKET_STATE_OPEN; /* restore state */ + res = esock_make_error_errno(env, err); + } else { + descP->state = SOCKET_STATE_CONNECTED; + /* And just to be on the safe side, reset these */ + enif_set_pid_undefined(&descP->connPid); + DEMONP("nconnect -> connected", + env, descP, &descP->connMon); + descP->isReadable = TRUE; + descP->isWritable = TRUE; + res = esock_atom_ok; + } + } + break; + + default: + SSDBG( descP, ("SOCKET", "nconnect -> other error(1): %d\r\n", + save_errno) ); + res = esock_make_error_errno(env, save_errno); + break; } + } else if (code == 0) { /* ok we are connected */ + SSDBG( descP, ("SOCKET", "nconnect -> connected\r\n") ); + descP->state = SOCKET_STATE_CONNECTED; + enif_set_pid_undefined(&descP->connPid); + DEMONP("nconnect -> connected", env, descP, &descP->connMon); descP->isReadable = TRUE; descP->isWritable = TRUE; res = esock_atom_ok; + } else { + /* Do we really need this case? */ + + SSDBG( descP, ("SOCKET", "nconnect -> other error(2): %d\r\n", + save_errno) ); + res = esock_make_error_errno(env, save_errno); } @@ -4916,7 +5078,7 @@ ERL_NIF_TERM nfinalize_connection(ErlNifEnv* env, { int error; - if (descP->state != SOCKET_STATE_CONNECTING) + if (!IS_CONNECTING(descP)) return esock_make_error(env, atom_enotconn); if (!verify_is_connected(descP, &error)) { @@ -4925,6 +5087,8 @@ ERL_NIF_TERM nfinalize_connection(ErlNifEnv* env, } descP->state = SOCKET_STATE_CONNECTED; + enif_set_pid_undefined(&descP->connPid); + DEMONP("nfinalize_connection -> connected", env, descP, &descP->connMon); descP->isReadable = TRUE; descP->isWritable = TRUE; @@ -4987,6 +5151,29 @@ BOOLEAN_T verify_is_connected(ESockDescriptor* descP, int* err) +/* *** is_connector *** + * Check if the current process is the connector process. + */ +#if !defined(__WIN32__) +static +BOOLEAN_T is_connector(ErlNifEnv* env, + ESockDescriptor* descP) +{ + ErlNifPid caller; + + if (enif_self(env, &caller) == NULL) + return FALSE; + + if (COMPARE_PIDS(&descP->connPid, &caller) == 0) + return TRUE; + else + return FALSE; + +} +#endif + + + /* ---------------------------------------------------------------------- * nif_listen * @@ -5253,6 +5440,7 @@ ERL_NIF_TERM naccept_listening_error(ErlNifEnv* env, enif_set_pid_undefined(&descP->currentAcceptor.pid); res = esock_make_error(env, atom_exmon); } else { + ESOCK_ASSERT(!descP->currentAcceptor.env); descP->currentAcceptor.env = esock_alloc_env("current acceptor"); descP->currentAcceptor.ref = CP_TERM(descP->currentAcceptor.env, accRef); @@ -5411,6 +5599,7 @@ ERL_NIF_TERM naccept_accepting_current_accept(ErlNifEnv* env, esock_free_env("naccept_accepting_current_accept - " "current-accept-env", descP->currentAcceptor.env); + descP->currentAcceptor.env = NULL; if (!activate_next_acceptor(env, descP, sockRef)) { @@ -5422,6 +5611,7 @@ ERL_NIF_TERM naccept_accepting_current_accept(ErlNifEnv* env, descP->state = SOCKET_STATE_LISTENING; descP->currentAcceptorP = NULL; + ESOCK_ASSERT(!descP->currentAcceptor.env); descP->currentAcceptor.env = NULL; MON_INIT(&descP->currentAcceptor.mon); } @@ -5448,6 +5638,7 @@ ERL_NIF_TERM naccept_accepting_current_error(ErlNifEnv* env, ESockRequestor req; ERL_NIF_TERM res, reason; + req.env = NULL; if (save_errno == ERRNO_BLOCK) { /* @@ -5475,6 +5666,7 @@ ERL_NIF_TERM naccept_accepting_current_error(ErlNifEnv* env, req.pid) ); esock_send_abort_msg(env, sockRef, req.ref, req.env, reason, &req.pid); + req.env = NULL; DEMONP("naccept_accepting_current_error -> pop'ed writer", env, descP, &req.mon); } @@ -8860,35 +9052,67 @@ ERL_NIF_TERM nsetopt_lvl_ip_update_membership(ErlNifEnv* env, #endif // It must be a map - if (!IS_MAP(env, eVal)) + if (!IS_MAP(env, eVal)) { + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_ip_update_membership -> " + "value *not* a map\r\n") ); return enif_make_badarg(env); + } // It must have atleast two attributes - if (!enif_get_map_size(env, eVal, &sz) || (sz >= 2)) + if (!enif_get_map_size(env, eVal, &sz) || (sz < 2)) { + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_ip_update_membership -> " + "invalid map value: %T\r\n", eVal) ); return enif_make_badarg(env); + } - if (!GET_MAP_VAL(env, eVal, atom_multiaddr, &eMultiAddr)) + if (!GET_MAP_VAL(env, eVal, atom_multiaddr, &eMultiAddr)) { + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_ip_update_membership -> " + "failed get multiaddr (map) attribute\r\n") ); return enif_make_badarg(env); + } - if (!GET_MAP_VAL(env, eVal, atom_interface, &eInterface)) + if (!GET_MAP_VAL(env, eVal, atom_interface, &eInterface)) { + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_ip_update_membership -> " + "failed get interface (map) attribute\r\n") ); return enif_make_badarg(env); + } if ((xres = esock_decode_ip4_address(env, eMultiAddr, - &mreq.imr_multiaddr)) != NULL) + &mreq.imr_multiaddr)) != NULL) { + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_ip_update_membership -> " + "failed decode multiaddr %T: %s\r\n", eMultiAddr, xres) ); return esock_make_error_str(env, xres); + } if ((xres = esock_decode_ip4_address(env, eInterface, - &mreq.imr_interface)) != NULL) + &mreq.imr_interface)) != NULL) { + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_ip_update_membership -> " + "failed decode interface %T: %s\r\n", eInterface, xres) ); return esock_make_error_str(env, xres); + } res = socket_setopt(descP->sock, level, opt, &mreq, sizeof(mreq)); - if (res != 0) - result = esock_make_error_errno(env, sock_errno()); - else + if (res != 0) { + int save_errno = sock_errno(); + + result = esock_make_error_errno(env, save_errno); + + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_ip_update_membership -> " + "failed setopt: %T (%d)\r\n", result, save_errno) ); + + } else { result = esock_atom_ok; + } return result; } @@ -9492,33 +9716,65 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_update_membership(ErlNifEnv* env, #endif // It must be a map - if (!IS_MAP(env, eVal)) + if (!IS_MAP(env, eVal)) { + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_ipv6_update_membership -> " + "value *not* a map\r\n") ); return enif_make_badarg(env); + } // It must have atleast two attributes - if (!enif_get_map_size(env, eVal, &sz) || (sz >= 2)) + if (!enif_get_map_size(env, eVal, &sz) || (sz < 2)) { + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_ipv6_update_membership -> " + "invalid map value: %T\r\n", eVal) ); return enif_make_badarg(env); + } - if (!GET_MAP_VAL(env, eVal, atom_multiaddr, &eMultiAddr)) + if (!GET_MAP_VAL(env, eVal, atom_multiaddr, &eMultiAddr)) { + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_ipv6_update_membership -> " + "failed get multiaddr (map) attribute\r\n") ); return enif_make_badarg(env); + } - if (!GET_MAP_VAL(env, eVal, atom_interface, &eInterface)) + if (!GET_MAP_VAL(env, eVal, atom_interface, &eInterface)) { + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_ipv6_update_membership -> " + "failed get interface (map) attribute\r\n") ); return enif_make_badarg(env); + } if ((xres = esock_decode_ip6_address(env, eMultiAddr, - &mreq.ipv6mr_multiaddr)) != NULL) + &mreq.ipv6mr_multiaddr)) != NULL) { + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_ipv6_update_membership -> " + "failed decode multiaddr %T: %s\r\n", eMultiAddr, xres) ); return esock_make_error_str(env, xres); + } - if (!GET_UINT(env, eInterface, &mreq.ipv6mr_interface)) + if (!GET_UINT(env, eInterface, &mreq.ipv6mr_interface)) { + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_ip_update_membership -> " + "failed decode interface %T: %s\r\n", eInterface, xres) ); return esock_make_error(env, esock_atom_einval); + } res = socket_setopt(descP->sock, level, opt, &mreq, sizeof(mreq)); - if (res != 0) - result = esock_make_error_errno(env, sock_errno()); - else + if (res != 0) { + int save_errno = sock_errno(); + + result = esock_make_error_errno(env, save_errno); + + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_ipv6_update_membership -> " + "failed setopt: %T (%d)\r\n", result, save_errno) ); + + } else { result = esock_atom_ok; + } return result; } @@ -10890,7 +11146,15 @@ ERL_NIF_TERM ngetopt_otp_protocol(ErlNifEnv* env, switch (val) { case IPPROTO_IP: +#if defined(AF_LOCAL) + if (descP->domain == AF_LOCAL) { + result = esock_make_ok2(env, esock_atom_default); + } else { + result = esock_make_ok2(env, esock_atom_ip); + } +#else result = esock_make_ok2(env, esock_atom_ip); +#endif break; case IPPROTO_TCP: @@ -11462,7 +11726,14 @@ ERL_NIF_TERM ngetopt_lvl_sock_protocol(ErlNifEnv* env, } else { switch (val) { case IPPROTO_IP: +#if defined(AF_LOCAL) + if (descP->domain == AF_LOCAL) + result = esock_make_ok2(env, esock_atom_default); + else + result = esock_make_ok2(env, esock_atom_ip); +#else result = esock_make_ok2(env, esock_atom_ip); +#endif break; case IPPROTO_TCP: @@ -14037,6 +14308,7 @@ ERL_NIF_TERM send_check_ok(ErlNifEnv* env, DEMONP("send_check_ok -> current writer", env, descP, &descP->currentWriter.mon); esock_free_env("send_check_ok", descP->currentWriter.env); + descP->currentWriter.env = NULL; } SSDBG( descP, @@ -14049,6 +14321,7 @@ ERL_NIF_TERM send_check_ok(ErlNifEnv* env, if (!activate_next_writer(env, descP, sockRef)) { descP->currentWriterP = NULL; + ESOCK_ASSERT(!descP->currentWriter.env); descP->currentWriter.env = NULL; descP->currentWriter.ref = esock_atom_undefined; enif_set_pid_undefined(&descP->currentWriter.pid); @@ -14074,6 +14347,7 @@ ERL_NIF_TERM send_check_fail(ErlNifEnv* env, ESockRequestor req; ERL_NIF_TERM reason; + req.env = NULL; cnt_inc(&descP->writeFails, 1); SSDBG( descP, ("SOCKET", "send_check_fail -> error: %d\r\n", saveErrno) ); @@ -14090,6 +14364,7 @@ ERL_NIF_TERM send_check_fail(ErlNifEnv* env, ("SOCKET", "send_check_fail -> abort %T\r\n", req.pid) ); esock_send_abort_msg(env, sockRef, req.ref, req.env, reason, &req.pid); + req.env = NULL; DEMONP("send_check_fail -> pop'ed writer", env, descP, &req.mon); } } @@ -14131,6 +14406,7 @@ ERL_NIF_TERM send_check_retry(ErlNifEnv* env, enif_set_pid_undefined(&descP->currentWriter.pid); return esock_make_error(env, atom_exmon); } else { + ESOCK_ASSERT(!descP->currentWriter.env); descP->currentWriter.env = esock_alloc_env("current-writer"); descP->currentWriter.ref = CP_TERM(descP->currentWriter.env, sendRef); descP->currentWriterP = &descP->currentWriter; @@ -14260,7 +14536,7 @@ char* recv_init_current_reader(ErlNifEnv* env, enif_set_pid_undefined(&descP->currentReader.pid); return str_exmon; } else { - + ESOCK_ASSERT(!descP->currentReader.env); descP->currentReader.env = esock_alloc_env("current-reader"); descP->currentReader.ref = CP_TERM(descP->currentReader.env, recvRef); @@ -14341,6 +14617,7 @@ void recv_error_current_reader(ErlNifEnv* env, { ESockRequestor req; + req.env = NULL; if (descP->currentReaderP != NULL) { DEMONP("recv_error_current_reader -> current reader", @@ -14352,6 +14629,7 @@ void recv_error_current_reader(ErlNifEnv* env, req.pid) ); esock_send_abort_msg(env, sockRef, req.ref, req.env, reason, &req.pid); + req.env = NULL; DEMONP("recv_error_current_reader -> pop'ed reader", env, descP, &req.mon); } @@ -15123,8 +15401,8 @@ char* encode_msghdr(ErlNifEnv* env, "\r\n read: %d" "\r\n", read) ); - /* The address is not used if we are connected, - * so check (length = 0) before we try to encodel + /* The address is not used if we are connected (unless, maybe, + * family is 'local'), so check (length = 0) before we try to encodel */ if (msgHdrP->msg_namelen != 0) { if ((xres = esock_encode_sockaddr(env, @@ -16246,8 +16524,6 @@ char* encode_cmsghdr_data_ipv6(ErlNifEnv* env, size_t dataLen, ERL_NIF_TERM* eCMsgHdrData) { - char* xres; - switch (type) { #if defined(IPV6_PKTINFO) case IPV6_PKTINFO: @@ -16255,6 +16531,7 @@ char* encode_cmsghdr_data_ipv6(ErlNifEnv* env, struct in6_pktinfo* pktInfoP = (struct in6_pktinfo*) dataP; ERL_NIF_TERM ifIndex = MKI(env, pktInfoP->ipi6_ifindex); ERL_NIF_TERM addr; + char* xres; if ((xres = esock_encode_ip6_address(env, &pktInfoP->ipi6_addr, @@ -16764,8 +17041,10 @@ ESockDescriptor* alloc_descriptor(SOCKET sock, HANDLE event) if ((descP = enif_alloc_resource(sockets, sizeof(ESockDescriptor))) != NULL) { char buf[64]; /* Buffer used for building the mutex name */ - // This needs to be released when the socket is closed! - // descP->env = enif_alloc_env(); + descP->pattern = ESOCK_DESC_PATTERN_CREATED; + + enif_set_pid_undefined(&descP->connPid); + MON_INIT(&descP->connMon); sprintf(buf, "esock[w,%d]", sock); descP->writeMtx = MCREATE(buf); @@ -17027,6 +17306,10 @@ BOOLEAN_T eproto2proto(ErlNifEnv* env, } switch (ep) { + case SOCKET_PROTOCOL_DEFAULT: + *proto = 0; // default - note that _IP also has the value 0... + break; + case SOCKET_PROTOCOL_IP: *proto = IPPROTO_IP; break; @@ -17647,6 +17930,7 @@ int esock_select_cancel(ErlNifEnv* env, esock_send_abort_msg(env, sockRef, \ reqP->ref, reqP->env, \ reason, &reqP->pid); \ + reqP->env = NULL; \ \ } else { \ \ @@ -17745,7 +18029,7 @@ REQ_SEARCH4PID_FUNCS reqP->pid = pid; \ if (MONP("reader_push -> " #F " request", \ env, descP, &pid, &reqP->mon) != 0) { \ - FREE(reqP); \ + FREE(e); \ return esock_make_error(env, atom_exmon); \ } \ reqP->env = esock_alloc_env(#F "_push"); \ @@ -17822,6 +18106,9 @@ BOOLEAN_T requestor_pop(ESockRequestQueue* q, { ESockRequestQueueElement* e = qpop(q); + if (reqP->env) + esock_free_env("requestor_pop", reqP->env); + if (e != NULL) { reqP->pid = e->data.pid; reqP->mon = e->data.mon; @@ -17933,6 +18220,8 @@ BOOLEAN_T qunqueue(ErlNifEnv* env, } } + if (e->data.env) + esock_free_env("qunqueue", e->data.env); FREE(e); return TRUE; @@ -18074,6 +18363,18 @@ ERL_NIF_TERM esock_make_monitor_term(ErlNifEnv* env, const ESockMonitor* monP) * ---------------------------------------------------------------------- */ + +static void free_request_queue(ESockRequestQueue* q) +{ + while (q->first) { + ESockRequestQueueElement* free_me = q->first; + q->first = free_me->nextP; + if (free_me->data.env) + esock_free_env("dtor", free_me->data.env); + FREE(free_me); + } +} + /* ========================================================================= * socket_dtor - Callback function for resource destructor * @@ -18084,11 +18385,30 @@ void socket_dtor(ErlNifEnv* env, void* obj) #if !defined(__WIN32__) ESockDescriptor* descP = (ESockDescriptor*) obj; - MDESTROY(descP->writeMtx); - MDESTROY(descP->readMtx); - MDESTROY(descP->accMtx); - MDESTROY(descP->closeMtx); - MDESTROY(descP->cfgMtx); + MDESTROY(descP->writeMtx); descP->writeMtx = NULL; + MDESTROY(descP->readMtx); descP->readMtx = NULL; + MDESTROY(descP->accMtx); descP->accMtx = NULL; + MDESTROY(descP->closeMtx); descP->closeMtx = NULL; + MDESTROY(descP->cfgMtx); descP->cfgMtx = NULL; + + if (descP->currentReader.env) { + esock_free_env("dtor reader", descP->currentReader.env); + descP->currentReader.env = NULL; + } + if (descP->currentWriter.env) { + esock_free_env("dtor writer", descP->currentWriter.env); + descP->currentWriter.env = NULL; + } + if (descP->currentAcceptor.env) { + esock_free_env("dtor acceptor", descP->currentAcceptor.env); + descP->currentAcceptor.env = NULL; + } + free_request_queue(&descP->readersQ); + free_request_queue(&descP->writersQ); + free_request_queue(&descP->acceptorsQ); + + descP->state = SOCKET_STATE_DTOR; + descP->pattern = ESOCK_DESC_PATTERN_DTOR; #endif } @@ -18321,6 +18641,7 @@ void socket_stop_handle_current(ErlNifEnv* env, "current %s %T\r\n", reqP->ref, role, reqP->pid); } + reqP->env = NULL; } } @@ -18376,6 +18697,7 @@ void inform_waiting_procs(ErlNifEnv* env, currentP->data.pid); } + currentP->data.env = NULL, DEMONP("inform_waiting_procs -> current 'request'", env, descP, ¤tP->data.mon); @@ -18510,6 +18832,17 @@ void socket_down(ErlNifEnv* env, MON2T(env, mon)); } + } else if (COMPARE_PIDS(&descP->connPid, pid) == 0) { + + /* The connPid is only set during the connection. + * The same goes for the monitor (connMon). + */ + + descP->state = SOCKET_STATE_OPEN; /* restore state */ + enif_set_pid_undefined(&descP->connPid); + DEMONP("socket_down -> connector", + env, descP, &descP->connMon); + } else { /* check all operation queue(s): acceptor, writer and reader. diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c index 8ad95cb6b7..2740cb51ef 100644 --- a/erts/emulator/nifs/common/socket_util.c +++ b/erts/emulator/nifs/common/socket_util.c @@ -35,6 +35,10 @@ #include "socket_util.h" #include "socket_dbg.h" +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + /* We don't have a "debug flag" to check here, so we * should use the compile debug flag, whatever that is... */ @@ -51,12 +55,10 @@ extern char* erl_errno_id(int error); /* THIS IS JUST TEMPORARY??? */ -#if defined(CLOCK_REALTIME) -static int realtime(struct timespec* tsP); -static int timespec2str(char *buf, - unsigned int len, - struct timespec *ts); +#if (defined(HAVE_LOCALTIME_R) && defined(HAVE_STRFTIME)) +#define ESOCK_USE_PRETTY_TIMESTAMP 1 #endif + static char* make_sockaddr_in4(ErlNifEnv* env, ERL_NIF_TERM port, @@ -984,9 +986,27 @@ char* esock_decode_timeval(ErlNifEnv* env, if (!GET_LONG(env, eSec, &timeP->tv_sec)) return ESOCK_STR_EINVAL; +#if (SIZEOF_INT == 4) + { + int usec; + if (!GET_INT(env, eUSec, &usec)) + return ESOCK_STR_EINVAL; + timeP->tv_usec = (typeof(timeP->tv_usec)) usec; + } +#elif (SIZEOF_LONG == 4) + { + long usec; + if (!GET_LONG(env, eUSec, &usec)) + return ESOCK_STR_EINVAL; + timeP->tv_usec = (typeof(timeP->tv_usec)) usec; + } +#else + /* Ok, we give up... */ if (!GET_LONG(env, eUSec, &timeP->tv_usec)) return ESOCK_STR_EINVAL; +#endif + return NULL; } @@ -1510,10 +1530,7 @@ void esock_warning_msg( const char* format, ... ) { va_list args; char f[512 + sizeof(format)]; // This has to suffice... -#if defined(CLOCK_REALTIME) char stamp[64]; // Just in case... - struct timespec ts; -#endif int res; /* @@ -1525,18 +1542,13 @@ void esock_warning_msg( const char* format, ... ) // 2018-06-29 12:13:21.232089 // 29-Jun-2018::13:47:25.097097 -#if defined(CLOCK_REALTIME) - if (!realtime(&ts) && - (timespec2str(stamp, sizeof(stamp), &ts) == 0)) { + if (esock_timestamp(stamp, sizeof(stamp))) { res = enif_snprintf(f, sizeof(f), "=WARNING MSG==== %s ===\r\n%s", stamp, format); } else { res = enif_snprintf(f, sizeof(f), "=WARNING MSG==== %s", format); } -#else - res = enif_snprintf(f, sizeof(f), "=WARNING MSG==== %s", format); -#endif if (res > 0) { va_start (args, format); @@ -1549,43 +1561,52 @@ void esock_warning_msg( const char* format, ... ) } -#if defined(CLOCK_REALTIME) -static -int realtime(struct timespec* tsP) -{ - return clock_gettime(CLOCK_REALTIME, tsP); -} - - -/* - * Convert a timespec struct into a readable/printable string. +/* *** esock_timestamp *** * - * "%F::%T" => 2018-06-29 12:13:21[.232089] - * "%d-%b-%Y::%T" => 29-Jun-2018::13:47:25.097097 + * Create a timestamp string. + * If awailable, we use the localtime_r and strftime function(s) + * to produces a nice readable timestamp. But if not (awailable), + * it produces a timestamp in the form of an "Epoch" (A real epoch + * is the number of seconds since 1/1 1970, but our timestamp is + * the number micro seconds since 1/1 1970). */ -static -int timespec2str(char *buf, unsigned int len, struct timespec *ts) -{ - int ret, buflen; - struct tm t; - tzset(); - if (localtime_r(&(ts->tv_sec), &t) == NULL) - return 1; +extern +BOOLEAN_T esock_timestamp(char *buf, unsigned int len) +{ + int ret; + ErlNifTime monTime = enif_monotonic_time(ERL_NIF_USEC); + ErlNifTime offTime = enif_time_offset(ERL_NIF_USEC); + ErlNifTime time = monTime + offTime; +#if defined(ESOCK_USE_PRETTY_TIMESTAMP) + time_t sec = time / 1000000; // (if _MSEC) sec = time / 1000; + time_t usec = time % 1000000; // (if _MSEC) msec = time % 1000; + int buflen; + struct tm t; + + if (localtime_r(&sec, &t) == NULL) + return FALSE; - ret = strftime(buf, len, "%d-%B-%Y::%T", &t); - if (ret == 0) - return 2; - len -= ret - 1; - buflen = strlen(buf); + ret = strftime(buf, len, "%d-%B-%Y::%T", &t); + if (ret == 0) + return FALSE; + len -= ret - 1; + buflen = strlen(buf); - ret = snprintf(&buf[buflen], len, ".%06ld", ts->tv_nsec/1000); - if (ret >= len) - return 3; + ret = enif_snprintf(&buf[buflen], len, ".%06b64d", usec); + if (ret >= len) + return FALSE; - return 0; -} + return TRUE; +#else + ret = enif_snprintf(buf, len, "%b64d", time); + if (ret == 0) + return FALSE; + else + return TRUE; #endif +} + /* =================================================================== * @@ -1653,7 +1674,7 @@ char* make_sockaddr_un(ErlNifEnv* env, ERL_NIF_TERM* sa) { ERL_NIF_TERM keys[] = {esock_atom_family, esock_atom_path}; - ERL_NIF_TERM vals[] = {esock_atom_inet, path}; + ERL_NIF_TERM vals[] = {esock_atom_local, path}; unsigned int numKeys = sizeof(keys) / sizeof(ERL_NIF_TERM); unsigned int numVals = sizeof(vals) / sizeof(ERL_NIF_TERM); diff --git a/erts/emulator/nifs/common/socket_util.h b/erts/emulator/nifs/common/socket_util.h index 84b1c8085f..2688a920c4 100644 --- a/erts/emulator/nifs/common/socket_util.h +++ b/erts/emulator/nifs/common/socket_util.h @@ -199,6 +199,9 @@ extern ERL_NIF_TERM esock_make_error_errno(ErlNifEnv* env, int err); extern +BOOLEAN_T esock_timestamp(char *buf, unsigned int len); + +extern void esock_warning_msg(const char* format, ... ); diff --git a/erts/emulator/pcre/LICENCE b/erts/emulator/pcre/LICENCE index f6ef7fd766..760a6666b6 100644 --- a/erts/emulator/pcre/LICENCE +++ b/erts/emulator/pcre/LICENCE @@ -25,7 +25,7 @@ Email domain: cam.ac.uk University of Cambridge Computing Service, Cambridge, England. -Copyright (c) 1997-2018 University of Cambridge +Copyright (c) 1997-2019 University of Cambridge All rights reserved. @@ -34,9 +34,9 @@ PCRE JUST-IN-TIME COMPILATION SUPPORT Written by: Zoltan Herczeg Email local part: hzmester -Emain domain: freemail.hu +Email domain: freemail.hu -Copyright(c) 2010-2018 Zoltan Herczeg +Copyright(c) 2010-2019 Zoltan Herczeg All rights reserved. @@ -45,9 +45,9 @@ STACK-LESS JUST-IN-TIME COMPILER Written by: Zoltan Herczeg Email local part: hzmester -Emain domain: freemail.hu +Email domain: freemail.hu -Copyright(c) 2009-2018 Zoltan Herczeg +Copyright(c) 2009-2019 Zoltan Herczeg All rights reserved. diff --git a/erts/emulator/pcre/pcre-8.42.tar.bz2 b/erts/emulator/pcre/pcre-8.42.tar.bz2 Binary files differdeleted file mode 100644 index 61bfa38970..0000000000 --- a/erts/emulator/pcre/pcre-8.42.tar.bz2 +++ /dev/null diff --git a/erts/emulator/pcre/pcre-8.43.tar.bz2 b/erts/emulator/pcre/pcre-8.43.tar.bz2 Binary files differnew file mode 100644 index 0000000000..e20c601f71 --- /dev/null +++ b/erts/emulator/pcre/pcre-8.43.tar.bz2 diff --git a/erts/emulator/pcre/pcre.h b/erts/emulator/pcre/pcre.h index 3563791223..49c9fc6dc8 100644 --- a/erts/emulator/pcre/pcre.h +++ b/erts/emulator/pcre/pcre.h @@ -43,9 +43,9 @@ POSSIBILITY OF SUCH DAMAGE. /* The current PCRE version information. */ #define PCRE_MAJOR 8 -#define PCRE_MINOR 42 +#define PCRE_MINOR 43 #define PCRE_PRERELEASE -#define PCRE_DATE 2018-03-20 +#define PCRE_DATE 2019-02-23 /* When an application links to a PCRE DLL in Windows, the symbols that are imported have to be identified as such. When building PCRE, the appropriate @@ -240,6 +240,9 @@ with J. */ #define PCRE_UTF8_ERR20 20 #define PCRE_UTF8_ERR21 21 #define PCRE_UTF8_ERR22 22 /* Unused (was non-character) */ +#if defined(ERLANG_INTEGRATION) +#define PCRE_UTF8_YIELD 23 +#endif /* Specific error codes for UTF-16 validity checks */ diff --git a/erts/emulator/pcre/pcre_compile.c b/erts/emulator/pcre/pcre_compile.c index ae7f6e2a2a..6ac222b27e 100644 --- a/erts/emulator/pcre/pcre_compile.c +++ b/erts/emulator/pcre/pcre_compile.c @@ -6,7 +6,7 @@ and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel - Copyright (c) 1997-2016 University of Cambridge + Copyright (c) 1997-2018 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -3300,7 +3300,7 @@ for(;;) if ((*xclass_flags & XCL_MAP) == 0) { /* No bits are set for characters < 256. */ - if (list[1] == 0) return TRUE; + if (list[1] == 0) return (*xclass_flags & XCL_NOT) == 0; /* Might be an empty repeat. */ continue; } @@ -7643,6 +7643,8 @@ for (;; ptr++) /* Can't determine a first byte now */ if (firstcharflags == REQ_UNSET) firstcharflags = REQ_NONE; + zerofirstchar = firstchar; + zerofirstcharflags = firstcharflags; continue; @@ -8683,10 +8685,18 @@ do { if (!is_anchored(scode, new_map, cd, atomcount)) return FALSE; } - /* Positive forward assertions and conditions */ + /* Positive forward assertion */ - else if (op == OP_ASSERT || op == OP_COND) + else if (op == OP_ASSERT) + { + if (!is_anchored(scode, bracket_map, cd, atomcount)) return FALSE; + } + + /* Condition; not anchored if no second branch */ + + else if (op == OP_COND) { + if (scode[GET(scode,1)] != OP_ALT) return FALSE; if (!is_anchored(scode, bracket_map, cd, atomcount)) return FALSE; } diff --git a/erts/emulator/pcre/pcre_exec.c b/erts/emulator/pcre/pcre_exec.c index 1946e97a72..55a7b377bf 100644 --- a/erts/emulator/pcre/pcre_exec.c +++ b/erts/emulator/pcre/pcre_exec.c @@ -6642,10 +6642,16 @@ typedef struct { REAL_PCRE *Xre; heapframe Xframe_zero; /* Always NO_RECURSE */ + /* for yield in valid_utf() */ + + struct PRIV(valid_utf_ystate) valid_utf_ystate; + /* Original function parameters that need be saved */ int Xstart_offset; int Xoffsetcount; int *Xoffsets; + int Xlength; + PCRE_SPTR Xsubject; } PcreExecContext; #endif @@ -6675,6 +6681,7 @@ pcre32_exec(const pcre32 *argument_re, const pcre32_extra *extra_data, #endif { #ifndef ERLANG_INTEGRATION +#define ERTS_UPDATE_CONSUMED(X, MD) int rc, ocount, arg_offset_max; int newline; BOOL using_temporary_offsets = FALSE; @@ -6736,6 +6743,8 @@ heapframe frame_zero; start_offset = exec_context->Xstart_offset; \ offsetcount = exec_context->Xoffsetcount; \ offsets = exec_context->Xoffsets; \ + length = exec_context->Xlength; \ + subject = exec_context->Xsubject; \ } while (0) #define SWAPOUT() do { \ @@ -6750,8 +6759,30 @@ heapframe frame_zero; exec_context->Xstart_offset = start_offset; \ exec_context->Xoffsetcount = offsetcount; \ exec_context->Xoffsets = offsets; \ + exec_context->Xlength = length; \ + exec_context->Xsubject = subject; \ } while (0) +#define ERTS_UPDATE_CONSUMED(X, MD) \ +do { \ + if (((X)->flags & PCRE_EXTRA_LOOP_LIMIT) != 0) { \ + unsigned long consumed__; \ + if (!(X)->restart_data) { \ + consumed__ = 0; \ + } \ + else { \ + PcreExecContext *ctx__ = (PcreExecContext *) \ + (*(X)->restart_data); \ + consumed__ = ctx__->valid_utf_ystate.cnt; \ + ctx__->valid_utf_ystate.cnt = 0; \ + } \ + if ((MD)) { \ + match_data *md__ = (MD); \ + consumed__ += (X)->loop_limit - md__->loop_limit; \ + } \ + *((X)->loop_counter_return) = consumed__; \ + } \ +} while (0) PcreExecContext *exec_context; PcreExecContext internal_context; @@ -6776,15 +6807,21 @@ pcre_uchar req_char; /* we are restarting, every initialization is skipped and we jump directly into the loop */ exec_context = (PcreExecContext *) *(extra_data->restart_data); SWAPIN(); - + if (exec_context->valid_utf_ystate.yielded) + goto restart_valid_utf; goto RESTART_INTERRUPTED; } else { if (extra_data != NULL && (extra_data->flags & PCRE_EXTRA_LOOP_LIMIT)) { exec_context = (PcreExecContext *) (erts_pcre_malloc)(sizeof(PcreExecContext)); - *(extra_data->restart_data) = (void *) exec_context; + *(extra_data->restart_data) = (void *) exec_context; + exec_context->valid_utf_ystate.yielded = 0; /* need freeing by special routine from client */ } else { +#if defined(ERLANG_INTEGRATION) + fprintf(stderr, "Unexpected execution path\n"); + abort(); +#endif exec_context = &internal_context; } @@ -6865,9 +6902,38 @@ code for an invalid string if a results vector is available. */ if (utf && (options & PCRE_NO_UTF8_CHECK) == 0) { int erroroffset; - int errorcode = PRIV(valid_utf)((PCRE_PUCHAR)subject, length, &erroroffset); + int errorcode; + +#if !defined(ERLANG_INTEGRATION) + errorcode = PRIV(valid_utf)((PCRE_PUCHAR)subject, length); +#else + struct PRIV(valid_utf_ystate) *ystate; + + if (!extra_data || !extra_data->restart_data) { + ystate = NULL; + } + else if (!(extra_data->flags & PCRE_EXTRA_LOOP_LIMIT)) { + exec_context->valid_utf_ystate.cnt = 10; + ystate = NULL; + } + else { + exec_context->valid_utf_ystate.yielded = 0; + restart_valid_utf: + ystate = &exec_context->valid_utf_ystate; + ystate->cnt = (int) extra_data->loop_limit; + } + errorcode = PRIV(yielding_valid_utf)((PCRE_PUCHAR)subject, length, + &erroroffset, ystate); +#endif if (errorcode != 0) { +#if defined(ERLANG_INTEGRATION) + if (ystate && ystate->yielded) { + ERTS_UPDATE_CONSUMED(extra_data, NULL); + SWAPOUT(); + return PCRE_ERROR_LOOP_LIMIT; + } +#endif if (offsetcount >= 2) { offsets[0] = erroroffset; @@ -6890,6 +6956,11 @@ if (utf && (options & PCRE_NO_UTF8_CHECK) == 0) return PCRE_ERROR_BADUTF8_OFFSET; #endif } +#if defined(ERLANG_INTEGRATION) +else { + exec_context->valid_utf_ystate.cnt = 0; +} +#endif #endif /* If the pattern was successfully studied with JIT support, run the JIT @@ -6950,7 +7021,11 @@ if (extra_data != NULL) #ifdef ERLANG_INTEGRATION if ((flags & PCRE_EXTRA_LOOP_LIMIT) != 0) { - md->loop_limit = extra_data->loop_limit; + md->loop_limit = extra_data->loop_limit; + if (extra_data->restart_data) + md->loop_limit -= extra_data->loop_limit - exec_context->valid_utf_ystate.cnt; + if (md->loop_limit < 10) + md->loop_limit = 10; /* At least do something if we've come this far... */ } #endif } @@ -7266,14 +7341,8 @@ for(;;) #endif if ((start_bits[c/8] & (1 << (c&7))) != 0) { -#ifdef ERLANG_INTEGRATION - if ((extra_data->flags & PCRE_EXTRA_LOOP_LIMIT) != 0) - { - *extra_data->loop_counter_return = - (extra_data->loop_limit - md->loop_limit); - } -#endif - break; + ERTS_UPDATE_CONSUMED(extra_data, md); + break; } start_match++; } @@ -7298,13 +7367,7 @@ for(;;) (pcre_uint32)(end_subject - start_match) < study->minlength) { rc = MATCH_NOMATCH; -#ifdef ERLANG_INTEGRATION - if ((extra_data->flags & PCRE_EXTRA_LOOP_LIMIT) != 0) - { - *extra_data->loop_counter_return = - (extra_data->loop_limit - md->loop_limit); - } -#endif + ERTS_UPDATE_CONSUMED(extra_data, md); break; } @@ -7353,13 +7416,7 @@ for(;;) if (p >= end_subject) { rc = MATCH_NOMATCH; -#ifdef ERLANG_INTEGRATION - if ((extra_data->flags & PCRE_EXTRA_LOOP_LIMIT) != 0) - { - *extra_data->loop_counter_return = - (extra_data->loop_limit - md->loop_limit); - } -#endif + ERTS_UPDATE_CONSUMED(extra_data, md); break; } @@ -7390,11 +7447,7 @@ for(;;) EDEBUGF(("Calling match...")); rc = match(start_match, md->start_code, start_match, 2, md, NULL, 0); #ifdef ERLANG_INTEGRATION - if ((extra_data->flags & PCRE_EXTRA_LOOP_LIMIT) != 0) - { - *extra_data->loop_counter_return = - (extra_data->loop_limit - md->loop_limit); - } + ERTS_UPDATE_CONSUMED(extra_data, md); SWAPOUT(); while(rc == PCRE_ERROR_LOOP_LIMIT) { EDEBUGF(("Loop limit break detected")); diff --git a/erts/emulator/pcre/pcre_internal.h b/erts/emulator/pcre/pcre_internal.h index c84dcb5a38..71f473e86f 100644 --- a/erts/emulator/pcre/pcre_internal.h +++ b/erts/emulator/pcre/pcre_internal.h @@ -2756,6 +2756,17 @@ extern int PRIV(strcmp_uc_c8_utf)(const pcre_uchar *, #endif /* COMPILE_PCRE[8|16|32] */ +#if defined(ERLANG_INTEGRATION) +struct PRIV(valid_utf_ystate) { + unsigned int cnt; + int length; + int yielded; + PCRE_PUCHAR p; +}; +extern int PRIV(yielding_valid_utf)(PCRE_PUCHAR, int, int *, + struct PRIV(valid_utf_ystate) *); +#endif + extern const pcre_uchar *PRIV(find_bracket)(const pcre_uchar *, BOOL, int); extern BOOL PRIV(is_newline)(PCRE_PUCHAR, int, PCRE_PUCHAR, int *, BOOL); diff --git a/erts/emulator/pcre/pcre_jit_compile.c b/erts/emulator/pcre/pcre_jit_compile.c index 926e40f6d3..2d2288f81e 100644 --- a/erts/emulator/pcre/pcre_jit_compile.c +++ b/erts/emulator/pcre/pcre_jit_compile.c @@ -9002,7 +9002,7 @@ if (exact > 1) #ifdef SUPPORT_UTF && !common->utf #endif - ) + && type != OP_ANYNL && type != OP_EXTUNI) { OP2(SLJIT_ADD, TMP1, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(exact)); add_jump(compiler, &backtrack->topbacktracks, CMP(SLJIT_GREATER, TMP1, 0, STR_END, 0)); diff --git a/erts/emulator/pcre/pcre_valid_utf8.c b/erts/emulator/pcre/pcre_valid_utf8.c index 516d8f4725..1dc1f9ba0c 100644 --- a/erts/emulator/pcre/pcre_valid_utf8.c +++ b/erts/emulator/pcre/pcre_valid_utf8.c @@ -107,19 +107,80 @@ Returns: = 0 if the string is a valid UTF-8 string int PRIV(valid_utf)(PCRE_PUCHAR string, int length, int *erroroffset) { + +#if defined(ERLANG_INTEGRATION) + return PRIV(yielding_valid_utf)(string, length, erroroffset, NULL); +} + +int +PRIV(yielding_valid_utf)(PCRE_PUCHAR string, int length, int *erroroffset, struct PRIV(valid_utf_ystate) *ystate) +{ +#endif + #ifdef SUPPORT_UTF register PCRE_PUCHAR p; +#if defined(ERLANG_INTEGRATION) +register long cnt; + +if (!ystate) { + cnt = -1; +} +else { + cnt = ystate->cnt; + if (ystate->yielded) { + p = ystate->p; + length = ystate->length; + if (length < 0) + goto restart_length; + else + goto restart_validate; + } +} +#endif + if (length < 0) { - for (p = string; *p != 0; p++); - length = (int)(p - string); + for (p = string; *p != 0; p++) { +#if defined(ERLANG_INTEGRATION) + if (cnt > 0 && --cnt == 0) { + /* + * Return with cnt set to amount consumed; + * i.e. same amount as at start... + */ + ystate->yielded = !0; + ystate->length = length; + ystate->p = p; + return PCRE_UTF8_YIELD; + } + restart_length: + (void) !0; +#endif + } + length = (int)(p - string); } for (p = string; length-- > 0; p++) { register pcre_uchar ab, c, d; +#if defined(ERLANG_INTEGRATION) + + if (cnt > 0 && --cnt == 0) { + /* + * Return with cnt set to amount consumed; + * i.e. same amount as at start... + */ + ystate->yielded = !0; + ystate->length = length; + ystate->p = p; + return PCRE_UTF8_YIELD; + } + + restart_validate: + +#endif + c = *p; if (c < 128) continue; /* ASCII character */ @@ -290,6 +351,14 @@ for (p = string; length-- > 0; p++) } } +#if defined(ERLANG_INTEGRATION) +if (ystate) { + /* Return with cnt set to amount consumed... */ + ystate->cnt -= cnt; + ystate->yielded = 0; +} +#endif + #else /* Not SUPPORT_UTF */ (void)(string); /* Keep picky compilers happy */ (void)(length); diff --git a/erts/emulator/test/bif_SUITE.erl b/erts/emulator/test/bif_SUITE.erl index 43975d1800..c5abd04e07 100644 --- a/erts/emulator/test/bif_SUITE.erl +++ b/erts/emulator/test/bif_SUITE.erl @@ -612,6 +612,16 @@ binary_to_existing_atom(Config) when is_list(Config) -> UnlikelyAtom = binary_to_atom(id(UnlikelyBin), latin1), UnlikelyAtom = binary_to_existing_atom(UnlikelyBin, latin1), + + %% ERL-944; a binary that was too large would overflow the latin1-to-utf8 + %% conversion buffer. + OverflowAtom = <<0:511/unit:8, + 196, 133, 196, 133, 196, 133, 196, 133, 196, 133, + 196, 133, 196, 133, 196, 133, 196, 133, 196, 133, + 196, 133, 196, 133, 196, 133, 196, 133, 196, 133, + 196, 133, 196, 133, 196, 133, 196, 133, 196, 133>>, + {'EXIT', _} = (catch binary_to_existing_atom(OverflowAtom, latin1)), + ok. diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl index 7885d35d9d..9dcdd60060 100644 --- a/erts/emulator/test/distribution_SUITE.erl +++ b/erts/emulator/test/distribution_SUITE.erl @@ -1400,6 +1400,10 @@ get_conflicting_unicode_atoms(CIX, N) -> %% The message_latency_large tests that small distribution messages are %% not blocked by other large distribution messages. Basically it tests %% that fragmentation of distribution messages works. +%% +%% Because of large problems to get reliable values from these testcases +%% they no longer fail when the latency is incorrect. However, they are +%% kept as they continue to find bugs in the distribution implementation. message_latency_large_message(Config) when is_list(Config) -> measure_latency_large_message(?FUNCTION_NAME, fun(Dropper, Payload) -> Dropper ! Payload end). @@ -1484,7 +1488,11 @@ measure_latency_large_message(Nodename, DataFun) -> case {lists:max(Times), lists:min(Times)} of {Max, Min} when Max * 0.25 > Min, BuildType =:= opt -> - ct:fail({incorrect_latency, IndexTimes}); + %% We only issue a comment for this failure as the + %% testcases proved very difficult to run successfully + %% on many platforms. + ct:comment({incorrect_latency, IndexTimes}), + ok; _ -> ok end. @@ -1503,10 +1511,7 @@ measure_latency(DataFun, Dropper, Echo, Payload) -> end end) || _ <- lists:seq(1,2)], - [receive - {monitor, _Sender, busy_dist_port, _Info} -> - ok - end || _ <- lists:seq(1,10)], + wait_for_busy_dist(2 * 60 * 1000, 10), {TS, Times} = timer:tc(fun() -> @@ -1530,6 +1535,18 @@ measure_latency(DataFun, Dropper, Echo, Payload) -> end || {Sender, Ref} <- Senders], TS. +wait_for_busy_dist(_Tmo, 0) -> + ok; +wait_for_busy_dist(Tmo, N) -> + T0 = erlang:monotonic_time(millisecond), + receive + {monitor, _Sender, busy_dist_port, _Info} -> + wait_for_busy_dist(Tmo - (erlang:monotonic_time(millisecond) - T0), N - 1) + after Tmo -> + ct:log("Timed out waiting for busy_dist, ~p left",[N]), + timeout + end. + flush() -> receive _ -> @@ -2600,7 +2617,7 @@ verify_nc(Node) -> demonitor(MonRef,[flush]), ok; {Ref, Error} -> - ct:log("~p",[Error]), + ct:log("~s",[Error]), ct:fail(failed_nc_refc_check); {'DOWN', MonRef, _, _, _} = Down -> ct:log("~p",[Down]), diff --git a/erts/emulator/test/dump_SUITE.erl b/erts/emulator/test/dump_SUITE.erl index 9f8ac42fa9..b7da69e556 100644 --- a/erts/emulator/test/dump_SUITE.erl +++ b/erts/emulator/test/dump_SUITE.erl @@ -140,13 +140,13 @@ free_dump(Config) when is_list(Config) -> {ok, NodeA} = start_node(Config), {ok, NodeB} = start_node(Config), - Self = self(), PidA = spawn_link( NodeA, fun() -> Self ! ready, + Reason = lists:duplicate(1000000,100), receive ok -> spawn(fun() -> @@ -154,24 +154,29 @@ free_dump(Config) when is_list(Config) -> timer:sleep(5), receive M -> - io:format("~p",[M]), - erlang:halt("dump") - end + io:format("~p",[M]) +%% We may want to add this timeout here in-case no busy condition is triggered +%% after 60 * 1000 -> +%% io:format("Timeout") + end, + erlang:halt("dump") end), - exit(lists:duplicate(1000000,100)) + exit(Reason) end end), - spawn_link(NodeB, - fun() -> - [erlang:monitor(process, PidA) || _ <- lists:seq(1,10000)], - Self ! done, - receive _ -> ok end - end), + PidB = spawn_link(NodeB, + fun() -> + [erlang:monitor(process, PidA) || _ <- lists:seq(1,10000)], + Self ! done, + receive _ -> ok end + end), receive done -> ok end, true = rpc:call(NodeA, os, putenv, ["ERL_CRASH_DUMP",Dump]), - ct:pal("~p",[rpc:call(NodeA, distribution_SUITE, make_busy, [NodeB, 1000])]), + %% Make the node busy towards NodeB for 10 seconds. + BusyPid = rpc:call(NodeA, distribution_SUITE, make_busy, [NodeB,10000]), + ct:pal("~p",[BusyPid]), receive ready -> unlink(PidA), PidA ! ok end, @@ -185,6 +190,10 @@ free_dump(Config) when is_list(Config) -> file:delete(Dump), + unlink(PidB), + + rpc:call(NodeB, erlang, halt, [0]), + ok. diff --git a/erts/emulator/test/esock_ttest/esock-ttest b/erts/emulator/test/esock_ttest/esock-ttest index f0d363ab30..9adc51fc8b 100755 --- a/erts/emulator/test/esock_ttest/esock-ttest +++ b/erts/emulator/test/esock_ttest/esock-ttest @@ -50,43 +50,50 @@ usage() -> "~n units (server or client)." "~n" "~n options: " - "~n --help Display this info and exit. " - "~n --server [server-options] Start a server. " - "~n There are no mandatory server options." - "~n --client client-options Start a client" - "~n Some client options are mandatory and" - "~n others optional." - "~n --active <active> boolean() | once." - "~n Valid for both client and server." - "~n Defaults to: false" - "~n --transport <transport> Which transport to use: gen|sock[:plain|msg]" - "~n gen: gen_tcp" - "~n sock: socket" - "~n plain: recv/send (default)" - "~n msg: recvmsg/sendmsg" - "~n Defaults to: sock:plain" - "~n --scon <addr>:<port> Address and port of the server." - "~n The address part is in the standard form:" - "~n \"a.b.c.d\"." - "~n Only valid for client." - "~n Mandatory." - "~n --msg-id <1|2|3> Choose which message to use during the test." - "~n Basically: " - "~n 1: small" - "~n 2: medium" - "~n 3: large" - "~n Defaults to: 1" - "~n --max-outstanding <Num> How many messages to send before waiting for" - "~n a reply." - "~n Valid only for client." - "~n Defaults to: " - "~n MsgID 1: 100" - "~n MsgID 2: 10" - "~n MsgID 3: 1" - "~n --runtime <Time> Time of the test in seconds." - "~n Only valid for client." - "~n Mandatory." - "~n Defaults to: 60 (seconds)" + "~n --help Display this info and exit. " + "~n --server [server-options] Start a server. " + "~n There are no mandatory server options." + "~n --client client-options Start a client" + "~n Some client options are mandatory and" + "~n others optional." + "~n --domain <domain> local | inet | inet6" + "~n Which domain to use." + "~n Only valid for server." + "~n Defaults to: inet" + "~n --async Asynchronous mode (Timeout = nowait)" + "~n This option is only valid for transport = sock." + "~n Also, its only used when active =/= false." + "~n --active <active> boolean() | once." + "~n Valid for both client and server." + "~n Defaults to: false" + "~n --transport <transport> Which transport to use: gen|sock[:plain|msg]" + "~n gen: gen_tcp" + "~n sock: socket" + "~n plain: recv/send (default)" + "~n msg: recvmsg/sendmsg" + "~n Defaults to: sock:plain" + "~n --scon <addr>:<port>|<path> Server info." + "~n The address part is in the standard form:" + "~n \"a.b.c.d\"." + "~n <path> is used for Unix Domain sockets (local)." + "~n Only valid, and mandatory, for client." + "~n --msg-id <1|2|3> Choose which message to use during the test." + "~n Basically: " + "~n 1: small" + "~n 2: medium" + "~n 3: large" + "~n Defaults to: 1" + "~n --max-outstanding <Num> How many messages to send before waiting for" + "~n a reply." + "~n Valid only for client." + "~n Defaults to: " + "~n MsgID 1: 100" + "~n MsgID 2: 10" + "~n MsgID 3: 1" + "~n --runtime <Time> Time of the test in seconds." + "~n Only valid for client." + "~n Mandatory." + "~n Defaults to: 60 (seconds)" "~n" "~n" "~n", @@ -106,6 +113,8 @@ process_args(Args) -> process_server_args(Args) -> Defaults = #{role => server, + domain => inet, + async => false, active => false, transport => {sock, plain}}, process_server_args(Args, Defaults). @@ -113,6 +122,15 @@ process_server_args(Args) -> process_server_args([], State) -> State; +process_server_args(["--domain", Domain|Args], State) + when ((Domain =:= "local") orelse + (Domain =:= "inet") orelse + (Domain =:= "inet6")) -> + process_server_args(Args, State#{domain => list_to_atom(Domain)}); + +process_server_args(["--async"|Args], State) -> + process_server_args(Args, State#{async => true}); + process_server_args(["--active", Active|Args], State) when ((Active =:= "false") orelse (Active =:= "once") orelse @@ -134,10 +152,11 @@ process_server_args([Arg|_], _State) -> process_client_args(Args) -> Defaults = #{role => client, + async => false, active => false, transport => {sock, plain}, %% Will cause error if not provided - %% Should be "addr:port" + %% Should be "addr:port or string() server => undefined, msg_id => 1, %% Will be filled in based on msg_id if not provided @@ -148,10 +167,13 @@ process_client_args(Args) -> process_client_args([], State) -> process_client_args_ensure_max_outstanding(State); +process_client_args(["--async"|Args], State) -> + process_client_args(Args, State#{async => true}); + process_client_args(["--active", Active|Args], State) - when ((Active =:= "false") orelse - (Active =:= "once") orelse - (Active =:= "true")) -> + when (Active =:= "false") orelse + (Active =:= "once") orelse + (Active =:= "true") -> process_client_args(Args, State#{active => list_to_atom(Active)}); process_client_args(["--transport", "gen" | Args], State) -> @@ -199,6 +221,8 @@ process_client_args(["--scon", Server|Args], State) -> usage(f("Invalid Server Port: ~s", [PortStr])) end, process_client_args(Args, State#{server => {Addr, Port}}); + [Path] -> + process_client_args(Args, State#{server => Path}); _ -> usage(f("Invalid Server: ~s", [Server])) end; @@ -249,9 +273,11 @@ process_client_args_ensure_max_outstanding( %% ========================================================================== exec(#{role := server, + domain := Domain, active := Active, - transport := gen}) -> - case socket_test_ttest_tcp_server_gen:start(Active) of + transport := gen}) + when (Domain =:= inet) orelse (Domain =:= inet6) -> + case socket_test_ttest_tcp_server_gen:start(Domain, Active) of {ok, {Pid, _}} -> MRef = erlang:monitor(process, Pid), receive @@ -264,9 +290,11 @@ exec(#{role := server, error end; exec(#{role := server, + domain := Domain, + async := Async, active := Active, transport := {sock, Method}}) -> - case socket_test_ttest_tcp_server_socket:start(Method, Active) of + case socket_test_ttest_tcp_server_socket:start(Method, Domain, Async, Active) of {ok, {Pid, _}} -> MRef = erlang:monitor(process, Pid), receive @@ -283,15 +311,15 @@ exec(#{role := client, server := undefined}) -> usage("Mandatory option 'server' not provided"); exec(#{role := client, - server := {Addr, Port}, + server := {_Addr, _Port} = ServerInfo, active := Active, transport := gen, msg_id := MsgID, max_outstanding := MaxOutstanding, runtime := RunTime}) -> case socket_test_ttest_tcp_client_gen:start(true, + ServerInfo, Active, - Addr, Port, MsgID, MaxOutstanding, RunTime) of {ok, Pid} -> @@ -306,16 +334,18 @@ exec(#{role := client, error end; exec(#{role := client, - server := {Addr, Port}, + server := ServerInfo, + async := Async, active := Active, transport := {sock, Method}, msg_id := MsgID, max_outstanding := MaxOutstanding, runtime := RunTime}) -> case socket_test_ttest_tcp_client_socket:start(true, + Async, Method, + ServerInfo, Active, - Addr, Port, MsgID, MaxOutstanding, RunTime) of {ok, Pid} -> diff --git a/erts/emulator/test/esock_ttest/esock-ttest-client b/erts/emulator/test/esock_ttest/esock-ttest-client index 1ab56f2d44..7c90ae6391 100755 --- a/erts/emulator/test/esock_ttest/esock-ttest-client +++ b/erts/emulator/test/esock_ttest/esock-ttest-client @@ -26,33 +26,46 @@ ESOCK_TTEST=$EMU_TEST/esock_ttest RUNTIME=30 -MSGID=$1 -SERVER_ADDR=$2 -SERVER_PORT=$3 +if [ $# = 3 ]; then + MSGID=$1 + SERVER_INFO=$2:$3 + + ITERATIONS="\ + gen false $MSGID + gen true $MSGID + gen once $MSGID + sock false $MSGID + sock true $MSGID + sock once $MSGID" + +else + if [ $# = 2 ]; then + MSGID=$1 + SERVER_INFO=$2 + + ITERATIONS="\ + sock false $MSGID + sock true $MSGID + sock once $MSGID" + + else + echo "Invalid number of args" + exit 1; + fi +fi + # --------------------------------------------------------------------------- -ITERATIONS="\ - gen false $MSGID - gen true $MSGID - gen once $MSGID - sock false $MSGID - sock true $MSGID - sock once $MSGID" - -# gen false 2 -# gen true 2 -# gen once 2 -# sock false 2 -# sock true 2 -# sock once 2 -# gen false 3 -# gen true 3 -# gen once 3 -# sock false 3 -# sock true 3 -# sock once 3 -# +# For when we have figured out how to configure local for gen_tcp... + +#ITERATIONS="\ +# gen false $MSGID +# gen true $MSGID +# gen once $MSGID +# sock false $MSGID +# sock true $MSGID +# sock once $MSGID" # --------------------------------------------------------------------------- @@ -64,7 +77,7 @@ echo "$ITERATIONS" | # The /dev/null at the end is necessary because erlang "does things" with stdin # and this case would cause the 'while read' to "fail" so that we only would # loop one time - $ESOCK_TTEST/esock-ttest --client --transport $TRANSPORT --active $ACTIVE --msg-id $MSG_ID --scon $SERVER_ADDR:$SERVER_PORT --runtime $RUNTIME </dev/null + $ESOCK_TTEST/esock-ttest --client --transport $TRANSPORT --active $ACTIVE --msg-id $MSG_ID --scon $SERVER_INFO --runtime $RUNTIME </dev/null echo "" done diff --git a/erts/emulator/test/esock_ttest/esock-ttest-server-sock b/erts/emulator/test/esock_ttest/esock-ttest-server-sock index 4ec0d335d9..fc87499f09 100755 --- a/erts/emulator/test/esock_ttest/esock-ttest-server-sock +++ b/erts/emulator/test/esock_ttest/esock-ttest-server-sock @@ -24,9 +24,27 @@ EMU=$ERL_TOP/erts/emulator EMU_TEST=$EMU/test ESOCK_TTEST=$EMU_TEST/esock_ttest -if [ $# = 1 ]; then - ACTIVE="--active $1" +# $1 - async - boolean() +# $2 - active - once | boolean() +if [ $# = 2 ]; then + + async=$1 + active=$2 + + if [ $async = true ]; then + ASYNC="--async" + else + ASYNC= + fi + + ACTIVE="--active $active" + +else + echo "<ERROR> Missing args: async and active" + echo "" + exit 1 fi -$ESOCK_TTEST/esock-ttest --server --transport sock $ACTIVE + +$ESOCK_TTEST/esock-ttest --server $ASYNC --transport sock $ACTIVE diff --git a/erts/emulator/test/exception_SUITE.erl b/erts/emulator/test/exception_SUITE.erl index c4d9ea515a..154bce3c35 100644 --- a/erts/emulator/test/exception_SUITE.erl +++ b/erts/emulator/test/exception_SUITE.erl @@ -23,6 +23,7 @@ -export([all/0, suite/0, badmatch/1, pending_errors/1, nil_arith/1, top_of_stacktrace/1, stacktrace/1, nested_stacktrace/1, raise/1, gunilla/1, per/1, + change_exception_class/1, exception_with_heap_frag/1, backtrace_depth/1, line_numbers/1]). @@ -48,6 +49,7 @@ suite() -> all() -> [badmatch, pending_errors, nil_arith, top_of_stacktrace, stacktrace, nested_stacktrace, raise, gunilla, per, + change_exception_class, exception_with_heap_frag, backtrace_depth, line_numbers]. -define(try_match(E), @@ -512,6 +514,38 @@ t1(_,X,_) -> t2(_,X,_) -> (X bsl 1) + 1. +change_exception_class(_Config) -> + try + change_exception_class_1(fun() -> throw(arne) end) + catch + error:arne -> + ok; + Class:arne -> + ct:fail({wrong_exception_class,Class}) + end. + +change_exception_class_1(F) -> + try + change_exception_class_2(F) + after + %% The exception would be caught and rethrown using + %% an i_raise instruction. Before the correction + %% of the raw_raise instruction, the change of class + %% would not stick. + io:put_chars("Exception automatically rethrown here\n") + end. + +change_exception_class_2(F) -> + try + F() + catch + throw:Reason:Stack -> + %% Translated to a raw_raise instruction. + %% The change of exception class would not stick + %% if the i_raise instruction was later executed. + erlang:raise(error, Reason, Stack) + end. + %% %% Make sure that even if a BIF builds an heap fragment, then causes an exception, %% the stacktrace term will still be OK (specifically, that it does not contain diff --git a/erts/emulator/test/multi_load_SUITE.erl b/erts/emulator/test/multi_load_SUITE.erl index edf3205812..c79e2b6dcd 100644 --- a/erts/emulator/test/multi_load_SUITE.erl +++ b/erts/emulator/test/multi_load_SUITE.erl @@ -30,7 +30,15 @@ all() -> [many,on_load,errors]. many(_Config) -> - Ms = make_modules(100, fun many_module/1), + + N = case erlang:system_info(build_type) of + valgrind -> + 10; + _ -> + 100 + end, + + Ms = make_modules(N, fun many_module/1), io:put_chars("Light load\n" "=========="), diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl index b530ced566..3684cde8d4 100644 --- a/erts/emulator/test/process_SUITE.erl +++ b/erts/emulator/test/process_SUITE.erl @@ -1098,42 +1098,86 @@ process_info_status_handled_signal(Config) when is_list(Config) -> %% OTP-15709 %% Provoke a bug where process_info(reductions) returned wrong result %% because REDS_IN (def_arg_reg[5]) is read when the process in not running. +%% +%% And a bug where process_info(reductions) on a process which was releasing its +%% main lock during execution could result in negative reduction diffs. process_info_reductions(Config) when is_list(Config) -> - pi_reductions_tester(spawn_link(fun() -> pi_reductions_spinnloop() end)), - pi_reductions_tester(spawn_link(fun() -> pi_reductions_recvloop() end)), + {S1, S2} = case erlang:system_info(schedulers) of + 1 -> {1,1}; + _ -> {1,2} + end, + io:format("Run on schedulers ~p and ~p\n", [S1,S2]), + Boss = self(), + Doer = spawn_opt(fun () -> + pi_reductions_tester(true, 10, fun pi_reductions_spinnloop/0, S2), + pi_reductions_tester(true, 10, fun pi_reductions_recvloop/0, S2), + pi_reductions_tester(false, 100, fun pi_reductions_main_unlocker/0, S2), + Boss ! {self(), done} + end, + [link, {scheduler, S1}]), + + {Doer, done} = receive M -> M end, ok. -pi_reductions_tester(Pid) -> - {_, DiffList} = - lists:foldl(fun(_, {Prev, Acc}) -> - %% Add another item that force sending the request - %% as a signal, like 'current_function'. - PI = process_info(Pid, [reductions, current_function]), - [{reductions,Reds}, {current_function,_}] = PI, - Diff = Reds - Prev, - {Diff, true} = {Diff, (Diff >= 0)}, - {Diff, true} = {Diff, (Diff =< 1000*1000)}, - {Reds, [Diff | Acc]} - end, - {0, []}, - lists:seq(1,10)), +pi_reductions_tester(ForceSignal, MaxCalls, Fun, S2) -> + Pid = spawn_opt(Fun, [link, {scheduler,S2}]), + Extra = case ForceSignal of + true -> + %% Add another item that force sending the request + %% as a signal, like 'current_function'. + [current_function]; + false -> + [] + end, + LoopFun = fun Me(Calls, Prev, Acc0) -> + PI = process_info(Pid, [reductions | Extra]), + [{reductions,Reds} | _] = PI, + Diff = Reds - Prev, + %% Verify we get sane non-negative reduction diffs + {Diff, true} = {Diff, (Diff >= 0)}, + {Diff, true} = {Diff, (Diff =< 1000*1000)}, + Acc1 = [Diff | Acc0], + case Calls >= MaxCalls of + true -> Acc1; + false -> Me(Calls+1, Reds, Acc1) + end + end, + DiffList = LoopFun(0, 0, []), unlink(Pid), exit(Pid,kill), - io:format("Reduction diffs: ~p\n", [DiffList]), + io:format("Reduction diffs: ~p\n", [lists:reverse(DiffList)]), ok. pi_reductions_spinnloop() -> %% 6 args to make use of def_arg_reg[5] which is also used as REDS_IN - pi_reductions_spinnloop(1, atom, "hej", self(), make_ref(), 3.14). + pi_reductions_spinnloop(999*1000, atom, "hej", self(), make_ref(), 3.14). -pi_reductions_spinnloop(A,B,C,D,E,F) -> - pi_reductions_spinnloop(B,C,D,E,F,A). +pi_reductions_spinnloop(N,A,B,C,D,E) when N > 0 -> + pi_reductions_spinnloop(N-1,B,C,D,E,A); +pi_reductions_spinnloop(0,_,_,_,_,_) -> + %% Stop to limit max number of reductions consumed + pi_reductions_recvloop(). pi_reductions_recvloop() -> receive "a free lunch" -> false end. +pi_reductions_main_unlocker() -> + Other = spawn_link(fun() -> receive die -> ok end end), + pi_reductions_main_unlocker_loop(Other). + +pi_reductions_main_unlocker_loop(Other) -> + %% Assumption: register(OtherPid, Name) will unlock main lock of calling + %% process during execution. + register(pi_reductions_main_unlocker, Other), + unregister(pi_reductions_main_unlocker), + + %% Yield in order to increase probability of process_info sometimes probing + %% this process when it's not RUNNING. + erlang:yield(), + pi_reductions_main_unlocker_loop(Other). + %% Tests erlang:bump_reductions/1. bump_reductions(Config) when is_list(Config) -> diff --git a/erts/emulator/test/socket_SUITE.erl b/erts/emulator/test/socket_SUITE.erl index e3545ccbf9..c82e2efad9 100644 --- a/erts/emulator/test/socket_SUITE.erl +++ b/erts/emulator/test/socket_SUITE.erl @@ -70,15 +70,40 @@ %% *** API Basic *** api_b_open_and_close_udp4/1, api_b_open_and_close_tcp4/1, + api_b_open_and_close_udpL/1, + api_b_open_and_close_tcpL/1, api_b_sendto_and_recvfrom_udp4/1, + api_b_sendto_and_recvfrom_udpL/1, api_b_sendmsg_and_recvmsg_udp4/1, + api_b_sendmsg_and_recvmsg_udpL/1, api_b_send_and_recv_tcp4/1, + api_b_send_and_recv_tcpL/1, api_b_sendmsg_and_recvmsg_tcp4/1, + api_b_sendmsg_and_recvmsg_tcpL/1, + + %% *** API async *** + api_a_connect_tcp4/1, + api_a_sendto_and_recvfrom_udp4/1, + api_a_sendmsg_and_recvmsg_udp4/1, + api_a_send_and_recv_tcp4/1, + api_a_sendmsg_and_recvmsg_tcp4/1, + api_a_recvfrom_cancel_udp4/1, + api_a_recvmsg_cancel_udp4/1, + api_a_accept_cancel_tcp4/1, + api_a_recv_cancel_tcp4/1, + api_a_recvmsg_cancel_tcp4/1, + api_a_mrecvfrom_cancel_udp4/1, + api_a_mrecvmsg_cancel_udp4/1, + api_a_maccept_cancel_tcp4/1, + api_a_mrecv_cancel_tcp4/1, + api_a_mrecvmsg_cancel_tcp4/1, + %% *** API Options *** api_opt_simple_otp_options/1, api_opt_simple_otp_rcvbuf_option/1, api_opt_simple_otp_controlling_process/1, + api_opt_ip_add_drop_membership/1, %% *** API Operation Timeout *** api_to_connect_tcp4/1, @@ -107,57 +132,79 @@ %% *** Socket Closure *** sc_cpe_socket_cleanup_tcp4/1, sc_cpe_socket_cleanup_tcp6/1, + sc_cpe_socket_cleanup_tcpL/1, sc_cpe_socket_cleanup_udp4/1, sc_cpe_socket_cleanup_udp6/1, + sc_cpe_socket_cleanup_udpL/1, sc_lc_recv_response_tcp4/1, sc_lc_recv_response_tcp6/1, + sc_lc_recv_response_tcpL/1, sc_lc_recvfrom_response_udp4/1, sc_lc_recvfrom_response_udp6/1, + sc_lc_recvfrom_response_udpL/1, sc_lc_recvmsg_response_tcp4/1, sc_lc_recvmsg_response_tcp6/1, + sc_lc_recvmsg_response_tcpL/1, sc_lc_recvmsg_response_udp4/1, sc_lc_recvmsg_response_udp6/1, + sc_lc_recvmsg_response_udpL/1, sc_lc_acceptor_response_tcp4/1, sc_lc_acceptor_response_tcp6/1, + sc_lc_acceptor_response_tcpL/1, sc_rc_recv_response_tcp4/1, sc_rc_recv_response_tcp6/1, + sc_rc_recv_response_tcpL/1, sc_rc_recvmsg_response_tcp4/1, sc_rc_recvmsg_response_tcp6/1, + sc_rc_recvmsg_response_tcpL/1, sc_rs_recv_send_shutdown_receive_tcp4/1, sc_rs_recv_send_shutdown_receive_tcp6/1, + sc_rs_recv_send_shutdown_receive_tcpL/1, sc_rs_recvmsg_send_shutdown_receive_tcp4/1, sc_rs_recvmsg_send_shutdown_receive_tcp6/1, + sc_rs_recvmsg_send_shutdown_receive_tcpL/1, %% *** Traffic *** traffic_send_and_recv_chunks_tcp4/1, traffic_send_and_recv_chunks_tcp6/1, + traffic_send_and_recv_chunks_tcpL/1, traffic_ping_pong_small_send_and_recv_tcp4/1, traffic_ping_pong_small_send_and_recv_tcp6/1, + traffic_ping_pong_small_send_and_recv_tcpL/1, traffic_ping_pong_medium_send_and_recv_tcp4/1, traffic_ping_pong_medium_send_and_recv_tcp6/1, + traffic_ping_pong_medium_send_and_recv_tcpL/1, traffic_ping_pong_large_send_and_recv_tcp4/1, traffic_ping_pong_large_send_and_recv_tcp6/1, + traffic_ping_pong_large_send_and_recv_tcpL/1, traffic_ping_pong_small_sendto_and_recvfrom_udp4/1, traffic_ping_pong_small_sendto_and_recvfrom_udp6/1, + traffic_ping_pong_small_sendto_and_recvfrom_udpL/1, traffic_ping_pong_medium_sendto_and_recvfrom_udp4/1, traffic_ping_pong_medium_sendto_and_recvfrom_udp6/1, + traffic_ping_pong_medium_sendto_and_recvfrom_udpL/1, traffic_ping_pong_small_sendmsg_and_recvmsg_tcp4/1, traffic_ping_pong_small_sendmsg_and_recvmsg_tcp6/1, + traffic_ping_pong_small_sendmsg_and_recvmsg_tcpL/1, traffic_ping_pong_medium_sendmsg_and_recvmsg_tcp4/1, traffic_ping_pong_medium_sendmsg_and_recvmsg_tcp6/1, + traffic_ping_pong_medium_sendmsg_and_recvmsg_tcpL/1, traffic_ping_pong_large_sendmsg_and_recvmsg_tcp4/1, traffic_ping_pong_large_sendmsg_and_recvmsg_tcp6/1, + traffic_ping_pong_large_sendmsg_and_recvmsg_tcpL/1, traffic_ping_pong_small_sendmsg_and_recvmsg_udp4/1, traffic_ping_pong_small_sendmsg_and_recvmsg_udp6/1, + traffic_ping_pong_small_sendmsg_and_recvmsg_udpL/1, traffic_ping_pong_medium_sendmsg_and_recvmsg_udp4/1, traffic_ping_pong_medium_sendmsg_and_recvmsg_udp6/1, + traffic_ping_pong_medium_sendmsg_and_recvmsg_udpL/1, %% *** Time Test *** %% Server: transport = gen_tcp, active = false @@ -325,24 +372,33 @@ %% Client: transport = socket(tcp) ttest_ssockf_csockf_small_tcp4/1, ttest_ssockf_csockf_small_tcp6/1, + ttest_ssockf_csockf_small_tcpL/1, ttest_ssockf_csockf_medium_tcp4/1, ttest_ssockf_csockf_medium_tcp6/1, + ttest_ssockf_csockf_medium_tcpL/1, ttest_ssockf_csockf_large_tcp4/1, ttest_ssockf_csockf_large_tcp6/1, + ttest_ssockf_csockf_large_tcpL/1, ttest_ssockf_csocko_small_tcp4/1, ttest_ssockf_csocko_small_tcp6/1, + ttest_ssockf_csocko_small_tcpL/1, ttest_ssockf_csocko_medium_tcp4/1, ttest_ssockf_csocko_medium_tcp6/1, + ttest_ssockf_csocko_medium_tcpL/1, ttest_ssockf_csocko_large_tcp4/1, ttest_ssockf_csocko_large_tcp6/1, + ttest_ssockf_csocko_large_tcpL/1, ttest_ssockf_csockt_small_tcp4/1, ttest_ssockf_csockt_small_tcp6/1, + ttest_ssockf_csockt_small_tcpL/1, ttest_ssockf_csockt_medium_tcp4/1, ttest_ssockf_csockt_medium_tcp6/1, + ttest_ssockf_csockt_medium_tcpL/1, ttest_ssockf_csockt_large_tcp4/1, ttest_ssockf_csockt_large_tcp6/1, + ttest_ssockf_csockt_large_tcpL/1, %% Server: transport = socket(tcp), active = once %% Client: transport = gen_tcp @@ -371,24 +427,33 @@ %% Client: transport = socket(tcp) ttest_ssocko_csockf_small_tcp4/1, ttest_ssocko_csockf_small_tcp6/1, + ttest_ssocko_csockf_small_tcpL/1, ttest_ssocko_csockf_medium_tcp4/1, + ttest_ssocko_csockf_medium_tcpL/1, ttest_ssocko_csockf_medium_tcp6/1, ttest_ssocko_csockf_large_tcp4/1, ttest_ssocko_csockf_large_tcp6/1, + ttest_ssocko_csockf_large_tcpL/1, ttest_ssocko_csocko_small_tcp4/1, ttest_ssocko_csocko_small_tcp6/1, + ttest_ssocko_csocko_small_tcpL/1, ttest_ssocko_csocko_medium_tcp4/1, ttest_ssocko_csocko_medium_tcp6/1, + ttest_ssocko_csocko_medium_tcpL/1, ttest_ssocko_csocko_large_tcp4/1, ttest_ssocko_csocko_large_tcp6/1, + ttest_ssocko_csocko_large_tcpL/1, ttest_ssocko_csockt_small_tcp4/1, ttest_ssocko_csockt_small_tcp6/1, + ttest_ssocko_csockt_small_tcpL/1, ttest_ssocko_csockt_medium_tcp4/1, ttest_ssocko_csockt_medium_tcp6/1, + ttest_ssocko_csockt_medium_tcpL/1, ttest_ssocko_csockt_large_tcp4/1, ttest_ssocko_csockt_large_tcp6/1, + ttest_ssocko_csockt_large_tcpL/1, %% Server: transport = socket(tcp), active = true %% Client: transport = gen_tcp @@ -417,24 +482,33 @@ %% Client: transport = socket(tcp) ttest_ssockt_csockf_small_tcp4/1, ttest_ssockt_csockf_small_tcp6/1, + ttest_ssockt_csockf_small_tcpL/1, ttest_ssockt_csockf_medium_tcp4/1, ttest_ssockt_csockf_medium_tcp6/1, + ttest_ssockt_csockf_medium_tcpL/1, ttest_ssockt_csockf_large_tcp4/1, ttest_ssockt_csockf_large_tcp6/1, + ttest_ssockt_csockf_large_tcpL/1, ttest_ssockt_csocko_small_tcp4/1, ttest_ssockt_csocko_small_tcp6/1, + ttest_ssockt_csocko_small_tcpL/1, ttest_ssockt_csocko_medium_tcp4/1, ttest_ssockt_csocko_medium_tcp6/1, + ttest_ssockt_csocko_medium_tcpL/1, ttest_ssockt_csocko_large_tcp4/1, ttest_ssockt_csocko_large_tcp6/1, + ttest_ssockt_csocko_large_tcpL/1, ttest_ssockt_csockt_small_tcp4/1, ttest_ssockt_csockt_small_tcp6/1, + ttest_ssockt_csockt_small_tcpL/1, ttest_ssockt_csockt_medium_tcp4/1, ttest_ssockt_csockt_medium_tcp6/1, + ttest_ssockt_csockt_medium_tcpL/1, ttest_ssockt_csockt_large_tcp4/1, - ttest_ssockt_csockt_large_tcp6/1 + ttest_ssockt_csockt_large_tcp6/1, + ttest_ssockt_csockt_large_tcpL/1 %% Tickets ]). @@ -508,71 +582,78 @@ use_group(Group, Env, Default) -> groups() -> - [{api, [], api_cases()}, - {api_basic, [], api_basic_cases()}, - {api_options, [], api_options_cases()}, - {api_op_with_timeout, [], api_op_with_timeout_cases()}, - {socket_closure, [], socket_closure_cases()}, - {sc_ctrl_proc_exit, [], sc_cp_exit_cases()}, - {sc_local_close, [], sc_lc_cases()}, - {sc_remote_close, [], sc_rc_cases()}, - {sc_remote_shutdown, [], sc_rs_cases()}, - {traffic, [], traffic_cases()}, - {ttest, [], ttest_cases()}, - {ttest_sgenf, [], ttest_sgenf_cases()}, - {ttest_sgenf_cgen, [], ttest_sgenf_cgen_cases()}, - {ttest_sgenf_cgenf, [], ttest_sgenf_cgenf_cases()}, - {ttest_sgenf_cgeno, [], ttest_sgenf_cgeno_cases()}, - {ttest_sgenf_cgent, [], ttest_sgenf_cgent_cases()}, - {ttest_sgenf_csock, [], ttest_sgenf_csock_cases()}, - {ttest_sgenf_csockf, [], ttest_sgenf_csockf_cases()}, - {ttest_sgenf_csocko, [], ttest_sgenf_csocko_cases()}, - {ttest_sgenf_csockt, [], ttest_sgenf_csockt_cases()}, - {ttest_sgeno, [], ttest_sgeno_cases()}, - {ttest_sgeno_cgen, [], ttest_sgeno_cgen_cases()}, - {ttest_sgeno_cgenf, [], ttest_sgeno_cgenf_cases()}, - {ttest_sgeno_cgeno, [], ttest_sgeno_cgeno_cases()}, - {ttest_sgeno_cgent, [], ttest_sgeno_cgent_cases()}, - {ttest_sgeno_csock, [], ttest_sgeno_csock_cases()}, - {ttest_sgeno_csockf, [], ttest_sgeno_csockf_cases()}, - {ttest_sgeno_csocko, [], ttest_sgeno_csocko_cases()}, - {ttest_sgeno_csockt, [], ttest_sgeno_csockt_cases()}, - {ttest_sgent, [], ttest_sgent_cases()}, - {ttest_sgent_cgen, [], ttest_sgent_cgen_cases()}, - {ttest_sgent_cgenf, [], ttest_sgent_cgenf_cases()}, - {ttest_sgent_cgeno, [], ttest_sgent_cgeno_cases()}, - {ttest_sgent_cgent, [], ttest_sgent_cgent_cases()}, - {ttest_sgent_csock, [], ttest_sgent_csock_cases()}, - {ttest_sgent_csockf, [], ttest_sgent_csockf_cases()}, - {ttest_sgent_csocko, [], ttest_sgent_csocko_cases()}, - {ttest_sgent_csockt, [], ttest_sgent_csockt_cases()}, - {ttest_ssockf, [], ttest_ssockf_cases()}, - {ttest_ssockf_cgen, [], ttest_ssockf_cgen_cases()}, - {ttest_ssockf_cgenf, [], ttest_ssockf_cgenf_cases()}, - {ttest_ssockf_cgeno, [], ttest_ssockf_cgeno_cases()}, - {ttest_ssockf_cgent, [], ttest_ssockf_cgent_cases()}, - {ttest_ssockf_csock, [], ttest_ssockf_csock_cases()}, - {ttest_ssockf_csockf, [], ttest_ssockf_csockf_cases()}, - {ttest_ssockf_csocko, [], ttest_ssockf_csocko_cases()}, - {ttest_ssockf_csockt, [], ttest_ssockf_csockt_cases()}, - {ttest_ssocko, [], ttest_ssocko_cases()}, - {ttest_ssocko_cgen, [], ttest_ssocko_cgen_cases()}, - {ttest_ssocko_cgenf, [], ttest_ssocko_cgenf_cases()}, - {ttest_ssocko_cgeno, [], ttest_ssocko_cgeno_cases()}, - {ttest_ssocko_cgent, [], ttest_ssocko_cgent_cases()}, - {ttest_ssocko_csock, [], ttest_ssocko_csock_cases()}, - {ttest_ssocko_csockf, [], ttest_ssocko_csockf_cases()}, - {ttest_ssocko_csocko, [], ttest_ssocko_csocko_cases()}, - {ttest_ssocko_csockt, [], ttest_ssocko_csockt_cases()}, - {ttest_ssockt, [], ttest_ssockt_cases()}, - {ttest_ssockt_cgen, [], ttest_ssockt_cgen_cases()}, - {ttest_ssockt_cgenf, [], ttest_ssockt_cgenf_cases()}, - {ttest_ssockt_cgeno, [], ttest_ssockt_cgeno_cases()}, - {ttest_ssockt_cgent, [], ttest_ssockt_cgent_cases()}, - {ttest_ssockt_csock, [], ttest_ssockt_csock_cases()}, - {ttest_ssockt_csockf, [], ttest_ssockt_csockf_cases()}, - {ttest_ssockt_csocko, [], ttest_ssockt_csocko_cases()}, - {ttest_ssockt_csockt, [], ttest_ssockt_csockt_cases()} + [{api, [], api_cases()}, + {api_basic, [], api_basic_cases()}, + {api_async, [], api_async_cases()}, + {api_options, [], api_options_cases()}, + {api_options_otp, [], api_options_otp_cases()}, + {api_options_ip, [], api_options_ip_cases()}, + {api_op_with_timeout, [], api_op_with_timeout_cases()}, + {socket_closure, [], socket_closure_cases()}, + {sc_ctrl_proc_exit, [], sc_cp_exit_cases()}, + {sc_local_close, [], sc_lc_cases()}, + {sc_remote_close, [], sc_rc_cases()}, + {sc_remote_shutdown, [], sc_rs_cases()}, + {traffic, [], traffic_cases()}, + {traffic_chunks, [], traffic_chunks_cases()}, + {traffic_pp_send_recv, [], traffic_pp_send_recv_cases()}, + {traffic_pp_sendto_recvfrom, [], traffic_pp_sendto_recvfrom_cases()}, + {traffic_pp_sendmsg_recvmsg, [], traffic_pp_sendmsg_recvmsg_cases()}, + {ttest, [], ttest_cases()}, + {ttest_sgenf, [], ttest_sgenf_cases()}, + {ttest_sgenf_cgen, [], ttest_sgenf_cgen_cases()}, + {ttest_sgenf_cgenf, [], ttest_sgenf_cgenf_cases()}, + {ttest_sgenf_cgeno, [], ttest_sgenf_cgeno_cases()}, + {ttest_sgenf_cgent, [], ttest_sgenf_cgent_cases()}, + {ttest_sgenf_csock, [], ttest_sgenf_csock_cases()}, + {ttest_sgenf_csockf, [], ttest_sgenf_csockf_cases()}, + {ttest_sgenf_csocko, [], ttest_sgenf_csocko_cases()}, + {ttest_sgenf_csockt, [], ttest_sgenf_csockt_cases()}, + {ttest_sgeno, [], ttest_sgeno_cases()}, + {ttest_sgeno_cgen, [], ttest_sgeno_cgen_cases()}, + {ttest_sgeno_cgenf, [], ttest_sgeno_cgenf_cases()}, + {ttest_sgeno_cgeno, [], ttest_sgeno_cgeno_cases()}, + {ttest_sgeno_cgent, [], ttest_sgeno_cgent_cases()}, + {ttest_sgeno_csock, [], ttest_sgeno_csock_cases()}, + {ttest_sgeno_csockf, [], ttest_sgeno_csockf_cases()}, + {ttest_sgeno_csocko, [], ttest_sgeno_csocko_cases()}, + {ttest_sgeno_csockt, [], ttest_sgeno_csockt_cases()}, + {ttest_sgent, [], ttest_sgent_cases()}, + {ttest_sgent_cgen, [], ttest_sgent_cgen_cases()}, + {ttest_sgent_cgenf, [], ttest_sgent_cgenf_cases()}, + {ttest_sgent_cgeno, [], ttest_sgent_cgeno_cases()}, + {ttest_sgent_cgent, [], ttest_sgent_cgent_cases()}, + {ttest_sgent_csock, [], ttest_sgent_csock_cases()}, + {ttest_sgent_csockf, [], ttest_sgent_csockf_cases()}, + {ttest_sgent_csocko, [], ttest_sgent_csocko_cases()}, + {ttest_sgent_csockt, [], ttest_sgent_csockt_cases()}, + {ttest_ssockf, [], ttest_ssockf_cases()}, + {ttest_ssockf_cgen, [], ttest_ssockf_cgen_cases()}, + {ttest_ssockf_cgenf, [], ttest_ssockf_cgenf_cases()}, + {ttest_ssockf_cgeno, [], ttest_ssockf_cgeno_cases()}, + {ttest_ssockf_cgent, [], ttest_ssockf_cgent_cases()}, + {ttest_ssockf_csock, [], ttest_ssockf_csock_cases()}, + {ttest_ssockf_csockf, [], ttest_ssockf_csockf_cases()}, + {ttest_ssockf_csocko, [], ttest_ssockf_csocko_cases()}, + {ttest_ssockf_csockt, [], ttest_ssockf_csockt_cases()}, + {ttest_ssocko, [], ttest_ssocko_cases()}, + {ttest_ssocko_cgen, [], ttest_ssocko_cgen_cases()}, + {ttest_ssocko_cgenf, [], ttest_ssocko_cgenf_cases()}, + {ttest_ssocko_cgeno, [], ttest_ssocko_cgeno_cases()}, + {ttest_ssocko_cgent, [], ttest_ssocko_cgent_cases()}, + {ttest_ssocko_csock, [], ttest_ssocko_csock_cases()}, + {ttest_ssocko_csockf, [], ttest_ssocko_csockf_cases()}, + {ttest_ssocko_csocko, [], ttest_ssocko_csocko_cases()}, + {ttest_ssocko_csockt, [], ttest_ssocko_csockt_cases()}, + {ttest_ssockt, [], ttest_ssockt_cases()}, + {ttest_ssockt_cgen, [], ttest_ssockt_cgen_cases()}, + {ttest_ssockt_cgenf, [], ttest_ssockt_cgenf_cases()}, + {ttest_ssockt_cgeno, [], ttest_ssockt_cgeno_cases()}, + {ttest_ssockt_cgent, [], ttest_ssockt_cgent_cases()}, + {ttest_ssockt_csock, [], ttest_ssockt_csock_cases()}, + {ttest_ssockt_csockf, [], ttest_ssockt_csockf_cases()}, + {ttest_ssockt_csocko, [], ttest_ssockt_csocko_cases()}, + {ttest_ssockt_csockt, [], ttest_ssockt_csockt_cases()} %% {tickets, [], ticket_cases()} ]. @@ -580,6 +661,7 @@ groups() -> api_cases() -> [ {group, api_basic}, + {group, api_async}, {group, api_options}, {group, api_op_with_timeout} ]. @@ -588,19 +670,55 @@ api_basic_cases() -> [ api_b_open_and_close_udp4, api_b_open_and_close_tcp4, + api_b_open_and_close_udpL, + api_b_open_and_close_tcpL, api_b_sendto_and_recvfrom_udp4, + api_b_sendto_and_recvfrom_udpL, api_b_sendmsg_and_recvmsg_udp4, + api_b_sendmsg_and_recvmsg_udpL, api_b_send_and_recv_tcp4, - api_b_sendmsg_and_recvmsg_tcp4 + api_b_send_and_recv_tcpL, + api_b_sendmsg_and_recvmsg_tcp4, + api_b_sendmsg_and_recvmsg_tcpL + ]. + +api_async_cases() -> + [ + api_a_connect_tcp4, + api_a_sendto_and_recvfrom_udp4, + api_a_sendmsg_and_recvmsg_udp4, + api_a_send_and_recv_tcp4, + api_a_sendmsg_and_recvmsg_tcp4, + api_a_recvfrom_cancel_udp4, + api_a_recvmsg_cancel_udp4, + api_a_accept_cancel_tcp4, + api_a_recv_cancel_tcp4, + api_a_recvmsg_cancel_tcp4, + api_a_mrecvfrom_cancel_udp4, + api_a_mrecvmsg_cancel_udp4, + api_a_maccept_cancel_tcp4, + api_a_mrecv_cancel_tcp4, + api_a_mrecvmsg_cancel_tcp4 ]. api_options_cases() -> [ + {group, api_options_otp}, + {group, api_options_ip} + ]. + +api_options_otp_cases() -> + [ api_opt_simple_otp_options, api_opt_simple_otp_rcvbuf_option, api_opt_simple_otp_controlling_process ]. +api_options_ip_cases() -> + [ + api_opt_ip_add_drop_membership + ]. + api_op_with_timeout_cases() -> [ api_to_connect_tcp4, @@ -638,13 +756,15 @@ socket_closure_cases() -> ]. %% These cases are all about socket cleanup after the controlling process -%% exits *without* calling socket:close/1. +%% exits *without* explicitly calling socket:close/1. sc_cp_exit_cases() -> [ sc_cpe_socket_cleanup_tcp4, sc_cpe_socket_cleanup_tcp6, + sc_cpe_socket_cleanup_tcpL, sc_cpe_socket_cleanup_udp4, - sc_cpe_socket_cleanup_udp6 + sc_cpe_socket_cleanup_udp6, + sc_cpe_socket_cleanup_udpL ]. %% These cases tests what happens when the socket is closed locally. @@ -652,17 +772,22 @@ sc_lc_cases() -> [ sc_lc_recv_response_tcp4, sc_lc_recv_response_tcp6, + sc_lc_recv_response_tcpL, sc_lc_recvfrom_response_udp4, sc_lc_recvfrom_response_udp6, + sc_lc_recvfrom_response_udpL, sc_lc_recvmsg_response_tcp4, sc_lc_recvmsg_response_tcp6, + sc_lc_recvmsg_response_tcpL, sc_lc_recvmsg_response_udp4, sc_lc_recvmsg_response_udp6, + sc_lc_recvmsg_response_udpL, sc_lc_acceptor_response_tcp4, - sc_lc_acceptor_response_tcp6 + sc_lc_acceptor_response_tcp6, + sc_lc_acceptor_response_tcpL ]. %% These cases tests what happens when the socket is closed remotely. @@ -670,9 +795,11 @@ sc_rc_cases() -> [ sc_rc_recv_response_tcp4, sc_rc_recv_response_tcp6, + sc_rc_recv_response_tcpL, sc_rc_recvmsg_response_tcp4, - sc_rc_recvmsg_response_tcp6 + sc_rc_recvmsg_response_tcp6, + sc_rc_recvmsg_response_tcpL ]. %% These cases tests what happens when the socket is shutdown/closed remotely @@ -681,43 +808,72 @@ sc_rs_cases() -> [ sc_rs_recv_send_shutdown_receive_tcp4, sc_rs_recv_send_shutdown_receive_tcp6, + sc_rs_recv_send_shutdown_receive_tcpL, sc_rs_recvmsg_send_shutdown_receive_tcp4, - sc_rs_recvmsg_send_shutdown_receive_tcp6 + sc_rs_recvmsg_send_shutdown_receive_tcp6, + sc_rs_recvmsg_send_shutdown_receive_tcpL ]. traffic_cases() -> [ + {group, traffic_chunks}, + {group, traffic_pp_send_recv}, + {group, traffic_pp_sendto_recvfrom}, + {group, traffic_pp_sendmsg_recvmsg} + ]. + +traffic_chunks_cases() -> + [ traffic_send_and_recv_chunks_tcp4, traffic_send_and_recv_chunks_tcp6, + traffic_send_and_recv_chunks_tcpL + ]. +traffic_pp_send_recv_cases() -> + [ traffic_ping_pong_small_send_and_recv_tcp4, traffic_ping_pong_small_send_and_recv_tcp6, + traffic_ping_pong_small_send_and_recv_tcpL, traffic_ping_pong_medium_send_and_recv_tcp4, traffic_ping_pong_medium_send_and_recv_tcp6, + traffic_ping_pong_medium_send_and_recv_tcpL, traffic_ping_pong_large_send_and_recv_tcp4, traffic_ping_pong_large_send_and_recv_tcp6, + traffic_ping_pong_large_send_and_recv_tcpL + ]. +traffic_pp_sendto_recvfrom_cases() -> + [ traffic_ping_pong_small_sendto_and_recvfrom_udp4, traffic_ping_pong_small_sendto_and_recvfrom_udp6, + traffic_ping_pong_small_sendto_and_recvfrom_udpL, traffic_ping_pong_medium_sendto_and_recvfrom_udp4, traffic_ping_pong_medium_sendto_and_recvfrom_udp6, + traffic_ping_pong_medium_sendto_and_recvfrom_udpL + ]. +traffic_pp_sendmsg_recvmsg_cases() -> + [ traffic_ping_pong_small_sendmsg_and_recvmsg_tcp4, traffic_ping_pong_small_sendmsg_and_recvmsg_tcp6, + traffic_ping_pong_small_sendmsg_and_recvmsg_tcpL, traffic_ping_pong_medium_sendmsg_and_recvmsg_tcp4, traffic_ping_pong_medium_sendmsg_and_recvmsg_tcp6, + traffic_ping_pong_medium_sendmsg_and_recvmsg_tcpL, traffic_ping_pong_large_sendmsg_and_recvmsg_tcp4, traffic_ping_pong_large_sendmsg_and_recvmsg_tcp6, + traffic_ping_pong_large_sendmsg_and_recvmsg_tcpL, traffic_ping_pong_small_sendmsg_and_recvmsg_udp4, traffic_ping_pong_small_sendmsg_and_recvmsg_udp6, + traffic_ping_pong_small_sendmsg_and_recvmsg_udpL, traffic_ping_pong_medium_sendmsg_and_recvmsg_udp4, - traffic_ping_pong_medium_sendmsg_and_recvmsg_udp6 + traffic_ping_pong_medium_sendmsg_and_recvmsg_udp6, + traffic_ping_pong_medium_sendmsg_and_recvmsg_udpL ]. - - + ttest_cases() -> [ %% Server: transport = gen_tcp, active = false @@ -1123,12 +1279,15 @@ ttest_ssockf_csockf_cases() -> [ ttest_ssockf_csockf_small_tcp4, ttest_ssockf_csockf_small_tcp6, + ttest_ssockf_csockf_small_tcpL, ttest_ssockf_csockf_medium_tcp4, ttest_ssockf_csockf_medium_tcp6, + ttest_ssockf_csockf_medium_tcpL, ttest_ssockf_csockf_large_tcp4, - ttest_ssockf_csockf_large_tcp6 + ttest_ssockf_csockf_large_tcp6, + ttest_ssockf_csockf_large_tcpL ]. %% Server: transport = socket(tcp), active = false @@ -1137,12 +1296,15 @@ ttest_ssockf_csocko_cases() -> [ ttest_ssockf_csocko_small_tcp4, ttest_ssockf_csocko_small_tcp6, + ttest_ssockf_csocko_small_tcpL, ttest_ssockf_csocko_medium_tcp4, ttest_ssockf_csocko_medium_tcp6, + ttest_ssockf_csocko_medium_tcpL, ttest_ssockf_csocko_large_tcp4, - ttest_ssockf_csocko_large_tcp6 + ttest_ssockf_csocko_large_tcp6, + ttest_ssockf_csocko_large_tcpL ]. %% Server: transport = socket(tcp), active = false @@ -1151,12 +1313,15 @@ ttest_ssockf_csockt_cases() -> [ ttest_ssockf_csockt_small_tcp4, ttest_ssockf_csockt_small_tcp6, + ttest_ssockf_csockt_small_tcpL, ttest_ssockf_csockt_medium_tcp4, ttest_ssockf_csockt_medium_tcp6, + ttest_ssockf_csockt_medium_tcpL, ttest_ssockf_csockt_large_tcp4, - ttest_ssockf_csockt_large_tcp6 + ttest_ssockf_csockt_large_tcp6, + ttest_ssockf_csockt_large_tcpL ]. %% Server: transport = socket(tcp), active = once @@ -1232,12 +1397,15 @@ ttest_ssocko_csockf_cases() -> [ ttest_ssocko_csockf_small_tcp4, ttest_ssocko_csockf_small_tcp6, + ttest_ssocko_csockf_small_tcpL, ttest_ssocko_csockf_medium_tcp4, ttest_ssocko_csockf_medium_tcp6, + ttest_ssocko_csockf_medium_tcpL, ttest_ssocko_csockf_large_tcp4, - ttest_ssocko_csockf_large_tcp6 + ttest_ssocko_csockf_large_tcp6, + ttest_ssocko_csockf_large_tcpL ]. %% Server: transport = socket(tcp), active = once @@ -1246,12 +1414,15 @@ ttest_ssocko_csocko_cases() -> [ ttest_ssocko_csocko_small_tcp4, ttest_ssocko_csocko_small_tcp6, + ttest_ssocko_csocko_small_tcpL, ttest_ssocko_csocko_medium_tcp4, ttest_ssocko_csocko_medium_tcp6, + ttest_ssocko_csocko_medium_tcpL, ttest_ssocko_csocko_large_tcp4, - ttest_ssocko_csocko_large_tcp6 + ttest_ssocko_csocko_large_tcp6, + ttest_ssocko_csocko_large_tcpL ]. %% Server: transport = socket(tcp), active = once @@ -1260,12 +1431,15 @@ ttest_ssocko_csockt_cases() -> [ ttest_ssocko_csockt_small_tcp4, ttest_ssocko_csockt_small_tcp6, + ttest_ssocko_csockt_small_tcpL, ttest_ssocko_csockt_medium_tcp4, ttest_ssocko_csockt_medium_tcp6, + ttest_ssocko_csockt_medium_tcpL, ttest_ssocko_csockt_large_tcp4, - ttest_ssocko_csockt_large_tcp6 + ttest_ssocko_csockt_large_tcp6, + ttest_ssocko_csockt_large_tcpL ]. %% Server: transport = socket(tcp), active = true @@ -1341,12 +1515,15 @@ ttest_ssockt_csockf_cases() -> [ ttest_ssockt_csockf_small_tcp4, ttest_ssockt_csockf_small_tcp6, + ttest_ssockt_csockf_small_tcpL, ttest_ssockt_csockf_medium_tcp4, ttest_ssockt_csockf_medium_tcp6, + ttest_ssockt_csockf_medium_tcpL, ttest_ssockt_csockf_large_tcp4, - ttest_ssockt_csockf_large_tcp6 + ttest_ssockt_csockf_large_tcp6, + ttest_ssockt_csockf_large_tcpL ]. %% Server: transport = socket(tcp), active = true @@ -1355,12 +1532,15 @@ ttest_ssockt_csocko_cases() -> [ ttest_ssockt_csocko_small_tcp4, ttest_ssockt_csocko_small_tcp6, + ttest_ssockt_csocko_small_tcpL, ttest_ssockt_csocko_medium_tcp4, ttest_ssockt_csocko_medium_tcp6, + ttest_ssockt_csocko_medium_tcpL, ttest_ssockt_csocko_large_tcp4, - ttest_ssockt_csocko_large_tcp6 + ttest_ssockt_csocko_large_tcp6, + ttest_ssockt_csocko_large_tcpL ]. %% Server: transport = socket(tcp), active = true @@ -1369,12 +1549,15 @@ ttest_ssockt_csockt_cases() -> [ ttest_ssockt_csockt_small_tcp4, ttest_ssockt_csockt_small_tcp6, + ttest_ssockt_csockt_small_tcpL, ttest_ssockt_csockt_medium_tcp4, ttest_ssockt_csockt_medium_tcp6, + ttest_ssockt_csockt_medium_tcpL, ttest_ssockt_csockt_large_tcp4, - ttest_ssockt_csockt_large_tcp6 + ttest_ssockt_csockt_large_tcp6, + ttest_ssockt_csockt_large_tcpL ]. %% ticket_cases() -> @@ -1413,11 +1596,12 @@ init_per_group(ttest = _GroupName, Config) -> io:format("init_per_group(~w) -> entry with" "~n Config: ~p" "~n", [_GroupName, Config]), + ttest_manager_start(), case lists:keysearch(esock_test_ttest_runtime, 1, Config) of {value, _} -> Config; false -> - [{esock_test_ttest_runtime, which_ttest_runtime_env()}|Config] + [{esock_test_ttest_runtime, which_ttest_runtime_env()} | Config] end; init_per_group(_GroupName, Config) -> Config. @@ -1426,6 +1610,7 @@ end_per_group(ttest = _GroupName, Config) -> io:format("init_per_group(~w) -> entry with" "~n Config: ~p" "~n", [_GroupName, Config]), + ttest_manager_stop(), lists:keydelete(esock_test_ttest_runtime, 1, Config); end_per_group(_GroupName, Config) -> Config. @@ -1435,16 +1620,16 @@ init_per_testcase(_TC, Config) -> io:format("init_per_testcase(~w) -> entry with" "~n Config: ~p" "~n", [_TC, Config]), - case quiet_mode(Config) of - default -> - ?LOGGER:start(); - Quiet -> - ?LOGGER:start(Quiet) - end, + %% case quiet_mode(Config) of + %% default -> + %% ?LOGGER:start(); + %% Quiet -> + %% ?LOGGER:start(Quiet) + %% end, Config. end_per_testcase(_TC, Config) -> - ?LOGGER:stop(), + %% ?LOGGER:stop(), Config. @@ -1510,6 +1695,46 @@ api_b_open_and_close_tcp4(_Config) when is_list(_Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Basically open (create) and close an Unix Domain dgram (UDP) socket. +%% With some extra checks... +api_b_open_and_close_udpL(suite) -> + []; +api_b_open_and_close_udpL(doc) -> + []; +api_b_open_and_close_udpL(_Config) when is_list(_Config) -> + ?TT(?SECS(5)), + tc_try(api_b_open_and_close_udpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + InitState = #{domain => local, + type => dgram, + protocol => default}, + ok = api_b_open_and_close(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically open (create) and close an Unix Domain stream (TCP) socket. +%% With some extra checks... +api_b_open_and_close_tcpL(suite) -> + []; +api_b_open_and_close_tcpL(doc) -> + []; +api_b_open_and_close_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(5)), + tc_try(api_b_open_and_close_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + InitState = #{domain => local, + type => stream, + protocol => default}, + ok = api_b_open_and_close(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + api_b_open_and_close(InitState) -> Seq = [ @@ -1633,6 +1858,37 @@ api_b_sendto_and_recvfrom_udp4(_Config) when is_list(_Config) -> socket:recvfrom(Sock) end, InitState = #{domain => inet, + proto => udp, + send => Send, + recv => Recv}, + ok = api_b_send_and_recv_udp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically send and receive on an IPv4 UDP (dgram) socket using +%% sendto and recvfrom. +api_b_sendto_and_recvfrom_udpL(suite) -> + []; +api_b_sendto_and_recvfrom_udpL(doc) -> + []; +api_b_sendto_and_recvfrom_udpL(_Config) when is_list(_Config) -> + ?TT(?SECS(5)), + tc_try(api_b_sendto_and_recvfrom_udpL, + fun() -> + has_support_unix_domain_socket(), + unix_domain_socket_host_cond() + end, + fun() -> + Send = fun(Sock, Data, Dest) -> + socket:sendto(Sock, Data, Dest) + end, + Recv = fun(Sock) -> + socket:recvfrom(Sock) + end, + InitState = #{domain => local, + proto => default, send => Send, recv => Recv}, ok = api_b_send_and_recv_udp(InitState) @@ -1665,17 +1921,66 @@ api_b_sendmsg_and_recvmsg_udp4(_Config) when is_list(_Config) -> end, Recv = fun(Sock) -> %% We have some issues on old darwing... - socket:setopt(Sock, otp, debug, true), + %% socket:setopt(Sock, otp, debug, true), case socket:recvmsg(Sock) of {ok, #{addr := Source, iov := [Data]}} -> - socket:setopt(Sock, otp, debug, false), + %% socket:setopt(Sock, otp, debug, false), {ok, {Source, Data}}; {error, _} = ERROR -> ERROR end end, InitState = #{domain => inet, + proto => udp, + send => Send, + recv => Recv}, + ok = api_b_send_and_recv_udp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically send and receive on an IPv4 UDP (dgram) socket +%% using sendmsg and recvmsg. +api_b_sendmsg_and_recvmsg_udpL(suite) -> + []; +api_b_sendmsg_and_recvmsg_udpL(doc) -> + []; +api_b_sendmsg_and_recvmsg_udpL(_Config) when is_list(_Config) -> + ?TT(?SECS(5)), + tc_try(api_b_sendmsg_and_recvmsg_udpL, + fun() -> + has_support_unix_domain_socket(), + unix_domain_socket_host_cond() + end, + fun() -> + Send = fun(Sock, Data, Dest) -> + %% We need tests for this, + %% but this is not the place it. + %% CMsgHdr = #{level => ip, + %% type => tos, + %% data => reliability}, + %% CMsgHdrs = [CMsgHdr], + MsgHdr = #{addr => Dest, + %% ctrl => CMsgHdrs, + iov => [Data]}, + socket:sendmsg(Sock, MsgHdr) + end, + Recv = fun(Sock) -> + %% We have some issues on old darwing... + %% socket:setopt(Sock, otp, debug, true), + case socket:recvmsg(Sock) of + {ok, #{addr := Source, + iov := [Data]}} -> + %% socket:setopt(Sock, otp, debug, false), + {ok, {Source, Data}}; + {error, _} = ERROR -> + ERROR + end + end, + InitState = #{domain => local, + proto => default, send => Send, recv => Recv}, ok = api_b_send_and_recv_udp(InitState) @@ -1688,42 +1993,64 @@ api_b_send_and_recv_udp(InitState) -> Seq = [ #{desc => "local address", - cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, - {ok, State#{lsa => LSA}} + cmd => fun(#{domain := local = Domain} = State) -> + LSASrc = which_local_socket_addr(Domain), + LSADst = which_local_socket_addr(Domain), + {ok, State#{lsa_src => LSASrc, + lsa_dst => LSADst}}; + (#{domain := Domain} = State) -> + LSA = which_local_socket_addr(Domain), + {ok, State#{lsa_src => LSA, + lsa_dst => LSA}} end}, + #{desc => "open src socket", - cmd => fun(#{domain := Domain} = State) -> - Sock = sock_open(Domain, dgram, udp), - SASrc = sock_sockname(Sock), - {ok, State#{sock_src => Sock, sa_src => SASrc}} + cmd => fun(#{domain := Domain, + proto := Proto} = State) -> + Sock = sock_open(Domain, dgram, Proto), + {ok, State#{sock_src => Sock}} end}, #{desc => "bind src", - cmd => fun(#{sock_src := Sock, lsa := LSA}) -> - sock_bind(Sock, LSA), - ok + cmd => fun(#{sock_src := Sock, lsa_src := LSA}) -> + case socket:bind(Sock, LSA) of + {ok, _Port} -> + ?SEV_IPRINT("src bound"), + ok; + {error, Reason} = ERROR -> + ?SEV_EPRINT("src bind failed: ~p", [Reason]), + ERROR + end end}, #{desc => "sockname src socket", cmd => fun(#{sock_src := Sock} = State) -> SASrc = sock_sockname(Sock), - %% ei("src sockaddr: ~p", [SASrc]), + ?SEV_IPRINT("src sockaddr: " + "~n ~p", [SASrc]), {ok, State#{sa_src => SASrc}} end}, + #{desc => "open dst socket", - cmd => fun(#{domain := Domain} = State) -> - Sock = sock_open(Domain, dgram, udp), + cmd => fun(#{domain := Domain, + proto := Proto} = State) -> + Sock = sock_open(Domain, dgram, Proto), {ok, State#{sock_dst => Sock}} end}, #{desc => "bind dst", - cmd => fun(#{sock_dst := Sock, lsa := LSA}) -> - sock_bind(Sock, LSA), - ok + cmd => fun(#{sock_dst := Sock, lsa_dst := LSA}) -> + case socket:bind(Sock, LSA) of + {ok, _Port} -> + ?SEV_IPRINT("src bound"), + ok; + {error, Reason} = ERROR -> + ?SEV_EPRINT("src bind failed: ~p", [Reason]), + ERROR + end end}, #{desc => "sockname dst socket", cmd => fun(#{sock_dst := Sock} = State) -> SADst = sock_sockname(Sock), - %% ei("dst sockaddr: ~p", [SADst]), + ?SEV_IPRINT("dst sockaddr: " + "~n ~p", [SADst]), {ok, State#{sa_dst => SADst}} end}, #{desc => "send req (to dst)", @@ -1761,12 +2088,32 @@ api_b_send_and_recv_udp(InitState) -> end end}, #{desc => "close src socket", - cmd => fun(#{sock_src := Sock}) -> - ok = socket:close(Sock) + cmd => fun(#{domain := local, + sock_src := Sock, + lsa_src := #{path := Path}} = State) -> + ok = socket:close(Sock), + State1 = + unlink_path(Path, + fun() -> maps:remove(lsa_src, State) end, + fun() -> State end), + {ok, maps:remove(sock_src, State1)}; + (#{sock_src := Sock} = State) -> + ok = socket:close(Sock), + {ok, maps:remove(sock_src, State)} end}, #{desc => "close dst socket", - cmd => fun(#{sock_dst := Sock}) -> - ok = socket:close(Sock) + cmd => fun(#{domain := local, + sock_dst := Sock, + lsa_dst := #{path := Path}} = State) -> + ok = socket:close(Sock), + State1 = + unlink_path(Path, + fun() -> maps:remove(lsa_dst, State) end, + fun() -> State end), + {ok, maps:remove(sock_dst, State1)}; + (#{sock_dst := Sock} = State) -> + ok = socket:close(Sock), + {ok, maps:remove(sock_dst, State)} end}, %% *** We are done *** @@ -1796,6 +2143,34 @@ api_b_send_and_recv_tcp4(_Config) when is_list(_Config) -> socket:recv(Sock) end, InitState = #{domain => inet, + proto => tcp, + send => Send, + recv => Recv}, + ok = api_b_send_and_recv_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically send and receive using the "common" functions (send and recv) +%% on an Unix Domain (stream) socket (TCP). +api_b_send_and_recv_tcpL(suite) -> + []; +api_b_send_and_recv_tcpL(doc) -> + []; +api_b_send_and_recv_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(api_b_send_and_recv_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + Send = fun(Sock, Data) -> + socket:send(Sock, Data) + end, + Recv = fun(Sock) -> + socket:recv(Sock) + end, + InitState = #{domain => local, + proto => default, send => Send, recv => Recv}, ok = api_b_send_and_recv_tcp(InitState) @@ -1828,6 +2203,56 @@ api_b_sendmsg_and_recvmsg_tcp4(_Config) when is_list(_Config) -> end end, InitState = #{domain => inet, + proto => tcp, + send => Send, + recv => Recv}, + ok = api_b_send_and_recv_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically send and receive using the msg functions (sendmsg and recvmsg) +%% on an Unix Domain (stream) socket (TCP). +api_b_sendmsg_and_recvmsg_tcpL(suite) -> + []; +api_b_sendmsg_and_recvmsg_tcpL(doc) -> + []; +api_b_sendmsg_and_recvmsg_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(api_b_sendmsg_and_recvmsg_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + Send = fun(Sock, Data) -> + MsgHdr = #{iov => [Data]}, + socket:sendmsg(Sock, MsgHdr) + end, + Recv = fun(Sock) -> + case socket:recvmsg(Sock) of + %% On some platforms, the address + %% is *not* provided (e.g. FreeBSD) + {ok, #{addr := undefined, + iov := [Data]}} -> + {ok, Data}; + %% On some platforms, the address + %% *is* provided (e.g. linux) + {ok, #{addr := #{family := local}, + iov := [Data]}} -> + socket:setopt(Sock, + otp, + debug, + false), + {ok, Data}; + {error, _} = ERROR -> + socket:setopt(Sock, + otp, + debug, + false), + ERROR + end + end, + InitState = #{domain => local, + proto => default, send => Send, recv => Recv}, ok = api_b_send_and_recv_tcp(InitState) @@ -1855,13 +2280,13 @@ api_b_send_and_recv_tcp(InitState) -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{lsa => LSA}} end}, #{desc => "create listen socket", - cmd => fun(#{domain := Domain} = State) -> - case socket:open(Domain, stream, tcp) of + cmd => fun(#{domain := Domain, + proto := Proto} = State) -> + case socket:open(Domain, stream, Proto) of {ok, Sock} -> {ok, State#{lsock => Sock}}; {error, _} = ERROR -> @@ -1869,9 +2294,19 @@ api_b_send_and_recv_tcp(InitState) -> end end}, #{desc => "bind to local address", - cmd => fun(#{lsock := LSock, lsa := LSA} = State) -> + cmd => fun(#{domain := local, + lsock := LSock, + lsa := LSA} = _State) -> + case socket:bind(LSock, LSA) of + {ok, _Port} -> + ok; % We do not care about the port for local + {error, _} = ERROR -> + ERROR + end; + (#{lsock := LSock, lsa := LSA} = State) -> case socket:bind(LSock, LSA) of {ok, Port} -> + ?SEV_IPRINT("bound to port: ~w", [Port]), {ok, State#{lport => Port}}; {error, _} = ERROR -> ERROR @@ -1882,7 +2317,12 @@ api_b_send_and_recv_tcp(InitState) -> socket:listen(LSock) end}, #{desc => "announce ready (init)", - cmd => fun(#{tester := Tester, lport := Port}) -> + cmd => fun(#{domain := local, + tester := Tester, lsa := #{path := Path}}) -> + ?SEV_ANNOUNCE_READY(Tester, init, Path), + ok; + (#{tester := Tester, lport := Port}) -> + %% This is actually not used for unix domain socket ?SEV_ANNOUNCE_READY(Tester, init, Port), ok end}, @@ -1946,12 +2386,29 @@ api_b_send_and_recv_tcp(InitState) -> end end}, #{desc => "close connection socket", - cmd => fun(#{csock := Sock}) -> - socket:close(Sock) + cmd => fun(#{csock := Sock} = State) -> + ok = socket:close(Sock), + {ok, maps:remove(csock, State)} end}, #{desc => "close listen socket", - cmd => fun(#{lsock := Sock}) -> - socket:close(Sock) + cmd => fun(#{domain := local, + lsock := Sock, + local_sa := #{path := Path}} = State) -> + ok = socket:close(Sock), + State1 = + unlink_path(Path, + fun() -> + maps:remove(local_sa, State) + end, + fun() -> State end), + {ok, maps:remove(lsock, State1)}; + (#{lsock := LSock} = State) -> + case socket:close(LSock) of + ok -> + {ok, maps:remove(lsock, State)}; + {error, _} = ERROR -> + ERROR + end end}, %% *** We are done *** @@ -1962,7 +2419,10 @@ api_b_send_and_recv_tcp(InitState) -> [ %% *** Wait for start order *** #{desc => "await start (from tester)", - cmd => fun(State) -> + cmd => fun(#{domain := local} = State) -> + {Tester, Path} = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester, server_path => Path}}; + (State) -> {Tester, Port} = ?SEV_AWAIT_START(), {ok, State#{tester => Tester, server_port => Port}} end}, @@ -1974,16 +2434,20 @@ api_b_send_and_recv_tcp(InitState) -> %% *** The init part *** #{desc => "which server (local) address", - cmd => fun(#{domain := Domain, server_port := Port} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, - addr => LAddr}, - SSA = LSA#{port => Port}, + cmd => fun(#{domain := local = Domain, + server_path := Path} = State) -> + LSA = which_local_socket_addr(Domain), + SSA = #{family => Domain, path => Path}, + {ok, State#{local_sa => LSA, server_sa => SSA}}; + (#{domain := Domain, server_port := Port} = State) -> + LSA = which_local_socket_addr(Domain), + SSA = LSA#{port => Port}, {ok, State#{local_sa => LSA, server_sa => SSA}} end}, #{desc => "create socket", - cmd => fun(#{domain := Domain} = State) -> - case socket:open(Domain, stream, tcp) of + cmd => fun(#{domain := Domain, + proto := Proto} = State) -> + case socket:open(Domain, stream, Proto) of {ok, Sock} -> {ok, State#{sock => Sock}}; {error, _} = ERROR -> @@ -2054,8 +2518,20 @@ api_b_send_and_recv_tcp(InitState) -> end end}, #{desc => "close socket", - cmd => fun(#{sock := Sock}) -> - socket:close(Sock) + cmd => fun(#{domain := local, + sock := Sock, + local_sa := #{path := Path}} = State) -> + ok = socket:close(Sock), + State1 = + unlink_path(Path, + fun() -> + maps:remove(local_sa, State) + end, + fun() -> State end), + {ok, maps:remove(sock, State1)}; + (#{sock := Sock} = State) -> + ok = socket:close(Sock), + {ok, maps:remove(sock, State)} end}, %% *** We are done *** @@ -2105,11 +2581,7 @@ api_b_send_and_recv_tcp(InitState) -> ?SEV_ANNOUNCE_CONTINUE(Server, accept), ok end}, - #{desc => "sleep", - cmd => fun(_) -> - ?SLEEP(?SECS(1)), - ok - end}, + ?SEV_SLEEP(?SECS(1)), #{desc => "order client to continue (with connect)", cmd => fun(#{client := Client} = _State) -> ?SEV_ANNOUNCE_CONTINUE(Client, connect), @@ -2196,6 +2668,3852 @@ api_b_send_and_recv_tcp(InitState) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically establish a TCP connection via an async connect. + +api_a_connect_tcp4(suite) -> + []; +api_a_connect_tcp4(doc) -> + []; +api_a_connect_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(api_a_connect_tcp4, + fun() -> + Connect = fun(Sock, SockAddr) -> + socket:connect(Sock, SockAddr, nowait) + end, + Send = fun(Sock, Data) -> + socket:send(Sock, Data) + end, + Recv = fun(Sock) -> + socket:recv(Sock) + end, + InitState = #{domain => inet, + connect => Connect, + send => Send, + recv => Recv}, + ok = api_a_connect_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +api_a_connect_tcp(InitState) -> + process_flag(trap_exit, true), + ServerSeq = + [ + %% *** Wait for start order *** + #{desc => "await start (from tester)", + cmd => fun(State) -> + Tester = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester}) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** Init part *** + #{desc => "which local address", + cmd => fun(#{domain := Domain} = State) -> + LSA = which_local_socket_addr(Domain), + {ok, State#{lsa => LSA}} + end}, + #{desc => "create listen socket", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, stream, tcp) of + {ok, Sock} -> + {ok, State#{lsock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "bind to local address", + cmd => fun(#{lsock := LSock, lsa := LSA} = State) -> + case socket:bind(LSock, LSA) of + {ok, Port} -> + {ok, State#{lport => Port}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "make listen socket", + cmd => fun(#{lsock := LSock}) -> + socket:listen(LSock) + end}, + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester, lport := Port}) -> + ?SEV_ANNOUNCE_READY(Tester, init, Port), + ok + end}, + + %% The actual test + #{desc => "await continue (accept)", + cmd => fun(#{tester := Tester}) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, accept) + end}, + #{desc => "await connection", + cmd => fun(#{lsock := LSock} = State) -> + case socket:accept(LSock) of + {ok, Sock} -> + ?SEV_IPRINT("accepted: ~n ~p", [Sock]), + {ok, State#{csock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (accept)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, accept), + ok + end}, + + #{desc => "await continue (recv_req)", + cmd => fun(#{tester := Tester}) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, recv_req) + end}, + #{desc => "recv req", + cmd => fun(#{csock := Sock, recv := Recv}) -> + case Recv(Sock) of + {ok, ?BASIC_REQ} -> + ok; + {ok, UnexpData} -> + {error, {unexpected_data, UnexpData}}; + {error, _} = ERROR -> + %% At the moment there is no way to get + %% status or state for the socket... + ERROR + end + end}, + #{desc => "announce ready (recv_req)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, recv_req), + ok + end}, + #{desc => "await continue (send_rep)", + cmd => fun(#{tester := Tester}) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, send_rep) + end}, + #{desc => "send rep", + cmd => fun(#{csock := Sock, send := Send}) -> + Send(Sock, ?BASIC_REP) + end}, + #{desc => "announce ready (send_rep)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, send_rep), + ok + end}, + + + %% *** Termination *** + #{desc => "await terminate", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + {ok, maps:remove(tester, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "close connection socket", + cmd => fun(#{csock := Sock} = State) -> + ok = socket:close(Sock), + {ok, maps:remove(csock, State)} + end}, + #{desc => "close listen socket", + cmd => fun(#{lsock := Sock} = State) -> + ok = socket:close(Sock), + {ok, maps:remove(lsock, State)} + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + ClientSeq = + [ + %% *** Wait for start order *** + #{desc => "await start (from tester)", + cmd => fun(State) -> + {Tester, Port} = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester, server_port => Port}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester}) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** The init part *** + #{desc => "which server (local) address", + cmd => fun(#{domain := Domain, server_port := Port} = State) -> + LSA = which_local_socket_addr(Domain), + SSA = LSA#{port => Port}, + {ok, State#{local_sa => LSA, server_sa => SSA}} + end}, + #{desc => "create socket", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, stream, tcp) of + {ok, Sock} -> + {ok, State#{sock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "bind to local address", + cmd => fun(#{sock := Sock, local_sa := LSA} = _State) -> + case socket:bind(Sock, LSA) of + {ok, _Port} -> + ok; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, init), + ok + end}, + + %% *** The actual test *** + #{desc => "await continue (async connect)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, async_connect) + end}, + #{desc => "connect (async) to server", + cmd => fun(#{sock := Sock, + server_sa := SSA, + connect := Connect} = State) -> + case Connect(Sock, SSA) of + ok -> + ?SEV_IPRINT("ok -> " + "unexpected success => SKIP", + []), + {skip, unexpected_success}; + {select, {select_info, ST, SR}} -> + ?SEV_IPRINT("select ->" + "~n tag: ~p" + "~n ref: ~p", [ST, SR]), + {ok, State#{connect_stag => ST, + connect_sref => SR}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (connect select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, connect_select), + ok + end}, + #{desc => "await select message", + cmd => fun(#{sock := Sock, connect_sref := Ref}) -> + receive + {'$socket', Sock, select, Ref} -> + ?SEV_IPRINT("select message ->" + "~n ref: ~p", [Ref]), + ok + after 5000 -> + ?SEV_EPRINT("timeout: " + "~n message queue: ~p", + [mq()]), + {error, timeout} + end + end}, + #{desc => "announce ready (select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, select), + ok + end}, + #{desc => "connect (async) to server", + cmd => fun(#{sock := Sock, server_sa := SSA, connect := Connect}) -> + case Connect(Sock, SSA) of + ok -> + ok; + {select, SelectInfo} -> + {error, {unexpected_select, SelectInfo}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (connect)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, connect), + ok + end}, + #{desc => "get peername", + cmd => fun(#{sock := Sock} = _State) -> + case socket:peername(Sock) of + {ok, SockAddr} -> + ?SEV_IPRINT("Peer Name: ~p", [SockAddr]), + ok; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "await continue (send_req)", + cmd => fun(#{tester := Tester}) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, send_req) + end}, + #{desc => "send req", + cmd => fun(#{sock := Sock, send := Send}) -> + Send(Sock, ?BASIC_REQ) + end}, + #{desc => "announce ready (send_req)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, send_req), + ok + end}, + #{desc => "await continue (recv_rep)", + cmd => fun(#{tester := Tester}) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, recv_rep) + end}, + #{desc => "recv rep", + cmd => fun(#{sock := Sock, recv := Recv}) -> + case Recv(Sock) of + {ok, ?BASIC_REP} -> + ok; + {ok, UnexpData} -> + {error, {unexpected_data, UnexpData}}; + {error, _} = ERROR -> + %% At the moment there is no way to get + %% status or state for the socket... + ERROR + end + end}, + #{desc => "announce ready (recv_rep)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, recv_rep), + ok + end}, + + %% *** Termination *** + #{desc => "await terminate", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + {ok, maps:remove(tester, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "close socket", + cmd => fun(#{sock := Sock} = State) -> + ok = socket:close(Sock), + State2 = maps:remove(sock, State), + State3 = maps:remove(connect_stag, State2), + State4 = maps:remove(connect_sref, State3), + {ok, State4} + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + TesterSeq = + [ + %% *** Init part *** + #{desc => "monitor server", + cmd => fun(#{server := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "monitor client", + cmd => fun(#{client := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + + %% Start the server + #{desc => "order server start", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await server ready (init)", + cmd => fun(#{server := Pid} = State) -> + {ok, Port} = ?SEV_AWAIT_READY(Pid, server, init), + {ok, State#{server_port => Port}} + end}, + + %% Start the client + #{desc => "order client start", + cmd => fun(#{client := Pid, server_port := Port} = _State) -> + ?SEV_ANNOUNCE_START(Pid, Port), + ok + end}, + #{desc => "await client ready (init)", + cmd => fun(#{client := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client, init) + end}, + + + %% *** The actual test *** + #{desc => "order client to continue (async connect)", + cmd => fun(#{client := Client} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Client, async_connect), + ok + end}, + #{desc => "await client ready (connect select)", + cmd => fun(#{client := Client} = _State) -> + ?SEV_AWAIT_READY(Client, client, connect_select) + end}, + + ?SEV_SLEEP(?SECS(1)), + + #{desc => "order server to continue (with accept)", + cmd => fun(#{server := Server} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Server, accept), + ok + end}, + #{desc => "await client ready (select)", + cmd => fun(#{client := Client} = _State) -> + ?SEV_AWAIT_READY(Client, client, select) + end}, + #{desc => "await client ready (connect)", + cmd => fun(#{client := Client} = _State) -> + ?SEV_AWAIT_READY(Client, client, connect) + end}, + #{desc => "await server ready (accept)", + cmd => fun(#{server := Server} = _State) -> + ?SEV_AWAIT_READY(Server, server, accept) + end}, + + ?SEV_SLEEP(?SECS(1)), + + #{desc => "order server to recv test req (recv req)", + cmd => fun(#{server := Server} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Server, recv_req), + ok + end}, + #{desc => "order client to send test req (send req)", + cmd => fun(#{client := Client} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Client, send_req), + ok + end}, + #{desc => "await client ready (send_req)", + cmd => fun(#{client := Client} = _State) -> + ?SEV_AWAIT_READY(Client, client, send_req) + end}, + #{desc => "await server ready (recv_req)", + cmd => fun(#{server := Server} = _State) -> + ?SEV_AWAIT_READY(Server, server, recv_req) + end}, + #{desc => "order client to recv test rep (send rep)", + cmd => fun(#{client := Client} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Client, recv_rep), + ok + end}, + #{desc => "order server to send test rep (send rep)", + cmd => fun(#{server := Server} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Server, send_rep), + ok + end}, + #{desc => "await server ready (send_rep)", + cmd => fun(#{server := Server} = _State) -> + ?SEV_AWAIT_READY(Server, server, send_rep) + end}, + #{desc => "await client ready (recv_rep)", + cmd => fun(#{client := Client} = _State) -> + ?SEV_AWAIT_READY(Client, client, recv_rep) + end}, + + + %% *** Termination *** + #{desc => "order client to terminate", + cmd => fun(#{client := Client} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Client), + ok + end}, + #{desc => "await client termination", + cmd => fun(#{client := Client} = State) -> + ?SEV_AWAIT_TERMINATION(Client), + State1 = maps:remove(client, State), + {ok, State1} + end}, + #{desc => "order server to terminate", + cmd => fun(#{server := Server} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Server), + ok + end}, + #{desc => "await server termination", + cmd => fun(#{server := Server} = State) -> + ?SEV_AWAIT_TERMINATION(Server), + State1 = maps:remove(server, State), + {ok, State1} + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + i("start server evaluator"), + Server = ?SEV_START("server", ServerSeq, InitState), + + i("start client evaluator"), + Client = ?SEV_START("client", ClientSeq, InitState), + i("await evaluator(s)"), + + i("start tester evaluator"), + TesterInitState = #{server => Server#ev.pid, + client => Client#ev.pid}, + Tester = ?SEV_START("tester", TesterSeq, TesterInitState), + + ok = ?SEV_AWAIT_FINISH([Server, Client, Tester]). + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically send and receive on an IPv4 UDP (dgram) socket using +%% sendto and recvfrom. But we try to be async. That is, we use +%% the 'nowait' value for the Timeout argument (and await the eventual +%% select message). Note that we only do this for the recvfrom, +%% since its much more difficult to "arrange" for sendto. +%% +api_a_sendto_and_recvfrom_udp4(suite) -> + []; +api_a_sendto_and_recvfrom_udp4(doc) -> + []; +api_a_sendto_and_recvfrom_udp4(_Config) when is_list(_Config) -> + ?TT(?SECS(5)), + tc_try(api_a_sendto_and_recvfrom_udp4, + fun() -> + Send = fun(Sock, Data, Dest) -> + socket:sendto(Sock, Data, Dest) + end, + Recv = fun(Sock) -> + socket:recvfrom(Sock, 0, nowait) + end, + InitState = #{domain => inet, + send => Send, + recv => Recv}, + ok = api_a_send_and_recv_udp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically send and receive on an IPv4 UDP (dgram) socket using +%% sendto and recvfrom. But we try to be async. That is, we use +%% the 'nowait' value for the Timeout argument (and await the eventual +%% select message). Note that we only do this for the recvmsg, +%% since its much more difficult to "arrange" for sendmsg. +%% +api_a_sendmsg_and_recvmsg_udp4(suite) -> + []; +api_a_sendmsg_and_recvmsg_udp4(doc) -> + []; +api_a_sendmsg_and_recvmsg_udp4(_Config) when is_list(_Config) -> + ?TT(?SECS(5)), + tc_try(api_a_sendmsg_and_recvmsg_udp4, + fun() -> + Send = fun(Sock, Data, Dest) -> + MsgHdr = #{addr => Dest, + %% ctrl => CMsgHdrs, + iov => [Data]}, + socket:sendmsg(Sock, MsgHdr) + end, + Recv = fun(Sock) -> + case socket:recvmsg(Sock, nowait) of + {ok, #{addr := Source, + iov := [Data]}} -> + {ok, {Source, Data}}; + {ok, _} = OK -> + OK; + {select, _} = SELECT -> + SELECT; + {error, _} = ERROR -> + ERROR + end + end, + InitState = #{domain => inet, + send => Send, + recv => Recv}, + ok = api_a_send_and_recv_udp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +api_a_send_and_recv_udp(InitState) -> + ServerSeq = + [ + %% *** Wait for start order part *** + #{desc => "await start", + cmd => fun(State) -> + Tester = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester} = _State) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** Init part *** + #{desc => "which local address", + cmd => fun(#{domain := Domain} = State) -> + LSA = which_local_socket_addr(Domain), + {ok, State#{local_sa => LSA}} + end}, + #{desc => "create socket", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, dgram, udp) of + {ok, Sock} -> + {ok, State#{sock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "bind socket (to local address)", + cmd => fun(#{sock := Sock, local_sa := LSA} = State) -> + case socket:bind(Sock, LSA) of + {ok, Port} -> + {ok, State#{port => Port}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester, local_sa := LSA, port := Port}) -> + ServerSA = LSA#{port => Port}, + ?SEV_ANNOUNCE_READY(Tester, init, ServerSA), + ok + end}, + + %% The actual test + #{desc => "await continue (recv)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, recv) + end}, + #{desc => "try recv request (with nowait, expect select)", + cmd => fun(#{sock := Sock, recv := Recv} = State) -> + case Recv(Sock) of + {select, {select_info, Tag, RecvRef}} -> + ?SEV_IPRINT("expected select: " + "~n Tag: ~p" + "~n Ref: ~p", [Tag, RecvRef]), + {ok, State#{recv_stag => Tag, + recv_sref => RecvRef}}; + {ok, X} -> + {error, {unexpected_succes, X}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (recv_select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, recv_select), + ok + end}, + #{desc => "await select message", + cmd => fun(#{sock := Sock, recv_sref := RecvRef}) -> + receive + {'$socket', Sock, select, RecvRef} -> + ok + after 5000 -> + ?SEV_EPRINT("message queue: ~p", [mq()]), + {error, timeout} + end + end}, + #{desc => "announce ready (select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, select), + ok + end}, + #{desc => "now read the data (request)", + cmd => fun(#{sock := Sock, recv := Recv} = State) -> + case Recv(Sock) of + {ok, {Src, ?BASIC_REQ}} -> + {ok, State#{req_src => Src}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (recv request)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, recv_req), + ok + end}, + + #{desc => "await continue (send reply)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, send_reply) + end}, + #{desc => "send reply", + cmd => fun(#{sock := Sock, req_src := Src, send := Send}) -> + Send(Sock, ?BASIC_REP, Src) + end}, + #{desc => "announce ready (send)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, send), + ok + end}, + + %% Termination + #{desc => "await terminate (from tester)", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + State2 = maps:remove(tester, State), + State3 = maps:remove(recv_stag, State2), + State4 = maps:remove(recv_sref, State3), + State5 = maps:remove(req_src, State4), + {ok, State5}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "close socket", + cmd => fun(#{sock := Sock} = State) -> + ok = socket:close(Sock), + {ok, maps:remove(sock, State)} + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + + ClientSeq = + [ + %% *** Wait for start order part *** + #{desc => "await start", + cmd => fun(State) -> + {Tester, ServerSA} = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester, + server_sa => ServerSA}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester} = _State) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** Init part *** + #{desc => "local address", + cmd => fun(#{domain := Domain} = State) -> + LSA = which_local_socket_addr(Domain), + {ok, State#{lsa => LSA}} + end}, + #{desc => "open socket", + cmd => fun(#{domain := Domain} = State) -> + Sock = sock_open(Domain, dgram, udp), + SA = sock_sockname(Sock), + {ok, State#{sock => Sock, sa => SA}} + end}, + #{desc => "bind socket (to local address)", + cmd => fun(#{sock := Sock, lsa := LSA}) -> + case socket:bind(Sock, LSA) of + {ok, _Port} -> + ok; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, init), + ok + end}, + + %% The actual test + #{desc => "await continue (send request)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, send_req) + end}, + #{desc => "send request", + cmd => fun(#{sock := Sock, server_sa := Server, send := Send}) -> + Send(Sock, ?BASIC_REQ, Server) + end}, + #{desc => "announce ready (send request)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, send_req), + ok + end}, + #{desc => "await continue (recv)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, recv) + end}, + #{desc => "try recv reply (with nowait)", + cmd => fun(#{sock := Sock, recv := Recv} = State) -> + case Recv(Sock) of + {select, {select_info, Tag, RecvRef}} -> + {ok, State#{recv_stag => Tag, + recv_sref => RecvRef}}; + {ok, X} -> + {error, {unexpected_select_info, X}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (recv_select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, recv_select), + ok + end}, + #{desc => "await select message", + cmd => fun(#{sock := Sock, recv_sref := RecvRef}) -> + receive + {'$socket', Sock, select, RecvRef} -> + ok + end + end}, + #{desc => "announce ready (select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, select), + ok + end}, + #{desc => "now read the data (reply)", + cmd => fun(#{sock := Sock, recv := Recv}) -> + case Recv(Sock) of + {ok, {_Src, ?BASIC_REP}} -> + ok; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (recv reply)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, recv_rep), + ok + end}, + + %% Termination + #{desc => "await terminate (from tester)", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + State2 = maps:remove(tester, State), + State3 = maps:remove(recv_stag, State2), + State4 = maps:remove(recv_sref, State3), + {ok, State4}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "close socket", + cmd => fun(#{sock := Sock} = State) -> + ok = socket:close(Sock), + {ok, maps:remove(sock, State)} + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + + TesterSeq = + [ + %% *** Init part *** + #{desc => "monitor server", + cmd => fun(#{server := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "monitor client", + cmd => fun(#{client := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + + %% Start the server + #{desc => "order server start", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await server ready (init)", + cmd => fun(#{server := Pid} = State) -> + {ok, ServerSA} = ?SEV_AWAIT_READY(Pid, server, init), + {ok, State#{server_sa => ServerSA}} + end}, + + %% Start the client + #{desc => "order client start", + cmd => fun(#{client := Pid, + server_sa := ServerSA} = _State) -> + ?SEV_ANNOUNCE_START(Pid, ServerSA), + ok + end}, + #{desc => "await client ready (init)", + cmd => fun(#{client := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client, init) + end}, + + %% The actual test + #{desc => "order server continue (recv)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, recv), + ok + end}, + #{desc => "await server ready (recv_select)", + cmd => fun(#{server := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, server, recv_select) + end}, + + #{desc => "order client continue (send request)", + cmd => fun(#{client := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, send_req), + ok + end}, + #{desc => "await client ready (send request)", + cmd => fun(#{client := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client, send_req) + end}, + #{desc => "await server ready (select)", + cmd => fun(#{server := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, server, select) + end}, + #{desc => "await server ready (recv request)", + cmd => fun(#{server := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, server, recv_req) + end}, + + #{desc => "order client continue (recv)", + cmd => fun(#{client := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, recv), + ok + end}, + #{desc => "await client ready (recv_select)", + cmd => fun(#{client := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client, recv_select) + end}, + #{desc => "order server continue (send reply)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, send_reply), + ok + end}, + #{desc => "await server ready (send)", + cmd => fun(#{server := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, server, send) + end}, + #{desc => "await client ready (select)", + cmd => fun(#{client := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client, select) + end}, + #{desc => "await client ready (recv reply)", + cmd => fun(#{client := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client, recv_rep) + end}, + + %% Terminations + #{desc => "order client to terminate", + cmd => fun(#{client := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await client termination", + cmd => fun(#{client := Pid} = State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + State1 = maps:remove(client, State), + {ok, State1}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "order server to terminate", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await server termination", + cmd => fun(#{server := Pid} = State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + State1 = maps:remove(server, State), + {ok, State1}; + {error, _} = ERROR -> + ERROR + end + end}, + + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + i("start server evaluator"), + ServerInitState = InitState, + Server = ?SEV_START("server", ServerSeq, ServerInitState), + + i("start client evaluator(s)"), + ClientInitState = InitState, + Client = ?SEV_START("client", ClientSeq, ClientInitState), + + i("start 'tester' evaluator"), + TesterInitState = #{server => Server#ev.pid, + client => Client#ev.pid}, + Tester = ?SEV_START("tester", TesterSeq, TesterInitState), + + i("await evaluator"), + ok = ?SEV_AWAIT_FINISH([Server, Client, Tester]). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically send and receive using the "common" functions (send and recv) +%% on an IPv4 TCP (stream) socket. But we try to be async. That is, we use +%% the 'nowait' value for the Timeout argument (and await the eventual +%% select message). Note that we only do this for the recv, +%% since its much more difficult to "arrange" for send. +%% We *also* test async for accept. +api_a_send_and_recv_tcp4(suite) -> + []; +api_a_send_and_recv_tcp4(doc) -> + []; +api_a_send_and_recv_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(api_a_send_and_recv_tcp4, + fun() -> + Send = fun(Sock, Data) -> + socket:send(Sock, Data) + end, + Recv = fun(Sock) -> + socket:recv(Sock, 0, nowait) + end, + InitState = #{domain => inet, + send => Send, + recv => Recv}, + ok = api_a_send_and_recv_tcp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically send and receive using the msg functions (sendmsg and recvmsg) +%% on an IPv4 TCP (stream) socket. But we try to be async. That is, we use +%% the 'nowait' value for the Timeout argument (and await the eventual +%% select message). Note that we only do this for the recvmsg, +%% since its much more difficult to "arrange" for sendmsg. +%% We *also* test async for accept. +api_a_sendmsg_and_recvmsg_tcp4(suite) -> + []; +api_a_sendmsg_and_recvmsg_tcp4(doc) -> + []; +api_a_sendmsg_and_recvmsg_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(api_a_sendmsg_and_recvmsg_tcp4, + fun() -> + Send = fun(Sock, Data) -> + MsgHdr = #{iov => [Data]}, + socket:sendmsg(Sock, MsgHdr) + end, + Recv = fun(Sock) -> + case socket:recvmsg(Sock, nowait) of + {ok, #{addr := undefined, + iov := [Data]}} -> + {ok, Data}; + {ok, _} = OK -> + OK; + {select, _} = SELECT -> + SELECT; + {error, _} = ERROR -> + ERROR + end + end, + InitState = #{domain => inet, + send => Send, + recv => Recv}, + ok = api_a_send_and_recv_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +api_a_send_and_recv_tcp(InitState) -> + process_flag(trap_exit, true), + ServerSeq = + [ + %% *** Wait for start order *** + #{desc => "await start (from tester)", + cmd => fun(State) -> + Tester = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester}) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** Init part *** + #{desc => "which local address", + cmd => fun(#{domain := Domain} = State) -> + LSA = which_local_socket_addr(Domain), + {ok, State#{lsa => LSA}} + end}, + #{desc => "create listen socket", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, stream, tcp) of + {ok, Sock} -> + {ok, State#{lsock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "bind to local address", + cmd => fun(#{lsock := LSock, lsa := LSA} = State) -> + case socket:bind(LSock, LSA) of + {ok, Port} -> + {ok, State#{lport => Port}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "make listen socket", + cmd => fun(#{lsock := LSock}) -> + socket:listen(LSock) + end}, + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester, lport := Port}) -> + ?SEV_ANNOUNCE_READY(Tester, init, Port), + ok + end}, + + %% The actual test + #{desc => "await continue (accept)", + cmd => fun(#{tester := Tester}) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, accept) + end}, + #{desc => "await connection (nowait)", + cmd => fun(#{lsock := LSock} = State) -> + case socket:accept(LSock, nowait) of + {select, {select_info, Tag, Ref}} -> + ?SEV_IPRINT("accept select: " + "~n Tag: ~p" + "~n Ref: ~p", [Tag, Ref]), + {ok, State#{accept_stag => Tag, + accept_sref => Ref}}; + {ok, X} -> + {error, {unexpected_select_info, X}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (accept_select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, accept_select), + ok + end}, + #{desc => "await select message", + cmd => fun(#{lsock := Sock, accept_sref := Ref}) -> + receive + {'$socket', Sock, select, Ref} -> + ok + end + end}, + #{desc => "announce ready (select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, select), + ok + end}, + #{desc => "await connection (again)", + cmd => fun(#{lsock := LSock} = State) -> + case socket:accept(LSock, nowait) of + {ok, Sock} -> + ?SEV_IPRINT("accepted: " + "~n Sock: ~p", [Sock]), + {ok, State#{csock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (accept)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, accept), + ok + end}, + + #{desc => "await continue (recv request)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, recv_req) + end}, + #{desc => "try recv request (with nowait, expect select)", + cmd => fun(#{csock := Sock, recv := Recv} = State) -> + case Recv(Sock) of + {select, {select_info, Tag, Ref}} -> + ?SEV_IPRINT("recv select: " + "~n Tag: ~p" + "~n Ref: ~p", [Tag, Ref]), + {ok, State#{recv_stag => Tag, + recv_sref => Ref}}; + {ok, X} -> + {error, {unexpected_select_info, X}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (recv_select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, recv_select), + ok + end}, + #{desc => "await select message", + cmd => fun(#{csock := Sock, recv_sref := RecvRef}) -> + receive + {'$socket', Sock, select, RecvRef} -> + ok + end + end}, + #{desc => "announce ready (select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, select), + ok + end}, + #{desc => "now read the data (request)", + cmd => fun(#{csock := Sock, recv := Recv} = _State) -> + case Recv(Sock) of + {ok, ?BASIC_REQ} -> + ok; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (recv request)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, recv_req), + ok + end}, + + #{desc => "await continue (send reply)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, send_rep) + end}, + #{desc => "send reply", + cmd => fun(#{csock := Sock, send := Send}) -> + Send(Sock, ?BASIC_REP) + end}, + #{desc => "announce ready (send reply)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, send_rep), + ok + end}, + + %% *** Termination *** + #{desc => "await terminate", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + {ok, maps:remove(tester, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "close connection socket", + cmd => fun(#{csock := Sock}) -> + socket:close(Sock) + end}, + #{desc => "close listen socket", + cmd => fun(#{lsock := Sock}) -> + socket:close(Sock) + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + + ClientSeq = + [ + %% *** Wait for start order *** + #{desc => "await start (from tester)", + cmd => fun(State) -> + {Tester, Port} = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester, server_port => Port}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester}) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** The init part *** + #{desc => "which server (local) address", + cmd => fun(#{domain := Domain, server_port := Port} = State) -> + LSA = which_local_socket_addr(Domain), + SSA = LSA#{port => Port}, + {ok, State#{local_sa => LSA, server_sa => SSA}} + end}, + #{desc => "create socket", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, stream, tcp) of + {ok, Sock} -> + {ok, State#{sock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "bind to local address", + cmd => fun(#{sock := Sock, local_sa := LSA} = _State) -> + case socket:bind(Sock, LSA) of + {ok, _Port} -> + ok; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, init), + ok + end}, + + %% *** The actual test *** + #{desc => "await continue (connect)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, connect) + end}, + #{desc => "connect to server", + cmd => fun(#{sock := Sock, server_sa := SSA}) -> + socket:connect(Sock, SSA) + end}, + #{desc => "announce ready (connect)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, connect), + ok + end}, + + #{desc => "await continue (send request)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, send_req) + end}, + #{desc => "send request (to server)", + cmd => fun(#{sock := Sock, send := Send}) -> + ok = Send(Sock, ?BASIC_REQ) + end}, + #{desc => "announce ready (send request)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, send_req), + ok + end}, + + #{desc => "try recv reply (with nowait, expect select)", + cmd => fun(#{sock := Sock, recv := Recv} = State) -> + case Recv(Sock) of + {select, {select_info, Tag, Ref}} -> + ?SEV_IPRINT("recv select: " + "~n Tag: ~p" + "~n Ref: ~p", [Tag, Ref]), + {ok, State#{recv_stag => Tag, + recv_sref => Ref}}; + {ok, X} -> + {error, {unexpected_select_info, X}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (recv_select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, recv_select), + ok + end}, + #{desc => "await select message", + cmd => fun(#{sock := Sock, recv_sref := RecvRef}) -> + receive + {'$socket', Sock, select, RecvRef} -> + ok + end + end}, + #{desc => "announce ready (select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, select), + ok + end}, + #{desc => "now read the data (reply)", + cmd => fun(#{sock := Sock, recv := Recv}) -> + {ok, ?BASIC_REP} = Recv(Sock), + ok + end}, + #{desc => "announce ready (recv reply)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, recv_rep), + ok + end}, + + %% *** Termination *** + #{desc => "await terminate", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + {ok, maps:remove(tester, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "close socket", + cmd => fun(#{sock := Sock}) -> + socket:close(Sock) + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + TesterSeq = + [ + %% *** Init part *** + #{desc => "monitor server", + cmd => fun(#{server := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "monitor client", + cmd => fun(#{client := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + + %% Start the server + #{desc => "order server start", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await server ready (init)", + cmd => fun(#{server := Pid} = State) -> + {ok, Port} = ?SEV_AWAIT_READY(Pid, server, init), + {ok, State#{server_port => Port}} + end}, + + %% Start the client + #{desc => "order client start", + cmd => fun(#{client := Pid, server_port := Port} = _State) -> + ?SEV_ANNOUNCE_START(Pid, Port), + ok + end}, + #{desc => "await client ready (init)", + cmd => fun(#{client := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client, init) + end}, + + %% *** The actual test *** + #{desc => "order server to continue (with accept)", + cmd => fun(#{server := Server} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Server, accept), + ok + end}, + #{desc => "await server ready (accept select)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_AWAIT_READY(Pid, server, accept_select) + end}, + #{desc => "order client to continue (with connect)", + cmd => fun(#{client := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, connect), + ok + end}, + #{desc => "await server ready (select)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_AWAIT_READY(Pid, server, select) + end}, + #{desc => "await server ready (accept)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_AWAIT_READY(Pid, server, accept) + end}, + #{desc => "await client ready (connect)", + cmd => fun(#{client := Pid} = _State) -> + ?SEV_AWAIT_READY(Pid, client, connect) + end}, + + #{desc => "order server to continue (recv request)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, recv_req), + ok + end}, + #{desc => "await server ready (recv select)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_AWAIT_READY(Pid, server, recv_select) + end}, + #{desc => "order client to continue (send request)", + cmd => fun(#{client := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, send_req), + ok + end}, + #{desc => "await client ready (send request)", + cmd => fun(#{client := Client} = _State) -> + ?SEV_AWAIT_READY(Client, client, send_req) + end}, + #{desc => "await server ready (select)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_AWAIT_READY(Pid, server, select) + end}, + #{desc => "await server ready (recv request)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_AWAIT_READY(Pid, server, recv_req) + end}, + + #{desc => "order client to continue (recv reply)", + cmd => fun(#{client := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, recv_rep), + ok + end}, + #{desc => "await client ready (recv select)", + cmd => fun(#{client := Pid} = _State) -> + ?SEV_AWAIT_READY(Pid, client, recv_select) + end}, + #{desc => "order server to continue (send reply)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, send_rep), + ok + end}, + #{desc => "await server ready (send reply)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_AWAIT_READY(Pid, server, send_rep) + end}, + #{desc => "await client ready (select)", + cmd => fun(#{client := Pid} = _State) -> + ?SEV_AWAIT_READY(Pid, client, select) + end}, + #{desc => "await client ready (reply recv)", + cmd => fun(#{client := Client} = _State) -> + ?SEV_AWAIT_READY(Client, client, recv_rep) + end}, + + + %% *** Termination *** + #{desc => "order client to terminate", + cmd => fun(#{client := Client} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Client), + ok + end}, + #{desc => "await client termination", + cmd => fun(#{client := Client} = State) -> + ?SEV_AWAIT_TERMINATION(Client), + State1 = maps:remove(client, State), + {ok, State1} + end}, + #{desc => "order server to terminate", + cmd => fun(#{server := Server} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Server), + ok + end}, + #{desc => "await server termination", + cmd => fun(#{server := Server} = State) -> + ?SEV_AWAIT_TERMINATION(Server), + State1 = maps:remove(server, State), + {ok, State1} + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + i("start server evaluator"), + Server = ?SEV_START("server", ServerSeq, InitState), + + i("start client evaluator"), + Client = ?SEV_START("client", ClientSeq, InitState), + + i("start tester evaluator"), + TesterInitState = #{server => Server#ev.pid, + client => Client#ev.pid}, + Tester = ?SEV_START("tester", TesterSeq, TesterInitState), + + i("await evaluator(s)"), + ok = ?SEV_AWAIT_FINISH([Server, Client, Tester]). + + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically we make an async (Timeout = nowait) call to recvfrom, +%% wait some time and then cancel. +%% +api_a_recvfrom_cancel_udp4(suite) -> + []; +api_a_recvfrom_cancel_udp4(doc) -> + []; +api_a_recvfrom_cancel_udp4(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(api_a_recvfrom_cancel_udp4, + fun() -> + Recv = fun(Sock) -> + case socket:recvfrom(Sock, 0, nowait) of + {ok, _} = OK -> + OK; + {select, _} = SELECT -> + SELECT; + {error, _} = ERROR -> + ERROR + end + end, + InitState = #{domain => inet, + recv => Recv}, + ok = api_a_recv_cancel_udp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically we make an async (Timeout = nowait) call to recvmsg, +%% wait some time and then cancel. +%% +api_a_recvmsg_cancel_udp4(suite) -> + []; +api_a_recvmsg_cancel_udp4(doc) -> + []; +api_a_recvmsg_cancel_udp4(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(api_a_recvmsg_cancel_udp4, + fun() -> + Recv = fun(Sock) -> + case socket:recvmsg(Sock, nowait) of + {ok, _} = OK -> + OK; + {select, _} = SELECT -> + SELECT; + {error, _} = ERROR -> + ERROR + end + end, + InitState = #{domain => inet, + recv => Recv}, + ok = api_a_recv_cancel_udp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +api_a_recv_cancel_udp(InitState) -> + ServerSeq = + [ + %% *** Wait for start order part *** + #{desc => "await start", + cmd => fun(State) -> + Tester = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester} = _State) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** Init part *** + #{desc => "which local address", + cmd => fun(#{domain := Domain} = State) -> + LSA = which_local_socket_addr(Domain), + {ok, State#{local_sa => LSA}} + end}, + #{desc => "create socket", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, dgram, udp) of + {ok, Sock} -> + {ok, State#{sock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "bind socket (to local address)", + cmd => fun(#{sock := Sock, local_sa := LSA} = State) -> + case socket:bind(Sock, LSA) of + {ok, Port} -> + {ok, State#{port => Port}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester, local_sa := LSA, port := Port}) -> + ServerSA = LSA#{port => Port}, + ?SEV_ANNOUNCE_READY(Tester, init, ServerSA), + ok + end}, + + %% The actual test + #{desc => "await continue (recv)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, recv) + end}, + #{desc => "try recv request (with nowait, expect select)", + cmd => fun(#{sock := Sock, recv := Recv} = State) -> + case Recv(Sock) of + {select, SelectInfo} -> + {ok, State#{recv_select_info => SelectInfo}}; + {ok, X} -> + {error, {unexpected_select_info, X}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (recv_select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, recv_select), + ok + end}, + #{desc => "await select message (without success)", + cmd => fun(#{sock := Sock}) -> + receive + {'$socket', Sock, select, Ref} -> + {error, {unexpected_select, Ref}} + after 5000 -> + ok + end + end}, + #{desc => "announce ready (no select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, no_select), + ok + end}, + #{desc => "await continue (cancel)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, cancel) + end}, + #{desc => "cancel", + cmd => fun(#{sock := Sock, recv_select_info := SelectInfo}) -> + ok = socket:cancel(Sock, SelectInfo) + end}, + #{desc => "announce ready (cancel)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, cancel), + ok + end}, + + %% Termination + #{desc => "await terminate (from tester)", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + State2 = maps:remove(tester, State), + State3 = maps:remove(recv_stag, State2), + State4 = maps:remove(recv_sref, State3), + State5 = maps:remove(req_src, State4), + {ok, State5}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "close socket", + cmd => fun(#{sock := Sock} = State) -> + ok = socket:close(Sock), + {ok, maps:remove(sock, State)} + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + + TesterSeq = + [ + %% *** Init part *** + #{desc => "monitor server", + cmd => fun(#{server := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + + %% Start the server + #{desc => "order server start", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await server ready (init)", + cmd => fun(#{server := Pid} = State) -> + {ok, ServerSA} = ?SEV_AWAIT_READY(Pid, server, init), + {ok, State#{server_sa => ServerSA}} + end}, + + %% The actual test + #{desc => "order server continue (recv)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, recv), + ok + end}, + #{desc => "await server ready (recv select)", + cmd => fun(#{server := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, server, recv_select) + end}, + #{desc => "await server ready (no select)", + cmd => fun(#{server := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, server, no_select) + end}, + #{desc => "order server continue (cancel)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, cancel), + ok + end}, + #{desc => "await server ready (cancel)", + cmd => fun(#{server := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, server, cancel) + end}, + + %% Terminations + #{desc => "order server to terminate", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await server termination", + cmd => fun(#{server := Pid} = State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + State1 = maps:remove(server, State), + {ok, State1}; + {error, _} = ERROR -> + ERROR + end + end}, + + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + i("start server evaluator"), + ServerInitState = InitState, + Server = ?SEV_START("server", ServerSeq, ServerInitState), + + i("start 'tester' evaluator"), + TesterInitState = #{server => Server#ev.pid}, + Tester = ?SEV_START("tester", TesterSeq, TesterInitState), + + i("await evaluator"), + ok = ?SEV_AWAIT_FINISH([Server, Tester]). + + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically we make an async (Timeout = nowait) call to accept, +%% wait some time and then cancel. +%% +api_a_accept_cancel_tcp4(suite) -> + []; +api_a_accept_cancel_tcp4(doc) -> + []; +api_a_accept_cancel_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(api_a_accept_cancel_tcp4, + fun() -> + Accept = fun(Sock) -> + case socket:accept(Sock, nowait) of + {ok, _} = OK -> + OK; + {select, _} = SELECT -> + SELECT; + {error, _} = ERROR -> + ERROR + end + end, + InitState = #{domain => inet, + accept => Accept}, + ok = api_a_accept_cancel_tcp(InitState) + end). + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +api_a_accept_cancel_tcp(InitState) -> + process_flag(trap_exit, true), + ServerSeq = + [ + %% *** Wait for start order *** + #{desc => "await start (from tester)", + cmd => fun(State) -> + Tester = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester}) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** Init part *** + #{desc => "which local address", + cmd => fun(#{domain := Domain} = State) -> + LSA = which_local_socket_addr(Domain), + {ok, State#{lsa => LSA}} + end}, + #{desc => "create listen socket", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, stream, tcp) of + {ok, Sock} -> + {ok, State#{lsock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "bind to local address", + cmd => fun(#{lsock := LSock, lsa := LSA} = State) -> + case socket:bind(LSock, LSA) of + {ok, Port} -> + {ok, State#{lport => Port}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "make listen socket", + cmd => fun(#{lsock := LSock}) -> + socket:listen(LSock) + end}, + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester, lport := Port}) -> + ?SEV_ANNOUNCE_READY(Tester, init, Port), + ok + end}, + + %% The actual test + #{desc => "await continue (accept)", + cmd => fun(#{tester := Tester}) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, accept) + end}, + #{desc => "await connection (nowait)", + cmd => fun(#{lsock := LSock, accept := Accept} = State) -> + case Accept(LSock) of + {select, {select_info, T, R} = SelectInfo} -> + ?SEV_IPRINT("accept select: " + "~n T: ~p" + "~n R: ~p", [T, R]), + {ok, State#{accept_select_info => SelectInfo}}; + {ok, X} -> + {error, {unexpected_select_info, X}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (accept_select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, accept_select), + ok + end}, + #{desc => "await select message (without success)", + cmd => fun(#{lsock := Sock}) -> + receive + {'$socket', Sock, select, Ref} -> + {error, {unexpected_select, Ref}} + after 5000 -> + ok + end + end}, + #{desc => "announce ready (no select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, no_select), + ok + end}, + #{desc => "await continue (cancel)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, cancel) + end}, + #{desc => "cancel", + cmd => fun(#{lsock := Sock, accept_select_info := SelectInfo}) -> + ok = socket:cancel(Sock, SelectInfo) + end}, + #{desc => "announce ready (cancel)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, cancel), + ok + end}, + + %% *** Termination *** + #{desc => "await terminate", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + {ok, maps:remove(tester, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "close listen socket", + cmd => fun(#{lsock := Sock}) -> + socket:close(Sock) + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + + TesterSeq = + [ + %% *** Init part *** + #{desc => "monitor server", + cmd => fun(#{server := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + + %% Start the server + #{desc => "order server start", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await server ready (init)", + cmd => fun(#{server := Pid} = State) -> + {ok, Port} = ?SEV_AWAIT_READY(Pid, server, init), + {ok, State#{server_port => Port}} + end}, + + %% *** The actual test *** + #{desc => "order server to continue (with accept)", + cmd => fun(#{server := Server} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Server, accept), + ok + end}, + #{desc => "await server ready (accept select)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_AWAIT_READY(Pid, server, accept_select) + end}, + #{desc => "await server ready (no select)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_AWAIT_READY(Pid, server, no_select) + end}, + #{desc => "order server to continue (cancel)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, cancel), + ok + end}, + #{desc => "await server ready (cancel)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_AWAIT_READY(Pid, server, cancel) + end}, + + %% *** Termination *** + #{desc => "order server to terminate", + cmd => fun(#{server := Server} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Server), + ok + end}, + #{desc => "await server termination", + cmd => fun(#{server := Server} = State) -> + ?SEV_AWAIT_TERMINATION(Server), + State1 = maps:remove(server, State), + {ok, State1} + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + i("start server evaluator"), + Server = ?SEV_START("server", ServerSeq, InitState), + + i("start tester evaluator"), + TesterInitState = #{server => Server#ev.pid}, + Tester = ?SEV_START("tester", TesterSeq, TesterInitState), + + i("await evaluator(s)"), + ok = ?SEV_AWAIT_FINISH([Server, Tester]). + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically we make an async (Timeout = nowait) call to recv, +%% wait some time and then cancel. +%% +api_a_recv_cancel_tcp4(suite) -> + []; +api_a_recv_cancel_tcp4(doc) -> + []; +api_a_recv_cancel_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(api_a_recv_cancel_tcp4, + fun() -> + Recv = fun(Sock) -> + socket:recv(Sock, 0, nowait) + end, + InitState = #{domain => inet, + recv => Recv}, + ok = api_a_recv_cancel_tcp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically we make an async (Timeout = nowait) call to recvmsg, +%% wait some time and then cancel. +%% +api_a_recvmsg_cancel_tcp4(suite) -> + []; +api_a_recvmsg_cancel_tcp4(doc) -> + []; +api_a_recvmsg_cancel_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(api_a_recvmsg_cancel_tcp4, + fun() -> + Recv = fun(Sock) -> + socket:recvmsg(Sock, nowait) + end, + InitState = #{domain => inet, + recv => Recv}, + ok = api_a_recv_cancel_tcp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +api_a_recv_cancel_tcp(InitState) -> + process_flag(trap_exit, true), + ServerSeq = + [ + %% *** Wait for start order *** + #{desc => "await start (from tester)", + cmd => fun(State) -> + Tester = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester}) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** Init part *** + #{desc => "which local address", + cmd => fun(#{domain := Domain} = State) -> + LSA = which_local_socket_addr(Domain), + {ok, State#{lsa => LSA}} + end}, + #{desc => "create listen socket", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, stream, tcp) of + {ok, Sock} -> + {ok, State#{lsock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "bind to local address", + cmd => fun(#{lsock := LSock, lsa := LSA} = State) -> + case socket:bind(LSock, LSA) of + {ok, Port} -> + {ok, State#{lport => Port}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "make listen socket", + cmd => fun(#{lsock := LSock}) -> + socket:listen(LSock) + end}, + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester, lport := Port}) -> + ?SEV_ANNOUNCE_READY(Tester, init, Port), + ok + end}, + + %% The actual test + #{desc => "await continue (accept)", + cmd => fun(#{tester := Tester}) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, accept) + end}, + #{desc => "await connection (nowait)", + cmd => fun(#{lsock := LSock} = State) -> + case socket:accept(LSock) of + {ok, CSock} -> + {ok, State#{csock => CSock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (accept)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, accept), + ok + end}, + + #{desc => "await continue (nowait recv)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, recv) + end}, + #{desc => "try recv request (with nowait, expect select)", + cmd => fun(#{csock := Sock, recv := Recv} = State) -> + case Recv(Sock) of + {select, {select_info, T, R} = SelectInfo} -> + ?SEV_IPRINT("recv select: " + "~n Tag: ~p" + "~n Ref: ~p", [T, R]), + {ok, State#{recv_select_info => SelectInfo}}; + {ok, X} -> + {error, {unexpected_select_info, X}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (recv_select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, recv_select), + ok + end}, + #{desc => "await select message", + cmd => fun(#{csock := Sock}) -> + receive + {'$socket', Sock, select, Ref} -> + {error, {unexpected_select, Ref}} + after 5000 -> + ok + end + end}, + #{desc => "announce ready (no select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, no_select), + ok + end}, + #{desc => "await continue (cancel)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, cancel) + end}, + #{desc => "cancel", + cmd => fun(#{csock := Sock, recv_select_info := SelectInfo}) -> + ok = socket:cancel(Sock, SelectInfo) + end}, + #{desc => "announce ready (cancel)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, cancel), + ok + end}, + + %% *** Termination *** + #{desc => "await terminate", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + {ok, maps:remove(tester, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "close connection socket", + cmd => fun(#{csock := Sock}) -> + socket:close(Sock) + end}, + #{desc => "close listen socket", + cmd => fun(#{lsock := Sock}) -> + socket:close(Sock) + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + + ClientSeq = + [ + %% *** Wait for start order *** + #{desc => "await start (from tester)", + cmd => fun(State) -> + {Tester, Port} = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester, server_port => Port}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester}) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** The init part *** + #{desc => "which server (local) address", + cmd => fun(#{domain := Domain, server_port := Port} = State) -> + LSA = which_local_socket_addr(Domain), + SSA = LSA#{port => Port}, + {ok, State#{local_sa => LSA, server_sa => SSA}} + end}, + #{desc => "create socket", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, stream, tcp) of + {ok, Sock} -> + {ok, State#{sock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "bind to local address", + cmd => fun(#{sock := Sock, local_sa := LSA} = _State) -> + case socket:bind(Sock, LSA) of + {ok, _Port} -> + ok; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, init), + ok + end}, + + %% *** The actual test *** + #{desc => "await continue (connect)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, connect) + end}, + #{desc => "connect to server", + cmd => fun(#{sock := Sock, server_sa := SSA}) -> + socket:connect(Sock, SSA) + end}, + #{desc => "announce ready (connect)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, connect), + ok + end}, + + %% *** Termination *** + #{desc => "await terminate", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + {ok, maps:remove(tester, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "close socket", + cmd => fun(#{sock := Sock}) -> + socket:close(Sock) + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + TesterSeq = + [ + %% *** Init part *** + #{desc => "monitor server", + cmd => fun(#{server := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "monitor client", + cmd => fun(#{client := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + + %% Start the server + #{desc => "order server start", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await server ready (init)", + cmd => fun(#{server := Pid} = State) -> + {ok, Port} = ?SEV_AWAIT_READY(Pid, server, init), + {ok, State#{server_port => Port}} + end}, + + %% Start the client + #{desc => "order client start", + cmd => fun(#{client := Pid, server_port := Port} = _State) -> + ?SEV_ANNOUNCE_START(Pid, Port), + ok + end}, + #{desc => "await client ready (init)", + cmd => fun(#{client := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client, init) + end}, + + %% *** The actual test *** + #{desc => "order server to continue (with accept)", + cmd => fun(#{server := Server} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Server, accept), + ok + end}, + #{desc => "order client to continue (connect)", + cmd => fun(#{client := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, connect), + ok + end}, + #{desc => "await client ready (connect)", + cmd => fun(#{client := Pid} = _State) -> + ?SEV_AWAIT_READY(Pid, client, connect) + end}, + #{desc => "await server ready (accept)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_AWAIT_READY(Pid, server, accept) + end}, + + #{desc => "order server to continue (recv)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, recv), + ok + end}, + #{desc => "await server ready (recv select)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_AWAIT_READY(Pid, server, recv_select) + end}, + #{desc => "await server ready (no select)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_AWAIT_READY(Pid, server, no_select) + end}, + #{desc => "order server to continue (send request)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, cancel), + ok + end}, + #{desc => "await server ready (cancel)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_AWAIT_READY(Pid, server, cancel) + end}, + + %% *** Termination *** + #{desc => "order client to terminate", + cmd => fun(#{client := Client} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Client), + ok + end}, + #{desc => "await client termination", + cmd => fun(#{client := Client} = State) -> + ?SEV_AWAIT_TERMINATION(Client), + State1 = maps:remove(client, State), + {ok, State1} + end}, + #{desc => "order server to terminate", + cmd => fun(#{server := Server} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Server), + ok + end}, + #{desc => "await server termination", + cmd => fun(#{server := Server} = State) -> + ?SEV_AWAIT_TERMINATION(Server), + State1 = maps:remove(server, State), + {ok, State1} + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + i("start server evaluator"), + Server = ?SEV_START("server", ServerSeq, InitState), + + i("start client evaluator"), + Client = ?SEV_START("client", ClientSeq, InitState), + + i("start tester evaluator"), + TesterInitState = #{server => Server#ev.pid, + client => Client#ev.pid}, + Tester = ?SEV_START("tester", TesterSeq, TesterInitState), + + i("await evaluator(s)"), + ok = ?SEV_AWAIT_FINISH([Server, Client, Tester]). + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically we make multiple async (Timeout = nowait) call(s) to recvfrom +%% (from *several* processes), wait some time and then cancel. +%% This should result in abort messages to the 'other' processes. +%% +api_a_mrecvfrom_cancel_udp4(suite) -> + []; +api_a_mrecvfrom_cancel_udp4(doc) -> + []; +api_a_mrecvfrom_cancel_udp4(_Config) when is_list(_Config) -> + ?TT(?SECS(20)), + tc_try(api_a_mrecvfrom_cancel_udp4, + fun() -> + Recv = fun(Sock) -> + case socket:recvfrom(Sock, 0, nowait) of + {ok, _} = OK -> + OK; + {select, _} = SELECT -> + SELECT; + {error, _} = ERROR -> + ERROR + end + end, + InitState = #{domain => inet, + recv => Recv}, + ok = api_a_mrecv_cancel_udp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically we make multiple async (Timeout = nowait) call(s) to recvmsg +%% (from *several* processes), wait some time and then cancel. +%% This should result in abort messages to the 'other' processes. +%% +api_a_mrecvmsg_cancel_udp4(suite) -> + []; +api_a_mrecvmsg_cancel_udp4(doc) -> + []; +api_a_mrecvmsg_cancel_udp4(_Config) when is_list(_Config) -> + ?TT(?SECS(20)), + tc_try(api_a_mrecvmsg_cancel_udp4, + fun() -> + Recv = fun(Sock) -> + case socket:recvmsg(Sock, nowait) of + {ok, _} = OK -> + OK; + {select, _} = SELECT -> + SELECT; + {error, _} = ERROR -> + ERROR + end + end, + InitState = #{domain => inet, + recv => Recv}, + ok = api_a_mrecv_cancel_udp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +api_a_mrecv_cancel_udp(InitState) -> + ServerSeq = + [ + %% *** Wait for start order part *** + #{desc => "await start", + cmd => fun(State) -> + Tester = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester} = _State) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** Init part *** + #{desc => "which local address", + cmd => fun(#{domain := Domain} = State) -> + LSA = which_local_socket_addr(Domain), + {ok, State#{local_sa => LSA}} + end}, + #{desc => "create socket", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, dgram, udp) of + {ok, Sock} -> + {ok, State#{sock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "bind socket (to local address)", + cmd => fun(#{sock := Sock, local_sa := LSA} = State) -> + case socket:bind(Sock, LSA) of + {ok, Port} -> + {ok, State#{port => Port}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester, sock := Sock}) -> + ?SEV_ANNOUNCE_READY(Tester, init, Sock), + ok + end}, + + %% The actual test + #{desc => "await continue (recv)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, recv) + end}, + #{desc => "try recv request (with nowait, expect select)", + cmd => fun(#{sock := Sock, recv := Recv} = State) -> + case Recv(Sock) of + {select, SelectInfo} -> + {ok, State#{recv_select_info => SelectInfo}}; + {ok, X} -> + {error, {unexpected_select_info, X}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (recv_select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, recv_select), + ok + end}, + #{desc => "await abort message", + cmd => fun(#{sock := Sock, + recv_select_info := {select_info, _, Ref}} = State) -> + receive + {'$socket', Sock, select, Ref} -> + {error, {unexpected_select, Ref}}; + {'$socket', Sock, abort, {Ref, closed}} -> + {ok, maps:remove(sock, State)} + after 5000 -> + ?SEV_EPRINT("message queue: ~p", [mq()]), + {error, timeout} + end + end}, + #{desc => "announce ready (abort)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, abort), + ok + end}, + + %% Termination + #{desc => "await terminate (from tester)", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + State2 = maps:remove(tester, State), + State3 = maps:remove(recv_stag, State2), + State4 = maps:remove(recv_sref, State3), + State5 = maps:remove(req_src, State4), + {ok, State5}; + {error, _} = ERROR -> + ERROR + end + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + + AltServerSeq = + [ + %% *** Wait for start order part *** + #{desc => "await start", + cmd => fun(State) -> + {Tester, Sock} = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester, sock => Sock}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester} = _State) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, init), + ok + end}, + + %% The actual test + #{desc => "await continue (recv)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, recv) + end}, + #{desc => "try recv request (with nowait, expect select)", + cmd => fun(#{sock := Sock, recv := Recv} = State) -> + case Recv(Sock) of + {select, SelectInfo} -> + {ok, State#{recv_select_info => SelectInfo}}; + {ok, X} -> + {error, {unexpected_select_info, X}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (recv_select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, recv_select), + ok + end}, + #{desc => "await abort message", + cmd => fun(#{sock := Sock, + recv_select_info := {select_info, _, Ref}} = State) -> + receive + {'$socket', Sock, select, Ref} -> + {error, {unexpected_select, Ref}}; + {'$socket', Sock, abort, {Ref, closed}} -> + {ok, maps:remove(sock, State)} + after 5000 -> + ?SEV_EPRINT("message queue: ~p", [mq()]), + {error, timeout} + end + end}, + #{desc => "announce ready (abort)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, abort), + ok + end}, + + %% Termination + #{desc => "await terminate (from tester)", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + ?SEV_IPRINT("terminating"), + State1 = maps:remove(recv_select_info, State), + State2 = maps:remove(tester, State1), + State3 = maps:remove(sock, State2), + {ok, State3}; + {error, _} = ERROR -> + ERROR + end + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + + + TesterSeq = + [ + %% *** Init part *** + #{desc => "monitor server", + cmd => fun(#{server := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "monitor alt-server 1", + cmd => fun(#{alt_server1 := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "monitor alt-server 2", + cmd => fun(#{alt_server2 := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + + %% Start the server + #{desc => "order server start", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await server ready (init)", + cmd => fun(#{server := Pid} = State) -> + {ok, Sock} = ?SEV_AWAIT_READY(Pid, server, init), + {ok, State#{sock => Sock}} + end}, + + %% Start the alt-server 1 + #{desc => "order alt-server 1 start", + cmd => fun(#{alt_server1 := Pid, sock := Sock} = _State) -> + ?SEV_ANNOUNCE_START(Pid, Sock), + ok + end}, + #{desc => "await alt-server 1 ready (init)", + cmd => fun(#{alt_server1 := Pid} = _State) -> + ?SEV_AWAIT_READY(Pid, alt_server1, init) + end}, + + %% Start the alt-server 2 + #{desc => "order alt-server 2 start", + cmd => fun(#{alt_server2 := Pid, sock := Sock} = _State) -> + ?SEV_ANNOUNCE_START(Pid, Sock), + ok + end}, + #{desc => "await alt-server 2 ready (init)", + cmd => fun(#{alt_server2 := Pid} = _State) -> + ?SEV_AWAIT_READY(Pid, alt_server2, init) + end}, + + + %% The actual test + #{desc => "order server continue (recv)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, recv), + ok + end}, + #{desc => "await server ready (recv select)", + cmd => fun(#{server := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, server, recv_select) + end}, + + #{desc => "order alt-server 1 continue (recv)", + cmd => fun(#{alt_server1 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, recv), + ok + end}, + #{desc => "await alt-server 1 ready (recv select)", + cmd => fun(#{alt_server1 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, alt_server1, recv_select) + end}, + + #{desc => "order alt-server 2 continue (recv)", + cmd => fun(#{alt_server2 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, recv), + ok + end}, + #{desc => "await alt-server 2 ready (recv select)", + cmd => fun(#{alt_server2 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, alt_server2, recv_select) + end}, + + ?SEV_SLEEP(?SECS(1)), + + #{desc => "close the socket", + cmd => fun(#{sock := Sock} = _State) -> + socket:close(Sock) + end}, + + #{desc => "await server ready (abort)", + cmd => fun(#{server := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, server, abort) + end}, + #{desc => "await alt-server 1 ready (abort)", + cmd => fun(#{alt_server1 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, alt_server1, abort) + end}, + #{desc => "await alt-server 2 ready (abort)", + cmd => fun(#{alt_server2 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, alt_server2, abort) + end}, + + %% Terminations + #{desc => "order alt-server 2 to terminate", + cmd => fun(#{alt_server2 := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await alt-server 2 termination", + cmd => fun(#{alt_server2 := Pid} = State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + State1 = maps:remove(alt_server2, State), + {ok, State1}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "order alt-server 1 to terminate", + cmd => fun(#{alt_server1 := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await alt-server 1 termination", + cmd => fun(#{alt_server1 := Pid} = State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + State1 = maps:remove(alt_server1, State), + {ok, State1}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "order server to terminate", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await server termination", + cmd => fun(#{server := Pid} = State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + State1 = maps:remove(server, State), + {ok, State1}; + {error, _} = ERROR -> + ERROR + end + end}, + + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + i("start server evaluator"), + Server = ?SEV_START("server", ServerSeq, InitState), + + i("start alt-server 1 evaluator"), + AltServer1 = ?SEV_START("alt_server1", AltServerSeq, InitState), + + i("start alt-server 2 evaluator"), + AltServer2 = ?SEV_START("alt_server2", AltServerSeq, InitState), + + i("start 'tester' evaluator"), + TesterInitState = #{server => Server#ev.pid, + alt_server1 => AltServer1#ev.pid, + alt_server2 => AltServer2#ev.pid}, + Tester = ?SEV_START("tester", TesterSeq, TesterInitState), + + i("await evaluator(s)"), + ok = ?SEV_AWAIT_FINISH([Server, AltServer1, AltServer2, Tester]). + + + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically we make multiple async (Timeout = nowait) call(s) to accept +%% (from *several* processes), wait some time and then cancel, +%% This should result in abort messages to the 'other' processes. +%% +api_a_maccept_cancel_tcp4(suite) -> + []; +api_a_maccept_cancel_tcp4(doc) -> + []; +api_a_maccept_cancel_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(20)), + tc_try(api_a_maccept_cancel_tcp4, + fun() -> + Accept = fun(Sock) -> + case socket:accept(Sock, nowait) of + {ok, _} = OK -> + OK; + {select, _} = SELECT -> + SELECT; + {error, _} = ERROR -> + ERROR + end + end, + InitState = #{domain => inet, + accept => Accept}, + ok = api_a_maccept_cancel_tcp(InitState) + end). + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +api_a_maccept_cancel_tcp(InitState) -> + process_flag(trap_exit, true), + ServerSeq = + [ + %% *** Wait for start order *** + #{desc => "await start (from tester)", + cmd => fun(State) -> + Tester = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester}) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** Init part *** + #{desc => "which local address", + cmd => fun(#{domain := Domain} = State) -> + LSA = which_local_socket_addr(Domain), + {ok, State#{lsa => LSA}} + end}, + #{desc => "create listen socket", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, stream, tcp) of + {ok, Sock} -> + {ok, State#{lsock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "bind to local address", + cmd => fun(#{lsock := LSock, lsa := LSA} = State) -> + case socket:bind(LSock, LSA) of + {ok, Port} -> + {ok, State#{lport => Port}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "make listen socket", + cmd => fun(#{lsock := LSock}) -> + socket:listen(LSock) + end}, + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester, lsock := Sock}) -> + ?SEV_ANNOUNCE_READY(Tester, init, Sock), + ok + end}, + + %% The actual test + #{desc => "await continue (accept)", + cmd => fun(#{tester := Tester}) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, accept) + end}, + #{desc => "await connection (nowait)", + cmd => fun(#{lsock := LSock, accept := Accept} = State) -> + case Accept(LSock) of + {select, {select_info, T, R} = SelectInfo} -> + ?SEV_IPRINT("accept select: " + "~n T: ~p" + "~n R: ~p", [T, R]), + {ok, State#{accept_select_info => SelectInfo}}; + {ok, X} -> + {error, {unexpected_select_info, X}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (accept_select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, accept_select), + ok + end}, + #{desc => "await select message (without success)", + cmd => fun(#{lsock := Sock, + accept_select_info := {select_info, _, Ref}} = State) -> + receive + {'$socket', Sock, select, Ref} -> + {error, {unexpected_select, Ref}}; + {'$socket', Sock, abort, {Ref, closed}} -> + {ok, maps:remove(lsock, State)} + after 5000 -> + ?SEV_EPRINT("message queue: ~p", [mq()]), + {error, timeout} + end + end}, + #{desc => "announce ready (abort)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, abort), + ok + end}, + + %% *** Termination *** + #{desc => "await terminate", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + {ok, maps:remove(tester, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + + AltServerSeq = + [ + %% *** Wait for start order part *** + #{desc => "await start", + cmd => fun(State) -> + {Tester, Sock} = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester, lsock => Sock}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester} = _State) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, init), + ok + end}, + + %% The actual test + #{desc => "await continue (accept)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, accept) + end}, + #{desc => "try accept request (with nowait, expect select)", + cmd => fun(#{lsock := Sock, accept := Accept} = State) -> + case Accept(Sock) of + {select, SelectInfo} -> + {ok, State#{accept_select_info => SelectInfo}}; + {ok, X} -> + {error, {unexpected_select_info, X}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (accept_select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, accept_select), + ok + end}, + #{desc => "await abort message", + cmd => fun(#{lsock := Sock, + accept_select_info := {select_info, _, Ref}} = State) -> + receive + {'$socket', Sock, select, Ref} -> + {error, {unexpected_select, Ref}}; + {'$socket', Sock, abort, {Ref, closed}} -> + {ok, maps:remove(sock, State)} + after 5000 -> + ?SEV_EPRINT("message queue: ~p", [mq()]), + {error, timeout} + end + end}, + #{desc => "announce ready (abort)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, abort), + ok + end}, + + %% Termination + #{desc => "await terminate (from tester)", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + ?SEV_IPRINT("terminating"), + State1 = maps:remove(tester, State), + State2 = maps:remove(accept_select_info, State1), + State3 = maps:remove(lsock, State2), + {ok, State3}; + {error, _} = ERROR -> + ERROR + end + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + + TesterSeq = + [ + %% *** Init part *** + #{desc => "monitor server", + cmd => fun(#{server := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "monitor alt-server 1", + cmd => fun(#{alt_server1 := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "monitor alt-server 2", + cmd => fun(#{alt_server2 := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + + %% Start the server + #{desc => "order server start", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await server ready (init)", + cmd => fun(#{server := Pid} = State) -> + {ok, Sock} = ?SEV_AWAIT_READY(Pid, server, init), + {ok, State#{sock => Sock}} + end}, + + %% Start the alt-server 1 + #{desc => "order alt-server 1 start", + cmd => fun(#{alt_server1 := Pid, sock := Sock} = _State) -> + ?SEV_ANNOUNCE_START(Pid, Sock), + ok + end}, + #{desc => "await alt-server 1 ready (init)", + cmd => fun(#{alt_server1 := Pid} = _State) -> + ?SEV_AWAIT_READY(Pid, alt_server1, init) + end}, + + %% Start the alt-server 2 + #{desc => "order alt-server 2 start", + cmd => fun(#{alt_server2 := Pid, sock := Sock} = _State) -> + ?SEV_ANNOUNCE_START(Pid, Sock), + ok + end}, + #{desc => "await alt-server 2 ready (init)", + cmd => fun(#{alt_server2 := Pid} = _State) -> + ?SEV_AWAIT_READY(Pid, alt_server2, init) + end}, + + + %% *** The actual test *** + #{desc => "order server continue (accept)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, accept), + ok + end}, + #{desc => "await server ready (accept select)", + cmd => fun(#{server := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, server, accept_select) + end}, + + #{desc => "order alt-server 1 continue (accept)", + cmd => fun(#{alt_server1 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, accept), + ok + end}, + #{desc => "await alt-server 1 ready (accept select)", + cmd => fun(#{alt_server1 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, alt_server1, accept_select) + end}, + + #{desc => "order alt-server 2 continue (accept)", + cmd => fun(#{alt_server2 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, accept), + ok + end}, + #{desc => "await alt-server 2 ready (accept select)", + cmd => fun(#{alt_server2 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, alt_server2, accept_select) + end}, + + ?SEV_SLEEP(?SECS(1)), + + #{desc => "close the socket", + cmd => fun(#{sock := Sock} = _State) -> + socket:close(Sock) + end}, + + #{desc => "await server ready (abort)", + cmd => fun(#{server := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, server, abort) + end}, + #{desc => "await alt-server 1 ready (abort)", + cmd => fun(#{alt_server1 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, alt_server1, abort) + end}, + #{desc => "await alt-server 2 ready (abort)", + cmd => fun(#{alt_server2 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, alt_server2, abort) + end}, + + + %% *** Termination *** + #{desc => "order alt-server 2 to terminate", + cmd => fun(#{alt_server2 := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await alt-server 2 termination", + cmd => fun(#{alt_server2 := Pid} = State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + State1 = maps:remove(alt_server2, State), + {ok, State1}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "order alt-server 1 to terminate", + cmd => fun(#{alt_server1 := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await alt-server 1 termination", + cmd => fun(#{alt_server1 := Pid} = State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + State1 = maps:remove(alt_server1, State), + {ok, State1}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "order server to terminate", + cmd => fun(#{server := Server} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Server), + ok + end}, + #{desc => "await server termination", + cmd => fun(#{server := Server} = State) -> + ?SEV_AWAIT_TERMINATION(Server), + State1 = maps:remove(server, State), + {ok, State1} + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + i("start server evaluator"), + Server = ?SEV_START("server", ServerSeq, InitState), + + i("start alt-server 1 evaluator"), + AltServer1 = ?SEV_START("alt_server1", AltServerSeq, InitState), + + i("start alt-server 2 evaluator"), + AltServer2 = ?SEV_START("alt_server2", AltServerSeq, InitState), + + i("start tester evaluator"), + TesterInitState = #{server => Server#ev.pid, + alt_server1 => AltServer1#ev.pid, + alt_server2 => AltServer2#ev.pid}, + Tester = ?SEV_START("tester", TesterSeq, TesterInitState), + + i("await evaluator(s)"), + ok = ?SEV_AWAIT_FINISH([Server, AltServer1, AltServer2, Tester]). + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically we make multiple async (Timeout = nowait) call(s) to recv +%% (from *several* processes), wait some time and then cancel, +%% This should result in abort messages to the 'other' processes. +%% +api_a_mrecv_cancel_tcp4(suite) -> + []; +api_a_mrecv_cancel_tcp4(doc) -> + []; +api_a_mrecv_cancel_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(20)), + tc_try(api_a_mrecv_cancel_tcp4, + fun() -> + Recv = fun(Sock) -> + socket:recv(Sock, 0, nowait) + end, + InitState = #{domain => inet, + recv => Recv}, + ok = api_a_mrecv_cancel_tcp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Basically we make multiple async (Timeout = nowait) call(s) to recvmsg +%% (from *several* processes), wait some time and then cancel, +%% This should result in abort messages to the 'other' processes. +%% +api_a_mrecvmsg_cancel_tcp4(suite) -> + []; +api_a_mrecvmsg_cancel_tcp4(doc) -> + []; +api_a_mrecvmsg_cancel_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(20)), + tc_try(api_a_mrecvmsg_cancel_tcp4, + fun() -> + Recv = fun(Sock) -> + socket:recvmsg(Sock, nowait) + end, + InitState = #{domain => inet, + recv => Recv}, + ok = api_a_mrecv_cancel_tcp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +api_a_mrecv_cancel_tcp(InitState) -> + process_flag(trap_exit, true), + ServerSeq = + [ + %% *** Wait for start order *** + #{desc => "await start (from tester)", + cmd => fun(State) -> + Tester = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester}) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** Init part *** + #{desc => "which local address", + cmd => fun(#{domain := Domain} = State) -> + LSA = which_local_socket_addr(Domain), + {ok, State#{lsa => LSA}} + end}, + #{desc => "create listen socket", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, stream, tcp) of + {ok, Sock} -> + {ok, State#{lsock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "bind to local address", + cmd => fun(#{lsock := LSock, lsa := LSA} = State) -> + case socket:bind(LSock, LSA) of + {ok, Port} -> + {ok, State#{lport => Port}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "make listen socket", + cmd => fun(#{lsock := LSock}) -> + socket:listen(LSock) + end}, + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester, lport := Port}) -> + ?SEV_ANNOUNCE_READY(Tester, init, Port), + ok + end}, + + %% The actual test + #{desc => "await continue (accept)", + cmd => fun(#{tester := Tester}) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, accept) + end}, + #{desc => "await connection (nowait)", + cmd => fun(#{lsock := LSock} = State) -> + case socket:accept(LSock) of + {ok, CSock} -> + {ok, State#{csock => CSock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (accept)", + cmd => fun(#{tester := Tester, csock := Sock}) -> + ?SEV_ANNOUNCE_READY(Tester, accept, Sock), + ok + end}, + + #{desc => "await continue (nowait recv)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, recv) + end}, + #{desc => "try recv request (with nowait, expect select)", + cmd => fun(#{csock := Sock, recv := Recv} = State) -> + case Recv(Sock) of + {select, {select_info, T, R} = SelectInfo} -> + ?SEV_IPRINT("recv select: " + "~n Tag: ~p" + "~n Ref: ~p", [T, R]), + {ok, State#{recv_select_info => SelectInfo}}; + {ok, X} -> + {error, {unexpected_select_info, X}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (recv_select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, recv_select), + ok + end}, + #{desc => "await select message", + cmd => fun(#{csock := Sock, + recv_select_info := {select_info, _, Ref}} = State) -> + receive + {'$socket', Sock, select, Ref} -> + {error, {unexpected_select, Ref}}; + {'$socket', Sock, abort, {Ref, closed}} -> + {ok, maps:remove(sock, State)} + after 5000 -> + ok + end + end}, + #{desc => "announce ready (abort)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, abort), + ok + end}, + + %% *** Termination *** + #{desc => "await terminate", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + {ok, maps:remove(tester, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "close listen socket", + cmd => fun(#{lsock := Sock}) -> + socket:close(Sock) + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + AltServerSeq = + [ + %% *** Wait for start order part *** + #{desc => "await start", + cmd => fun(State) -> + {Tester, Sock} = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester, sock => Sock}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester} = _State) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, init), + ok + end}, + + %% The actual test + #{desc => "await continue (recv)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, recv) + end}, + #{desc => "try recv request (with nowait, expect select)", + cmd => fun(#{sock := Sock, recv := Recv} = State) -> + case Recv(Sock) of + {select, SelectInfo} -> + {ok, State#{recv_select_info => SelectInfo}}; + {ok, X} -> + {error, {unexpected_select_info, X}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (recv_select)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, recv_select), + ok + end}, + #{desc => "await abort message", + cmd => fun(#{sock := Sock, + recv_select_info := {select_info, _, Ref}} = State) -> + receive + {'$socket', Sock, select, Ref} -> + {error, {unexpected_select, Ref}}; + {'$socket', Sock, abort, {Ref, closed}} -> + {ok, maps:remove(sock, State)} + after 5000 -> + ?SEV_EPRINT("message queue: ~p", [mq()]), + {error, timeout} + end + end}, + #{desc => "announce ready (abort)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, abort), + ok + end}, + + %% Termination + #{desc => "await terminate (from tester)", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + ?SEV_IPRINT("terminating"), + State1 = maps:remove(recv_select_info, State), + State2 = maps:remove(tester, State1), + State3 = maps:remove(sock, State2), + {ok, State3}; + {error, _} = ERROR -> + ERROR + end + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + ClientSeq = + [ + %% *** Wait for start order *** + #{desc => "await start (from tester)", + cmd => fun(State) -> + {Tester, Port} = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester, server_port => Port}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester}) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** The init part *** + #{desc => "which server (local) address", + cmd => fun(#{domain := Domain, server_port := Port} = State) -> + LSA = which_local_socket_addr(Domain), + SSA = LSA#{port => Port}, + {ok, State#{local_sa => LSA, server_sa => SSA}} + end}, + #{desc => "create socket", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, stream, tcp) of + {ok, Sock} -> + {ok, State#{sock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "bind to local address", + cmd => fun(#{sock := Sock, local_sa := LSA} = _State) -> + case socket:bind(Sock, LSA) of + {ok, _Port} -> + ok; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, init), + ok + end}, + + %% *** The actual test *** + #{desc => "await continue (connect)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, connect) + end}, + #{desc => "connect to server", + cmd => fun(#{sock := Sock, server_sa := SSA}) -> + socket:connect(Sock, SSA) + end}, + #{desc => "announce ready (connect)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, connect), + ok + end}, + + %% *** Termination *** + #{desc => "await terminate", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + {ok, maps:remove(tester, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "close socket", + cmd => fun(#{sock := Sock}) -> + socket:close(Sock) + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + TesterSeq = + [ + %% *** Init part *** + #{desc => "monitor server", + cmd => fun(#{server := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "monitor alt-server 1", + cmd => fun(#{alt_server1 := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "monitor alt-server 2", + cmd => fun(#{alt_server2 := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + #{desc => "monitor client", + cmd => fun(#{client := Pid} = _State) -> + _MRef = erlang:monitor(process, Pid), + ok + end}, + + %% Start the server + #{desc => "order server start", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_START(Pid), + ok + end}, + #{desc => "await server ready (init)", + cmd => fun(#{server := Pid} = State) -> + {ok, Port} = ?SEV_AWAIT_READY(Pid, server, init), + {ok, State#{server_port => Port}} + end}, + + %% Start the client + #{desc => "order client start", + cmd => fun(#{client := Pid, server_port := Port} = _State) -> + ?SEV_ANNOUNCE_START(Pid, Port), + ok + end}, + #{desc => "await client ready (init)", + cmd => fun(#{client := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, client, init) + end}, + + #{desc => "order server to continue (with accept)", + cmd => fun(#{server := Server} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Server, accept), + ok + end}, + #{desc => "order client to continue (connect)", + cmd => fun(#{client := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, connect), + ok + end}, + #{desc => "await client ready (connect)", + cmd => fun(#{client := Pid} = _State) -> + ?SEV_AWAIT_READY(Pid, client, connect) + end}, + #{desc => "await server ready (accept)", + cmd => fun(#{server := Pid} = State) -> + {ok, Sock} = ?SEV_AWAIT_READY(Pid, server, accept), + {ok, State#{sock => Sock}} + end}, + + %% Start the alt server 1 + #{desc => "order alt-server 1 start", + cmd => fun(#{alt_server1 := Pid, sock := Sock} = _State) -> + ?SEV_ANNOUNCE_START(Pid, Sock), + ok + end}, + #{desc => "await alt-server 1 ready (init)", + cmd => fun(#{alt_server1 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, alt_server1, init) + end}, + + %% Start the alt server 2 + #{desc => "order alt-server 2 start", + cmd => fun(#{alt_server2 := Pid, sock := Sock} = _State) -> + ?SEV_ANNOUNCE_START(Pid, Sock), + ok + end}, + #{desc => "await alt-server 2 ready (init)", + cmd => fun(#{alt_server2 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, alt_server2, init) + end}, + + + %% *** The actual test *** + #{desc => "order server continue (recv)", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, recv), + ok + end}, + #{desc => "await server ready (recv select)", + cmd => fun(#{server := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, server, recv_select) + end}, + + #{desc => "order alt-server 1 continue (recv)", + cmd => fun(#{alt_server1 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, recv), + ok + end}, + #{desc => "await alt-server 1 ready (recv select)", + cmd => fun(#{alt_server1 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, alt_server1, recv_select) + end}, + + #{desc => "order alt-server 2 continue (recv)", + cmd => fun(#{alt_server2 := Pid} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, recv), + ok + end}, + #{desc => "await alt-server 2 ready (recv select)", + cmd => fun(#{alt_server2 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, alt_server2, recv_select) + end}, + + ?SEV_SLEEP(?SECS(1)), + + #{desc => "close the socket", + cmd => fun(#{sock := Sock} = _State) -> + socket:close(Sock) + end}, + + #{desc => "await server ready (abort)", + cmd => fun(#{server := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, server, abort) + end}, + #{desc => "await alt-server 1 ready (abort)", + cmd => fun(#{alt_server1 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, alt_server1, abort) + end}, + #{desc => "await alt-server 2 ready (abort)", + cmd => fun(#{alt_server2 := Pid} = _State) -> + ok = ?SEV_AWAIT_READY(Pid, alt_server2, abort) + end}, + + %% Terminations + #{desc => "order client to terminate", + cmd => fun(#{client := Client} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Client), + ok + end}, + #{desc => "await client termination", + cmd => fun(#{client := Client} = State) -> + ?SEV_AWAIT_TERMINATION(Client), + State1 = maps:remove(client, State), + {ok, State1} + end}, + + #{desc => "order alt-server 2 to terminate", + cmd => fun(#{alt_server2 := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await alt-server 2 termination", + cmd => fun(#{alt_server2 := Pid} = State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + State1 = maps:remove(alt_server2, State), + {ok, State1}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "order alt-server 1 to terminate", + cmd => fun(#{alt_server1 := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await alt-server 1 termination", + cmd => fun(#{alt_server1 := Pid} = State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + State1 = maps:remove(alt_server1, State), + {ok, State1}; + {error, _} = ERROR -> + ERROR + end + end}, + + #{desc => "order server to terminate", + cmd => fun(#{server := Pid} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Pid), + ok + end}, + #{desc => "await server termination", + cmd => fun(#{server := Pid} = State) -> + case ?SEV_AWAIT_TERMINATION(Pid) of + ok -> + State1 = maps:remove(server, State), + {ok, State1}; + {error, _} = ERROR -> + ERROR + end + end}, + + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + i("start server evaluator"), + Server = ?SEV_START("server", ServerSeq, InitState), + + i("start alt-server 1 evaluator"), + AltServer1 = ?SEV_START("alt_server1", AltServerSeq, InitState), + + i("start alt-server 2 evaluator"), + AltServer2 = ?SEV_START("alt_server2", AltServerSeq, InitState), + + i("start client evaluator"), + Client = ?SEV_START("client", ClientSeq, InitState), + + i("start tester evaluator"), + TesterInitState = #{server => Server#ev.pid, + alt_server1 => AltServer1#ev.pid, + alt_server2 => AltServer2#ev.pid, + client => Client#ev.pid}, + Tester = ?SEV_START("tester", TesterSeq, TesterInitState), + + i("await evaluator(s)"), + ok = ?SEV_AWAIT_FINISH([Server, AltServer1, AltServer2, Client, Tester]). + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% %% API OPTIONS %% @@ -2530,8 +6848,7 @@ api_opt_simple_otp_rcvbuf_option() -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{local_sa => LSA}} end}, #{desc => "create listen socket", @@ -2776,8 +7093,7 @@ api_opt_simple_otp_rcvbuf_option() -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{local_sa => LSA}} end}, #{desc => "create socket", @@ -3173,7 +7489,7 @@ api_opt_simple_otp_controlling_process(suite) -> api_opt_simple_otp_controlling_process(doc) -> []; api_opt_simple_otp_controlling_process(_Config) when is_list(_Config) -> - ?TT(?SECS(5)), + ?TT(?SECS(30)), tc_try(api_opt_simple_otp_controlling_process, fun() -> api_opt_simple_otp_controlling_process() end). @@ -3418,6 +7734,320 @@ api_opt_simple_otp_controlling_process() -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Tests that the add_mambership and drop_membership ip options work. +%% We create one server and two clients. The server only send messages, +%% the clients only receives messages. +%% An UDP datagram is forbidden (RFC 1122) from having a source address +%% that is a multicast address (or a broadcast address). +%% So, the server create a socket "for sending" and the clients sockets +%% "for receiving". +%% Sending socket: Bound to the local address (and any port). +%% When sending, the dest will be the multicast address +%% and port of the receiving socket. +%% Receiving socket: Bound to the multicast address and port. +api_opt_ip_add_drop_membership(suite) -> + []; +api_opt_ip_add_drop_membership(doc) -> + ["OTP-15908 (ERL-980)"]; +api_opt_ip_add_drop_membership(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), + tc_try(api_opt_ip_add_drop_membership, + fun() -> + has_ip_add_membership_support(), + has_ip_drop_membership_support(), + has_ip_multicast_support() + end, + fun() -> api_opt_ip_add_drop_membership() end). + + +api_opt_ip_add_drop_membership() -> + Set = fun(S, Key, Val) -> + socket:setopt(S, ip, Key, Val) + end, + AddMembership = fun(S, Val) -> Set(S, add_membership, Val) end, + DropMembership = fun(S, Val) -> Set(S, drop_membership, Val) end, + + ServerSeq = + [ + %% *** Wait for start order part *** + #{desc => "await start", + cmd => fun(State) -> + {Tester, MSA} = ?SEV_AWAIT_START(), + {ok, State#{tester => Tester, msa => MSA}} + end}, + #{desc => "monitor tester", + cmd => fun(#{tester := Tester} = _State) -> + _MRef = erlang:monitor(process, Tester), + ok + end}, + + %% *** Init part *** + #{desc => "which local address", + cmd => fun(#{domain := Domain} = State) -> + LSA = which_local_socket_addr(Domain), + {ok, State#{local_sa => LSA}} + end}, + #{desc => "create socket", + cmd => fun(#{domain := Domain} = State) -> + case socket:open(Domain, dgram, udp) of + {ok, Sock} -> + {ok, State#{sock => Sock}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "make recv socket reuse addr", + cmd => fun(#{sock := Sock} = _State) -> + case socket:setopt(Sock, socket, reuseaddr, true) of + ok -> + ok; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Failed set reuseaddr: " + "~n ~p", [Reason]), + ERROR + end + end}, + #{desc => "bind recv socket to multicast address", + cmd => fun(#{sock := Sock, msa := MSA} = State) -> + case socket:bind(Sock, MSA) of + {ok, Port} -> + ?SEV_IPRINT("bound to:" + "~n ~p", [Port]), + {ok, State#{msa => MSA#{port => Port}}}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "announce ready (init)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, init), + ok + end}, + + %% The actual test + #{desc => "await continue (add_membership)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, add_membership) + end}, + #{desc => "add membership", + cmd => fun(#{sock := Sock, + msa := #{addr := MAddr}, + local_sa := #{addr := Addr}} = State) -> + MReq = #{multiaddr => MAddr, + interface => Addr}, + ?SEV_IPRINT("try add membership to:" + "~n ~p", [MReq]), + case AddMembership(Sock, MReq) of + ok -> + ?SEV_IPRINT("membership added"), + {ok, State#{mreq => MReq}}; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Failed adding membership to: " + "~n ~p" + "~n Reason: ~p", + [MReq, Reason]), + ERROR + end + end}, + #{desc => "announce ready (add-membership)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, add_membership), + ok + end}, + + #{desc => "await continue (drop_membership)", + cmd => fun(#{tester := Tester} = _State) -> + ?SEV_AWAIT_CONTINUE(Tester, tester, drop_membership) + end}, + #{desc => "drop membership", + cmd => fun(#{sock := Sock, + mreq := MReq} = State) -> + ?SEV_IPRINT("try drop membership from:" + "~n ~p", [MReq]), + case DropMembership(Sock, MReq) of + ok -> + ?SEV_IPRINT("membership dropped"), + {ok, maps:remove(mreq, State)}; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Failed drop membership from: " + "~n ~p" + "~n Reason: ~p", + [MReq, Reason]), + ERROR + end + end}, + #{desc => "announce ready (drop-membership)", + cmd => fun(#{tester := Tester}) -> + ?SEV_ANNOUNCE_READY(Tester, drop_membership), + ok + end}, + + + %% Termination + #{desc => "await terminate (from tester)", + cmd => fun(#{tester := Tester} = State) -> + case ?SEV_AWAIT_TERMINATE(Tester, tester) of + ok -> + {ok, maps:remove(tester, State)}; + {error, _} = ERROR -> + ERROR + end + end}, + #{desc => "close socket", + cmd => fun(#{sock := Sock} = State) -> + socket:close(Sock), + {ok, maps:remove(sock, State)} + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + + TesterSeq = + [ + %% *** Init part *** + #{desc => "monitor server", + cmd => fun(#{server := Server} = _State) -> + _MRef = erlang:monitor(process, Server), + ok + end}, + + %% Start the server + #{desc => "order server start", + cmd => fun(#{server := Pid, msa := MSA} = _State) -> + ?SEV_ANNOUNCE_START(Pid, MSA), + ok + end}, + #{desc => "await server ready (init)", + cmd => fun(#{server := Pid} = _State) -> + case ?SEV_AWAIT_READY(Pid, server, init) of + ok -> + ok; + {error, Reason} = ERROR -> + ?SEV_EPRINT("Start of server failed: " + "~n ~p", [Reason]), + ERROR + end + end}, + + + %% *** The actual test *** + #{desc => "order server to continue (add-membership)", + cmd => fun(#{server := Server} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Server, add_membership), + ok + end}, + #{desc => "await server ready (add-membership)", + cmd => fun(#{server := Server} = _State) -> + ?SEV_AWAIT_READY(Server, server, add_membership) + end}, + + #{desc => "order server to continue (drop-membership)", + cmd => fun(#{server := Server} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Server, drop_membership), + ok + end}, + #{desc => "await server ready (drop-membership)", + cmd => fun(#{server := Server} = _State) -> + ?SEV_AWAIT_READY(Server, server, drop_membership) + end}, + + ?SEV_SLEEP(?SECS(1)), + + %% *** Termination *** + #{desc => "order server terminate", + cmd => fun(#{server := Server} = _State) -> + ?SEV_ANNOUNCE_TERMINATE(Server), + ok + end}, + #{desc => "await server termination", + cmd => fun(#{server := Server} = State) -> + ?SEV_AWAIT_TERMINATION(Server), + {ok, maps:remove(server, State)} + end}, + + %% *** We are done *** + ?SEV_FINISH_NORMAL + ], + + + i("get multicast address"), + Domain = inet, + MAddr = which_ip_multicast_address(), + MSA = #{family => Domain, addr => MAddr}, + + i("start server evaluator"), + ServerInitState = #{domain => Domain}, + Server = ?SEV_START("server", ServerSeq, ServerInitState), + + i("start tester evaluator"), + TesterInitState = #{domain => Domain, + msa => MSA, + server => Server#ev.pid}, + Tester = ?SEV_START("tester", TesterSeq, TesterInitState), + + i("await evaluator(s)"), + ok = ?SEV_AWAIT_FINISH([Tester, Server]). + + + +which_ip_multicast_address() -> + which_multicast_address(inet). + +which_multicast_address(Domain) -> + case os:type() of + {unix, linux} -> + WhichMAddr = fun([_, _, MAddr]) -> MAddr end, + which_multicast_address2(Domain, WhichMAddr); + + {unix, sunos} -> + WhichMAddr = fun([_, MAddr, _]) -> MAddr end, + which_multicast_address2(Domain, WhichMAddr); + + Type -> + not_supported({multicast, Type}) + end. + +%% Note that the 'netstat -g' table looks different on linux and SunOS +%% Linux: IfName - RefCnt - Group +%% SunOS: IfName - Group - RefCnt + +which_multicast_address2(Domain, WhichMAddr) -> + IfName = which_local_host_ifname(Domain), + NetstatGroupsStr = os:cmd("netstat -g | grep " ++ IfName), + NetstatGroups0 = string:tokens(NetstatGroupsStr, [$\n]), + NetstatGroups = [string:tokens(G, [$ ]) || G <- NetstatGroups0], + MAddrs = [WhichMAddr(NetstatGroup) || NetstatGroup <- + NetstatGroups], + which_multicast_address3(Domain, MAddrs). + +which_multicast_address3(_Domain, []) -> + not_supported({multicast, no_valid_addrs}); +which_multicast_address3(Domain, [MAddrStr|MAddrs]) -> + %% Even on linux some of these are not actually addresses, but + %% "host names", such as all-systems.mcast.net. But both + %% address strings, such as "224.0.0.251" and host name strings + %% gets translated into an address by the inet:inet:getaddr/2. + case inet:getaddr(MAddrStr, Domain) of + {ok, MAddr} -> + MAddr; + {error, _} -> + which_multicast_address3(Domain, MAddrs) + end. + +which_local_host_ifname(Domain) -> + case which_local_host_info(Domain) of + {ok, {Name, _Addr, _Flags}} -> + Name; + {error, Reason} -> + not_supported({multicast, Reason}) + end. + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% %% API OPERATIONS WITH TIMEOUT %% @@ -3434,11 +8064,11 @@ api_to_connect_tcp4(suite) -> api_to_connect_tcp4(doc) -> []; api_to_connect_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), Cond = fun() -> api_to_connect_cond() end, tc_try(api_to_connect_tcp4, Cond, fun() -> - ?TT(?SECS(10)), InitState = #{domain => inet, backlog => 1, timeout => 5000, @@ -3454,7 +8084,9 @@ api_to_connect_cond() -> %% So, just to simplify, we require atleast 4.15 api_to_connect_cond({unix, linux}, {Maj, Min, _Rev}) -> if - ((Maj >= 4) andalso (Min >= 15)) -> + (Maj > 4) -> + ok; + ((Maj =:= 4) andalso (Min >= 15)) -> ok; true -> skip("TC does not work") @@ -3491,10 +8123,10 @@ api_to_connect_tcp6(suite) -> api_to_connect_tcp6(doc) -> []; api_to_connect_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(api_to_connect_tcp6, fun() -> has_support_ipv6(), api_to_connect_cond() end, fun() -> - ?TT(?SECS(10)), InitState = #{domain => inet6, backlog => 1, timeout => 5000, @@ -3531,8 +8163,7 @@ api_to_connect_tcp(InitState) -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{local_sa => LSA}} end}, #{desc => "create listen socket", @@ -3603,8 +8234,7 @@ api_to_connect_tcp(InitState) -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{local_sa => LSA}} end}, #{desc => "create node", @@ -3754,8 +8384,7 @@ api_to_connect_tcp(InitState) -> end}, #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{local_sa => LSA}} end}, #{desc => "order server start", @@ -3906,9 +8535,7 @@ api_toc_tcp_client_await_terminate(Parent) -> end. api_to_connect_tcp_await_timeout(To, ServerSA, Domain, ConLimit) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, - addr => LAddr}, + LSA = which_local_socket_addr(Domain), NewSock = fun() -> S = case socket:open(Domain, stream, tcp) of {ok, Sock} -> @@ -3986,9 +8613,9 @@ api_to_accept_tcp4(suite) -> api_to_accept_tcp4(doc) -> []; api_to_accept_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(api_to_accept_tcp4, fun() -> - ?TT(?SECS(10)), InitState = #{domain => inet, timeout => 5000}, ok = api_to_accept_tcp(InitState) end). @@ -4003,10 +8630,10 @@ api_to_accept_tcp6(suite) -> api_to_accept_tcp6(doc) -> []; api_to_accept_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(api_to_accept_tcp4, fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(10)), InitState = #{domain => inet6, timeout => 5000}, ok = api_to_accept_tcp(InitState) end). @@ -4020,8 +8647,7 @@ api_to_accept_tcp(InitState) -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{lsa => LSA}} end}, #{desc => "create (listen) socket", @@ -4145,8 +8771,7 @@ api_to_maccept_tcp(InitState) -> end}, #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{lsa => LSA}} end}, #{desc => "create (listen) socket", @@ -4602,9 +9227,9 @@ api_to_recv_tcp4(suite) -> api_to_recv_tcp4(doc) -> []; api_to_recv_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(api_to_recv_tcp4, fun() -> - ?TT(?SECS(10)), Recv = fun(Sock, To) -> socket:recv(Sock, 0, To) end, InitState = #{domain => inet, recv => Recv, @@ -4622,12 +9247,12 @@ api_to_recv_tcp6(suite) -> api_to_recv_tcp6(doc) -> []; api_to_recv_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(api_to_recv_tcp6, fun() -> has_support_ipv6() end, fun() -> case socket:supports(ipv6) of true -> - ?TT(?SECS(10)), Recv = fun(Sock, To) -> socket:recv(Sock, 0, To) end, @@ -4663,8 +9288,7 @@ api_to_receive_tcp(InitState) -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{local_sa => LSA}} end}, #{desc => "create listen socket", @@ -4782,10 +9406,8 @@ api_to_receive_tcp(InitState) -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain, server_port := Port} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, - addr => LAddr}, - SSA = LSA#{port => Port}, + LSA = which_local_socket_addr(Domain), + SSA = LSA#{port => Port}, {ok, State#{local_sa => LSA, server_sa => SSA}} end}, #{desc => "create socket", @@ -4961,9 +9583,9 @@ api_to_recvfrom_udp4(suite) -> api_to_recvfrom_udp4(doc) -> []; api_to_recvfrom_udp4(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(api_to_recvfrom_udp4, fun() -> - ?TT(?SECS(10)), Recv = fun(Sock, To) -> socket:recvfrom(Sock, 0, To) end, InitState = #{domain => inet, recv => Recv, @@ -4981,10 +9603,10 @@ api_to_recvfrom_udp6(suite) -> api_to_recvfrom_udp6(doc) -> []; api_to_recvfrom_udp6(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(api_to_recvfrom_udp6, fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(10)), Recv = fun(Sock, To) -> socket:recvfrom(Sock, 0, To) end, InitState = #{domain => inet6, recv => Recv, @@ -5001,8 +9623,7 @@ api_to_receive_udp(InitState) -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{lsa => LSA}} end}, #{desc => "create socket", @@ -5078,9 +9699,9 @@ api_to_recvmsg_udp4(suite) -> api_to_recvmsg_udp4(doc) -> []; api_to_recvmsg_udp4(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(api_to_recvmsg_udp4, fun() -> - ?TT(?SECS(10)), Recv = fun(Sock, To) -> socket:recvmsg(Sock, To) end, InitState = #{domain => inet, recv => Recv, @@ -5098,10 +9719,10 @@ api_to_recvmsg_udp6(suite) -> api_to_recvmsg_udp6(doc) -> []; api_to_recvmsg_udp6(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(api_to_recvmsg_udp6, fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(10)), Recv = fun(Sock, To) -> socket:recvmsg(Sock, To) end, InitState = #{domain => inet6, recv => Recv, @@ -5119,9 +9740,9 @@ api_to_recvmsg_tcp4(suite) -> api_to_recvmsg_tcp4(doc) -> []; api_to_recvmsg_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(api_to_recvmsg_tcp4, fun() -> - ?TT(?SECS(10)), Recv = fun(Sock, To) -> socket:recvmsg(Sock, To) end, InitState = #{domain => inet, recv => Recv, @@ -5139,10 +9760,10 @@ api_to_recvmsg_tcp6(suite) -> api_to_recvmsg_tcp6(doc) -> []; api_to_recvmsg_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(api_to_recvmsg_tcp6, fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(10)), Recv = fun(Sock, To) -> socket:recvmsg(Sock, To) end, InitState = #{domain => inet6, recv => Recv, @@ -5170,9 +9791,9 @@ sc_cpe_socket_cleanup_tcp4(suite) -> sc_cpe_socket_cleanup_tcp4(doc) -> []; sc_cpe_socket_cleanup_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(5)), tc_try(sc_cpe_socket_cleanup_tcp4, fun() -> - ?TT(?SECS(5)), InitState = #{domain => inet, type => stream, protocol => tcp}, @@ -5190,10 +9811,10 @@ sc_cpe_socket_cleanup_tcp6(suite) -> sc_cpe_socket_cleanup_tcp6(doc) -> []; sc_cpe_socket_cleanup_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(5)), tc_try(sc_cpe_socket_cleanup_tcp6, fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(5)), InitState = #{domain => inet6, type => stream, protocol => tcp}, @@ -5204,6 +9825,27 @@ sc_cpe_socket_cleanup_tcp6(_Config) when is_list(_Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case is intended to test that the sockets are cleaned up %% ("removed") when the controlling process terminates (without explicitly +%% calling the close function). For a Unix Domain (stream) socket (TCP). + +sc_cpe_socket_cleanup_tcpL(suite) -> + []; +sc_cpe_socket_cleanup_tcpL(doc) -> + []; +sc_cpe_socket_cleanup_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(5)), + tc_try(sc_cpe_socket_cleanup_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + InitState = #{domain => local, + type => stream, + protocol => default}, + ok = sc_cpe_socket_cleanup(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test that the sockets are cleaned up +%% ("removed") when the controlling process terminates (without explicitly %% calling the close function). For a IPv4 UDP (dgram) socket. sc_cpe_socket_cleanup_udp4(suite) -> @@ -5211,9 +9853,9 @@ sc_cpe_socket_cleanup_udp4(suite) -> sc_cpe_socket_cleanup_udp4(doc) -> []; sc_cpe_socket_cleanup_udp4(_Config) when is_list(_Config) -> + ?TT(?SECS(5)), tc_try(sc_cpe_socket_cleanup_udp4, fun() -> - ?TT(?SECS(5)), InitState = #{domain => inet, type => dgram, protocol => udp}, @@ -5232,10 +9874,10 @@ sc_cpe_socket_cleanup_udp6(suite) -> sc_cpe_socket_cleanup_udp6(doc) -> []; sc_cpe_socket_cleanup_udp6(_Config) when is_list(_Config) -> + ?TT(?SECS(5)), tc_try(sc_cpe_socket_cleanup_udp6, fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(5)), InitState = #{domain => inet6, type => dgram, protocol => udp}, @@ -5244,6 +9886,28 @@ sc_cpe_socket_cleanup_udp6(_Config) when is_list(_Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test that the sockets are cleaned up +%% ("removed") when the controlling process terminates (without explicitly +%% calling the close function). For a Unix Domain (dgram) socket (UDP). + +sc_cpe_socket_cleanup_udpL(suite) -> + []; +sc_cpe_socket_cleanup_udpL(doc) -> + []; +sc_cpe_socket_cleanup_udpL(_Config) when is_list(_Config) -> + ?TT(?SECS(5)), + tc_try(sc_cpe_socket_cleanup_udpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + InitState = #{domain => local, + type => dgram, + protocol => default}, + ok = sc_cpe_socket_cleanup(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% sc_cpe_socket_cleanup(InitState) -> OwnerSeq = @@ -5386,12 +10050,11 @@ sc_lc_recv_response_tcp4(suite) -> sc_lc_recv_response_tcp4(doc) -> []; sc_lc_recv_response_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(sc_lc_recv_response_tcp4, fun() -> - ?TT(?SECS(10)), Recv = fun(Sock) -> socket:recv(Sock) end, InitState = #{domain => inet, - type => stream, protocol => tcp, recv => Recv}, ok = sc_lc_receive_response_tcp(InitState) @@ -5408,13 +10071,12 @@ sc_lc_recv_response_tcp6(suite) -> sc_lc_recv_response_tcp6(doc) -> []; sc_lc_recv_response_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(sc_lc_recv_response_tcp6, fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(10)), Recv = fun(Sock) -> socket:recv(Sock) end, InitState = #{domain => inet6, - type => stream, protocol => tcp, recv => Recv}, ok = sc_lc_receive_response_tcp(InitState) @@ -5422,6 +10084,28 @@ sc_lc_recv_response_tcp6(_Config) when is_list(_Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test what happens when a socket is +%% locally closed while the process is calling the recv function. +%% Socket is Unix Domain (stream) socket. + +sc_lc_recv_response_tcpL(suite) -> + []; +sc_lc_recv_response_tcpL(doc) -> + []; +sc_lc_recv_response_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(sc_lc_recv_response_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + Recv = fun(Sock) -> socket:recv(Sock) end, + InitState = #{domain => local, + protocol => default, + recv => Recv}, + ok = sc_lc_receive_response_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% sc_lc_receive_response_tcp(InitState) -> %% This (acceptor) is the server that accepts connections. @@ -5444,15 +10128,13 @@ sc_lc_receive_response_tcp(InitState) -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, - {ok, State#{local_sa => LSA}} + LSA = which_local_socket_addr(Domain), + {ok, State#{lsa => LSA}} end}, #{desc => "create (listen) socket", cmd => fun(#{domain := Domain, - type := Type, protocol := Proto} = State) -> - case socket:open(Domain, Type, Proto) of + case socket:open(Domain, stream, Proto) of {ok, Sock} -> {ok, State#{lsock => Sock}}; {error, _} = ERROR -> @@ -5460,9 +10142,22 @@ sc_lc_receive_response_tcp(InitState) -> end end}, #{desc => "bind to local address", - cmd => fun(#{lsock := LSock, local_sa := LSA} = State) -> + cmd => fun(#{domain := local, + lsock := LSock, + lsa := LSA} = _State) -> + ?SEV_IPRINT("bind to LSA: " + "~n ~p", [LSA]), + case socket:bind(LSock, LSA) of + {ok, _Port} -> + ok; % We do not care about the port for local + {error, _} = ERROR -> + ERROR + end; + (#{lsock := LSock, + lsa := LSA} = State) -> case socket:bind(LSock, LSA) of {ok, Port} -> + ?SEV_IPRINT("bound to port: ~w", [Port]), {ok, State#{lport => Port}}; {error, _} = ERROR -> ERROR @@ -5473,7 +10168,12 @@ sc_lc_receive_response_tcp(InitState) -> socket:listen(LSock) end}, #{desc => "announce ready (init)", - cmd => fun(#{tester := Tester, lport := Port}) -> + cmd => fun(#{domain := local, + tester := Tester, + lsa := #{path := Path}}) -> + ?SEV_ANNOUNCE_READY(Tester, init, Path), + ok; + (#{tester := Tester, lport := Port}) -> ?SEV_ANNOUNCE_READY(Tester, init, Port), ok end}, @@ -5494,7 +10194,8 @@ sc_lc_receive_response_tcp(InitState) -> cmd => fun(#{lsock := LSock} = State) -> case socket:accept(LSock) of {ok, Sock} -> - ?SEV_IPRINT("connection accepted"), + ?SEV_IPRINT("connection accepted: " + "~n ~p", [socket:sockname(Sock)]), {ok, State#{csock => Sock}}; {error, _} = ERROR -> ERROR @@ -5525,9 +10226,8 @@ sc_lc_receive_response_tcp(InitState) -> ?SEV_AWAIT_CONTINUE(Tester, tester, close), ok end}, - #{desc => "close the connection socket", + #{desc => "close connection socket", cmd => fun(#{csock := Sock} = State) -> - %% ok = socket:setopt(Sock, otp, debug, true), case socket:close(Sock) of ok -> {ok, maps:remove(csock, State)}; @@ -5551,8 +10251,19 @@ sc_lc_receive_response_tcp(InitState) -> ERROR end end}, - #{desc => "close socket", - cmd => fun(#{lsock := Sock} = State) -> + #{desc => "close listen socket", + cmd => fun(#{domain := local, + lsock := Sock, + lsa := #{path := Path}} = State) -> + ok = socket:close(Sock), + State1 = + unlink_path(Path, + fun() ->maps:remove(lsa, State) end, + fun() -> State end), + State2 = maps:remove(lsock, State1), + State3 = maps:remove(lport, State2), + {ok, State3}; + (#{lsock := Sock} = State) -> case socket:close(Sock) of ok -> State1 = maps:remove(lsock, State), @@ -5667,15 +10378,13 @@ sc_lc_receive_response_tcp(InitState) -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{local_sa => LSA}} end}, #{desc => "create socket", cmd => fun(#{domain := Domain, - type := Type, protocol := Proto} = State) -> - case socket:open(Domain, Type, Proto) of + case socket:open(Domain, stream, Proto) of {ok, Sock} -> {ok, State#{sock => Sock}}; {error, _} = ERROR -> @@ -5684,6 +10393,8 @@ sc_lc_receive_response_tcp(InitState) -> end}, #{desc => "bind socket to local address", cmd => fun(#{sock := Sock, local_sa := LSA} = _State) -> + ?SEV_IPRINT("bind to LSA: " + "~n ~p", [LSA]), case socket:bind(Sock, LSA) of {ok, _} -> ok; @@ -5699,7 +10410,19 @@ sc_lc_receive_response_tcp(InitState) -> %% The actual test #{desc => "await continue (connect)", - cmd => fun(#{tester := Tester, local_sa := LSA} = State) -> + cmd => fun(#{domain := local = Domain, + tester := Tester} = State) -> + case ?SEV_AWAIT_CONTINUE(Tester, tester, connect) of + {ok, ServerPath} -> + ?SEV_IPRINT("Server Path: " + "~n ~s", [ServerPath]), + ServerSA = #{family => Domain, + path => ServerPath}, + {ok, State#{server_sa => ServerSA}}; + {error, _} = ERROR -> + ERROR + end; + (#{tester := Tester, local_sa := LSA} = State) -> case ?SEV_AWAIT_CONTINUE(Tester, tester, connect) of {ok, Port} -> ServerSA = LSA#{port => Port}, @@ -5729,7 +10452,18 @@ sc_lc_receive_response_tcp(InitState) -> end end}, #{desc => "close socket", - cmd => fun(#{sock := Sock} = State) -> + cmd => fun(#{domain := local, + sock := Sock, + local_sa := #{path := Path}} = State) -> + sock_close(Sock), + State1 = + unlink_path(Path, + fun() -> + maps:remove(local_sa, State) + end, + fun() -> State end), + {ok, maps:remove(sock, State1)}; + (#{sock := Sock} = State) -> sock_close(Sock), {ok, maps:remove(sock, State)} end}, @@ -5776,8 +10510,8 @@ sc_lc_receive_response_tcp(InitState) -> #{desc => "await acceptor ready (init)", cmd => fun(#{acceptor := Pid} = State) -> case ?SEV_AWAIT_READY(Pid, acceptor, init) of - {ok, Port} -> - {ok, State#{lport => Port}}; + {ok, PortOrPath} -> + {ok, State#{server_info => PortOrPath}}; {error, _} = ERROR -> ERROR end @@ -5834,8 +10568,8 @@ sc_lc_receive_response_tcp(InitState) -> end}, ?SEV_SLEEP(?SECS(1)), #{desc => "order client to continue (connect)", - cmd => fun(#{client := Pid, lport := Port} = _State) -> - ?SEV_ANNOUNCE_CONTINUE(Pid, connect, Port), + cmd => fun(#{client := Pid, server_info := Info} = _State) -> + ?SEV_ANNOUNCE_CONTINUE(Pid, connect, Info), ok end}, #{desc => "await acceptor ready (accept)", @@ -6002,12 +10736,11 @@ sc_lc_recvfrom_response_udp4(suite) -> sc_lc_recvfrom_response_udp4(doc) -> []; sc_lc_recvfrom_response_udp4(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), tc_try(sc_lc_recvfrom_response_udp4, fun() -> - ?TT(?SECS(30)), Recv = fun(Sock, To) -> socket:recvfrom(Sock, [], To) end, InitState = #{domain => inet, - type => dgram, protocol => udp, recv => Recv}, ok = sc_lc_receive_response_udp(InitState) @@ -6024,13 +10757,36 @@ sc_lc_recvfrom_response_udp6(suite) -> sc_lc_recvfrom_response_udp6(doc) -> []; sc_lc_recvfrom_response_udp6(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), tc_try(sc_lc_recvfrom_response_udp6, fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(30)), Recv = fun(Sock, To) -> socket:recvfrom(Sock, [], To) end, - InitState = #{domain => inet6, - recv => Recv}, + InitState = #{domain => inet6, + protocol => udp, + recv => Recv}, + ok = sc_lc_receive_response_udp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test what happens when a socket is +%% locally closed while the process is calling the recv function. +%% Socket is Unix Domainm (dgram) socket. + +sc_lc_recvfrom_response_udpL(suite) -> + []; +sc_lc_recvfrom_response_udpL(doc) -> + []; +sc_lc_recvfrom_response_udpL(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), + tc_try(sc_lc_recvfrom_response_udpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + Recv = fun(Sock, To) -> socket:recvfrom(Sock, [], To) end, + InitState = #{domain => local, + protocol => default, + recv => Recv}, ok = sc_lc_receive_response_udp(InitState) end). @@ -6055,20 +10811,25 @@ sc_lc_receive_response_udp(InitState) -> %% *** Init part *** #{desc => "local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{local_sa => LSA}} end}, #{desc => "open socket", - cmd => fun(#{domain := Domain} = State) -> - Sock = sock_open(Domain, dgram, udp), + cmd => fun(#{domain := Domain, protocol := Proto} = State) -> + Sock = sock_open(Domain, dgram, Proto), SA = sock_sockname(Sock), {ok, State#{sock => Sock, sa => SA}} end}, #{desc => "bind socket", cmd => fun(#{sock := Sock, local_sa := LSA}) -> - sock_bind(Sock, LSA), - ok + case socket:bind(Sock, LSA) of + {ok, _Port} -> + ?SEV_IPRINT("src bound"), + ok; + {error, Reason} = ERROR -> + ?SEV_EPRINT("src bind failed: ~p", [Reason]), + ERROR + end end}, #{desc => "announce ready (init)", cmd => fun(#{tester := Tester, sock := Sock}) -> @@ -6107,7 +10868,18 @@ sc_lc_receive_response_udp(InitState) -> ok = ?SEV_AWAIT_CONTINUE(Tester, tester, close) end}, #{desc => "close socket", - cmd => fun(#{sock := Sock} = State) -> + cmd => fun(#{domain := local, + sock := Sock, + local_sa := #{path := Path}} = State) -> + ok = socket:close(Sock), + State1 = + unlink_path(Path, + fun() -> + maps:remove(local_sa, State) + end, + fun() -> State end), + {ok, maps:remove(sock, State1)}; + (#{sock := Sock} = State) -> case socket:close(Sock) of ok -> {ok, maps:remove(sock, State)}; @@ -6400,9 +11172,9 @@ sc_lc_receive_response_udp(InitState) -> i("start 'tester' evaluator"), TesterInitState = #{prim_server => PrimServer#ev.pid, - sec_server1 => SecServer1#ev.pid, - sec_server2 => SecServer2#ev.pid, - sec_server3 => SecServer3#ev.pid}, + sec_server1 => SecServer1#ev.pid, + sec_server2 => SecServer2#ev.pid, + sec_server3 => SecServer3#ev.pid}, Tester = ?SEV_START("tester", TesterSeq, TesterInitState), i("await evaluator"), @@ -6422,12 +11194,11 @@ sc_lc_recvmsg_response_tcp4(suite) -> sc_lc_recvmsg_response_tcp4(doc) -> []; sc_lc_recvmsg_response_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(sc_lc_recvmsg_response_tcp4, fun() -> - ?TT(?SECS(10)), Recv = fun(Sock) -> socket:recvmsg(Sock) end, InitState = #{domain => inet, - type => stream, protocol => tcp, recv => Recv}, ok = sc_lc_receive_response_tcp(InitState) @@ -6444,13 +11215,12 @@ sc_lc_recvmsg_response_tcp6(suite) -> sc_lc_recvmsg_response_tcp6(doc) -> []; sc_lc_recvmsg_response_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(sc_recvmsg_response_tcp6, fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(10)), Recv = fun(Sock) -> socket:recvmsg(Sock) end, InitState = #{domain => inet6, - type => stream, protocol => tcp, recv => Recv}, ok = sc_lc_receive_response_tcp(InitState) @@ -6460,6 +11230,28 @@ sc_lc_recvmsg_response_tcp6(_Config) when is_list(_Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case is intended to test what happens when a socket is %% locally closed while the process is calling the recvmsg function. +%% Socket is Unix Domain (stream) socket. + +sc_lc_recvmsg_response_tcpL(suite) -> + []; +sc_lc_recvmsg_response_tcpL(doc) -> + []; +sc_lc_recvmsg_response_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(sc_recvmsg_response_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + Recv = fun(Sock) -> socket:recvmsg(Sock) end, + InitState = #{domain => local, + protocol => default, + recv => Recv}, + ok = sc_lc_receive_response_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test what happens when a socket is +%% locally closed while the process is calling the recvmsg function. %% Socket is IPv4. sc_lc_recvmsg_response_udp4(suite) -> @@ -6471,8 +11263,9 @@ sc_lc_recvmsg_response_udp4(_Config) when is_list(_Config) -> fun() -> ?TT(?SECS(10)), Recv = fun(Sock, To) -> socket:recvmsg(Sock, To) end, - InitState = #{domain => inet, - recv => Recv}, + InitState = #{domain => inet, + protocol => udp, + recv => Recv}, ok = sc_lc_receive_response_udp(InitState) end). @@ -6492,8 +11285,32 @@ sc_lc_recvmsg_response_udp6(_Config) when is_list(_Config) -> fun() -> ?TT(?SECS(10)), Recv = fun(Sock, To) -> socket:recvmsg(Sock, To) end, - InitState = #{domain => inet6, - recv => Recv}, + InitState = #{domain => inet6, + protocol => udp, + recv => Recv}, + ok = sc_lc_receive_response_udp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test what happens when a socket is +%% locally closed while the process is calling the recvmsg function. +%% Socket is Unix Domain (dgram) socket. + +sc_lc_recvmsg_response_udpL(suite) -> + []; +sc_lc_recvmsg_response_udpL(doc) -> + []; +sc_lc_recvmsg_response_udpL(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(sc_recvmsg_response_udpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + Recv = fun(Sock, To) -> socket:recvmsg(Sock, To) end, + InitState = #{domain => local, + protocol => default, + recv => Recv}, ok = sc_lc_receive_response_udp(InitState) end). @@ -6511,11 +11328,10 @@ sc_lc_acceptor_response_tcp4(suite) -> sc_lc_acceptor_response_tcp4(doc) -> []; sc_lc_acceptor_response_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(sc_lc_acceptor_response_tcp4, fun() -> - ?TT(?SECS(10)), InitState = #{domain => inet, - type => stream, protocol => tcp}, ok = sc_lc_acceptor_response_tcp(InitState) end). @@ -6533,18 +11349,39 @@ sc_lc_acceptor_response_tcp6(suite) -> sc_lc_acceptor_response_tcp6(doc) -> []; sc_lc_acceptor_response_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), tc_try(sc_lc_acceptor_response_tcp6, fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(10)), - InitState = #{domain => inet, - type => stream, + InitState = #{domain => inet6, protocol => tcp}, ok = sc_lc_acceptor_response_tcp(InitState) end). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test what happens when a socket is +%% locally closed while the process is calling the accept function. +%% We test what happens with a non-controlling_process also, since we +%% git the setup anyway. +%% Socket is Unix Domain (stream) socket. + +sc_lc_acceptor_response_tcpL(suite) -> + []; +sc_lc_acceptor_response_tcpL(doc) -> + []; +sc_lc_acceptor_response_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(sc_lc_acceptor_response_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + InitState = #{domain => local, + protocol => default}, + ok = sc_lc_acceptor_response_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% sc_lc_acceptor_response_tcp(InitState) -> PrimAcceptorSeq = @@ -6564,15 +11401,13 @@ sc_lc_acceptor_response_tcp(InitState) -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{lsa => LSA}} end}, #{desc => "create (listen) socket", cmd => fun(#{domain := Domain, - type := Type, protocol := Proto} = State) -> - case socket:open(Domain, Type, Proto) of + case socket:open(Domain, stream, Proto) of {ok, Sock} -> {ok, State#{sock => Sock}}; {error, _} = ERROR -> @@ -6609,8 +11444,8 @@ sc_lc_acceptor_response_tcp(InitState) -> end end}, #{desc => "await connection", - cmd => fun(#{sock := Sock, timeout := Timeout} = _State) -> - case socket:accept(Sock, Timeout) of + cmd => fun(#{sock := LSock, timeout := Timeout} = _State) -> + case socket:accept(LSock, Timeout) of {error, timeout} -> ok; {ok, Sock} -> @@ -6631,7 +11466,25 @@ sc_lc_acceptor_response_tcp(InitState) -> ok = ?SEV_AWAIT_CONTINUE(Tester, tester, close) end}, #{desc => "close socket", - cmd => fun(#{sock := Sock} = State) -> + cmd => fun(#{domain := local, + sock := Sock, + lsa := #{path := Path}} = State) -> + case socket:close(Sock) of + ok -> + State1 = + unlink_path(Path, + fun() -> + maps:remove(lsa, State) + end, + fun() -> + State + end), + {ok, maps:remove(sock, State1)}; + {error, _} = ERROR -> + unlink_path(Path), + ERROR + end; + (#{sock := Sock} = State) -> case socket:close(Sock) of ok -> {ok, maps:remove(sock, State)}; @@ -6946,12 +11799,11 @@ sc_rc_recv_response_tcp4(suite) -> sc_rc_recv_response_tcp4(doc) -> []; sc_rc_recv_response_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), tc_try(sc_rc_recv_response_tcp4, fun() -> - ?TT(?SECS(30)), Recv = fun(Sock) -> socket:recv(Sock) end, InitState = #{domain => inet, - type => stream, protocol => tcp, recv => Recv}, ok = sc_rc_receive_response_tcp(InitState) @@ -6968,13 +11820,12 @@ sc_rc_recv_response_tcp6(suite) -> sc_rc_recv_response_tcp6(doc) -> []; sc_rc_recv_response_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), tc_try(sc_rc_recv_response_tcp6, fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(10)), Recv = fun(Sock) -> socket:recv(Sock) end, InitState = #{domain => inet6, - type => stream, protocol => tcp, recv => Recv}, ok = sc_rc_receive_response_tcp(InitState) @@ -6982,6 +11833,28 @@ sc_rc_recv_response_tcp6(_Config) when is_list(_Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test what happens when a socket is +%% remotely closed while the process is calling the recv function. +%% Socket is Unix Domain (stream) socket. + +sc_rc_recv_response_tcpL(suite) -> + []; +sc_rc_recv_response_tcpL(doc) -> + []; +sc_rc_recv_response_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), + tc_try(sc_rc_recv_response_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + Recv = fun(Sock) -> socket:recv(Sock) end, + InitState = #{domain => local, + protocol => default, + recv => Recv}, + ok = sc_rc_receive_response_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% sc_rc_receive_response_tcp(InitState) -> %% Each connection are handled by handler processes. @@ -7004,13 +11877,12 @@ sc_rc_receive_response_tcp(InitState) -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{local_sa => LSA}} end}, #{desc => "create listen socket", - cmd => fun(#{domain := Domain} = State) -> - case socket:open(Domain, stream, tcp) of + cmd => fun(#{domain := Domain, protocol := Proto} = State) -> + case socket:open(Domain, stream, Proto) of {ok, Sock} -> {ok, State#{lsock => Sock}}; {error, _} = ERROR -> @@ -7018,7 +11890,17 @@ sc_rc_receive_response_tcp(InitState) -> end end}, #{desc => "bind to local address", - cmd => fun(#{lsock := LSock, local_sa := LSA} = State) -> + cmd => fun(#{domain := local, + lsock := LSock, + lsa := LSA} = _State) -> + case socket:bind(LSock, LSA) of + {ok, _Port} -> + ok; % We do not care about the port for local + {error, _} = ERROR -> + ERROR + end; + (#{lsock := LSock, + local_sa := LSA} = State) -> case socket:bind(LSock, LSA) of {ok, Port} -> {ok, State#{lport => Port}}; @@ -7031,7 +11913,15 @@ sc_rc_receive_response_tcp(InitState) -> socket:listen(LSock) end}, #{desc => "announce ready (init)", - cmd => fun(#{tester := Tester, local_sa := LSA, lport := Port}) -> + cmd => fun(#{domain := local, + tester := Tester, + local_sa := LSA}) -> + %% Actually we only need to send the path, + %% but to keep it simple, we send the "same" + %% as for non-local. + ?SEV_ANNOUNCE_READY(Tester, init, LSA), + ok; + (#{tester := Tester, local_sa := LSA, lport := Port}) -> ServerSA = LSA#{port => Port}, ?SEV_ANNOUNCE_READY(Tester, init, ServerSA), ok @@ -7213,7 +12103,25 @@ sc_rc_receive_response_tcp(InitState) -> {ok, State2} end}, #{desc => "close listen socket", - cmd => fun(#{lsock := LSock} = State) -> + cmd => fun(#{domain := local, + lsock := LSock, + lsa := #{path := Path}} = State) -> + case socket:close(LSock) of + ok -> + State1 = + unlink_path(Path, + fun() -> + maps:remove(lsa, State) + end, + fun() -> + State + end), + {ok, maps:remove(lsock, State1)}; + {error, _} = ERROR -> + unlink_path(Path), + ERROR + end; + (#{lsock := LSock} = State) -> case socket:close(LSock) of ok -> {ok, maps:remove(lsock, State)}; @@ -7270,8 +12178,10 @@ sc_rc_receive_response_tcp(InitState) -> ok end}, #{desc => "order remote client to start", - cmd => fun(#{rclient := Client, server_sa := ServerSA}) -> - ?SEV_ANNOUNCE_START(Client, ServerSA), + cmd => fun(#{rclient := Client, + server_sa := ServerSA, + protocol := Proto}) -> + ?SEV_ANNOUNCE_START(Client, {ServerSA, Proto}), ok end}, #{desc => "await remote client ready", @@ -7672,16 +12582,16 @@ sc_rc_tcp_client_start(Node) -> sc_rc_tcp_client(Parent) -> sc_rc_tcp_client_init(Parent), - ServerSA = sc_rc_tcp_client_await_start(Parent), + {ServerSA, Proto} = sc_rc_tcp_client_await_start(Parent), Domain = maps:get(family, ServerSA), - Sock = sc_rc_tcp_client_create(Domain), - sc_rc_tcp_client_bind(Sock, Domain), + Sock = sc_rc_tcp_client_create(Domain, Proto), + Path = sc_rc_tcp_client_bind(Sock, Domain), sc_rc_tcp_client_announce_ready(Parent, init), sc_rc_tcp_client_await_continue(Parent, connect), sc_rc_tcp_client_connect(Sock, ServerSA), sc_rc_tcp_client_announce_ready(Parent, connect), sc_rc_tcp_client_await_continue(Parent, close), - sc_rc_tcp_client_close(Sock), + sc_rc_tcp_client_close(Sock, Path), sc_rc_tcp_client_announce_ready(Parent, close), Reason = sc_rc_tcp_client_await_terminate(Parent), ?SEV_IPRINT("terminate"), @@ -7697,9 +12607,9 @@ sc_rc_tcp_client_await_start(Parent) -> i("sc_rc_tcp_client_await_start -> entry"), ?SEV_AWAIT_START(Parent). -sc_rc_tcp_client_create(Domain) -> +sc_rc_tcp_client_create(Domain, Proto) -> i("sc_rc_tcp_client_create -> entry"), - case socket:open(Domain, stream, tcp) of + case socket:open(Domain, stream, Proto) of {ok, Sock} -> case socket:getopt(Sock, otp, fd) of {ok, FD} -> @@ -7714,12 +12624,17 @@ sc_rc_tcp_client_create(Domain) -> sc_rc_tcp_client_bind(Sock, Domain) -> i("sc_rc_tcp_client_bind -> entry"), - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, - addr => LAddr}, + LSA = which_local_socket_addr(Domain), case socket:bind(Sock, LSA) of {ok, _} -> - ok; + case socket:sockname(Sock) of + {ok, #{family := local, path := Path}} -> + Path; + {ok, _} -> + undefined; + {error, Reason1} -> + exit({sockname, Reason1}) + end; {error, Reason} -> exit({bind, Reason}) end. @@ -7741,13 +12656,17 @@ sc_rc_tcp_client_connect(Sock, ServerSA) -> exit({connect, Reason}) end. -sc_rc_tcp_client_close(Sock) -> +sc_rc_tcp_client_close(Sock, Path) -> i("sc_rc_tcp_client_close -> entry"), case socket:close(Sock) of ok -> + unlink_path(Path), ok; {error, Reason} -> - exit({close, Reason}) + ?SEV_EPRINT("failed closing: " + "~n Reason: ~p", [Reason]), + unlink_path(Path), + {error, {close, Reason}} end. sc_rc_tcp_client_await_terminate(Parent) -> @@ -7827,12 +12746,11 @@ sc_rc_recvmsg_response_tcp4(suite) -> sc_rc_recvmsg_response_tcp4(doc) -> []; sc_rc_recvmsg_response_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), tc_try(sc_rc_recvmsg_response_tcp4, fun() -> - ?TT(?SECS(30)), Recv = fun(Sock) -> socket:recvmsg(Sock) end, InitState = #{domain => inet, - type => stream, protocol => tcp, recv => Recv}, ok = sc_rc_receive_response_tcp(InitState) @@ -7849,13 +12767,12 @@ sc_rc_recvmsg_response_tcp6(suite) -> sc_rc_recvmsg_response_tcp6(doc) -> []; sc_rc_recvmsg_response_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), tc_try(sc_rc_recvmsg_response_tcp6, fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(10)), Recv = fun(Sock) -> socket:recvmsg(Sock) end, InitState = #{domain => inet6, - type => stream, protocol => tcp, recv => Recv}, ok = sc_rc_receive_response_tcp(InitState) @@ -7863,6 +12780,28 @@ sc_rc_recvmsg_response_tcp6(_Config) when is_list(_Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test what happens when a socket is +%% remotely closed while the process is calling the recvmsg function. +%% Socket is Unix Domain (stream) socket. + +sc_rc_recvmsg_response_tcpL(suite) -> + []; +sc_rc_recvmsg_response_tcpL(doc) -> + []; +sc_rc_recvmsg_response_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), + tc_try(sc_rc_recvmsg_response_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + Recv = fun(Sock) -> socket:recvmsg(Sock) end, + InitState = #{domain => local, + protocol => default, + recv => Recv}, + ok = sc_rc_receive_response_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case is intended to test what happens when a socket is %% remotely closed while the process is calling the recv function. %% The remote client sends data, then shutdown(write) and then the @@ -7872,6 +12811,7 @@ sc_rc_recvmsg_response_tcp6(_Config) when is_list(_Config) -> %% To minimize the chance of "weirdness", we should really have test cases %% where the two sides of the connection is on different machines. But for %% now, we will make do with different VMs on the same host. +%% This would of course not work for Unix Domain sockets. %% sc_rs_recv_send_shutdown_receive_tcp4(suite) -> @@ -7879,9 +12819,9 @@ sc_rs_recv_send_shutdown_receive_tcp4(suite) -> sc_rs_recv_send_shutdown_receive_tcp4(doc) -> []; sc_rs_recv_send_shutdown_receive_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), tc_try(sc_rs_recv_send_shutdown_receive_tcp4, fun() -> - ?TT(?SECS(30)), MsgData = ?DATA, Recv = fun(Sock) -> socket:recv(Sock) @@ -7890,6 +12830,7 @@ sc_rs_recv_send_shutdown_receive_tcp4(_Config) when is_list(_Config) -> socket:send(Sock, Data) end, InitState = #{domain => inet, + proto => tcp, recv => Recv, send => Send, data => MsgData}, @@ -7921,6 +12862,39 @@ sc_rs_recv_send_shutdown_receive_tcp6(_Config) when is_list(_Config) -> socket:send(Sock, Data) end, InitState = #{domain => inet6, + proto => tcp, + recv => Recv, + send => Send, + data => MsgData}, + ok = sc_rs_send_shutdown_receive_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test what happens when a socket is +%% remotely closed while the process is calling the recv function. +%% The remote client sends data, then shutdown(write) and then the +%% reader attempts a recv. +%% Socket is Unix Domain (stream) socket. + +sc_rs_recv_send_shutdown_receive_tcpL(suite) -> + []; +sc_rs_recv_send_shutdown_receive_tcpL(doc) -> + []; +sc_rs_recv_send_shutdown_receive_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(sc_rs_recv_send_shutdown_receive_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + MsgData = ?DATA, + Recv = fun(Sock) -> + socket:recv(Sock) + end, + Send = fun(Sock, Data) -> + socket:send(Sock, Data) + end, + InitState = #{domain => local, + proto => default, recv => Recv, send => Send, data => MsgData}, @@ -7952,13 +12926,12 @@ sc_rs_send_shutdown_receive_tcp(InitState) -> #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> i("get local address for ~p", [Domain]), - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{local_sa => LSA}} end}, #{desc => "create listen socket", - cmd => fun(#{domain := Domain} = State) -> - case socket:open(Domain, stream, tcp) of + cmd => fun(#{domain := Domain, proto := Proto} = State) -> + case socket:open(Domain, stream, Proto) of {ok, Sock} -> {ok, State#{lsock => Sock}}; {error, _} = ERROR -> @@ -7966,9 +12939,19 @@ sc_rs_send_shutdown_receive_tcp(InitState) -> end end}, #{desc => "bind to local address", - cmd => fun(#{lsock := LSock, local_sa := LSA} = State) -> + cmd => fun(#{domain := local, + lsock := LSock, + local_sa := LSA} = _State) -> + case socket:bind(LSock, LSA) of + {ok, _Port} -> + ok; % We do not care about the port for local + {error, _} = ERROR -> + ERROR + end; + (#{lsock := LSock, local_sa := LSA} = State) -> case socket:bind(LSock, LSA) of {ok, Port} -> + ?SEV_IPRINT("bound to port: ~w", [Port]), {ok, State#{lport => Port}}; {error, _} = ERROR -> ERROR @@ -7979,7 +12962,11 @@ sc_rs_send_shutdown_receive_tcp(InitState) -> socket:listen(LSock) end}, #{desc => "announce ready (init)", - cmd => fun(#{tester := Tester, local_sa := LSA, lport := Port}) -> + cmd => fun(#{domain := local, + tester := Tester, local_sa := LSA}) -> + ?SEV_ANNOUNCE_READY(Tester, init, LSA), + ok; + (#{tester := Tester, local_sa := LSA, lport := Port}) -> ServerSA = LSA#{port => Port}, ?SEV_ANNOUNCE_READY(Tester, init, ServerSA), ok @@ -8090,7 +13077,18 @@ sc_rs_send_shutdown_receive_tcp(InitState) -> {ok, State2} end}, #{desc => "close listen socket", - cmd => fun(#{lsock := LSock} = State) -> + cmd => fun(#{domain := local, + lsock := Sock, + local_sa := #{path := Path}} = State) -> + socket:close(Sock), + State1 = + unlink_path(Path, + fun() -> + maps:remove(local_sa, State) + end, + fun() -> State end), + {ok, maps:remove(lsock, State1)}; + (#{lsock := LSock} = State) -> case socket:close(LSock) of ok -> {ok, maps:remove(lsock, State)}; @@ -8148,8 +13146,10 @@ sc_rs_send_shutdown_receive_tcp(InitState) -> ok end}, #{desc => "order remote client to start", - cmd => fun(#{rclient := Client, server_sa := ServerSA}) -> - ?SEV_ANNOUNCE_START(Client, ServerSA), + cmd => fun(#{rclient := Client, + proto := Proto, + server_sa := ServerSA}) -> + ?SEV_ANNOUNCE_START(Client, {ServerSA, Proto}), ok end}, #{desc => "await remote client ready", @@ -8479,12 +13479,14 @@ sc_rs_send_shutdown_receive_tcp(InitState) -> i("start server evaluator"), ServerInitState = #{domain => maps:get(domain, InitState), + proto => maps:get(proto, InitState), recv => maps:get(recv, InitState)}, Server = ?SEV_START("server", ServerSeq, ServerInitState), i("start client evaluator"), ClientInitState = #{host => local_host(), domain => maps:get(domain, InitState), + proto => maps:get(proto, InitState), send => maps:get(send, InitState)}, Client = ?SEV_START("client", ClientSeq, ClientInitState), @@ -8506,10 +13508,10 @@ sc_rs_tcp_client_start(Node, Send) -> sc_rs_tcp_client(Parent, Send) -> sc_rs_tcp_client_init(Parent), - ServerSA = sc_rs_tcp_client_await_start(Parent), + {ServerSA, Proto} = sc_rs_tcp_client_await_start(Parent), Domain = maps:get(family, ServerSA), - Sock = sc_rs_tcp_client_create(Domain), - sc_rs_tcp_client_bind(Sock, Domain), + Sock = sc_rs_tcp_client_create(Domain, Proto), + Path = sc_rs_tcp_client_bind(Sock, Domain), sc_rs_tcp_client_announce_ready(Parent, init), sc_rs_tcp_client_await_continue(Parent, connect), sc_rs_tcp_client_connect(Sock, ServerSA), @@ -8521,7 +13523,7 @@ sc_rs_tcp_client(Parent, Send) -> sc_rs_tcp_client_shutdown(Sock), sc_rs_tcp_client_announce_ready(Parent, shutdown), sc_rs_tcp_client_await_continue(Parent, close), - sc_rs_tcp_client_close(Sock), + sc_rs_tcp_client_close(Sock, Path), sc_rs_tcp_client_announce_ready(Parent, close), Reason = sc_rs_tcp_client_await_terminate(Parent), ?SEV_IPRINT("terminate"), @@ -8537,9 +13539,9 @@ sc_rs_tcp_client_await_start(Parent) -> i("sc_rs_tcp_client_await_start -> entry"), ?SEV_AWAIT_START(Parent). -sc_rs_tcp_client_create(Domain) -> +sc_rs_tcp_client_create(Domain, Proto) -> i("sc_rs_tcp_client_create -> entry"), - case socket:open(Domain, stream, tcp) of + case socket:open(Domain, stream, Proto) of {ok, Sock} -> Sock; {error, Reason} -> @@ -8548,12 +13550,17 @@ sc_rs_tcp_client_create(Domain) -> sc_rs_tcp_client_bind(Sock, Domain) -> i("sc_rs_tcp_client_bind -> entry"), - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, - addr => LAddr}, + LSA = which_local_socket_addr(Domain), case socket:bind(Sock, LSA) of {ok, _} -> - ok; + case socket:sockname(Sock) of + {ok, #{family := local, path := Path}} -> + Path; + {ok, _} -> + undefined; + {error, Reason1} -> + exit({sockname, Reason1}) + end; {error, Reason} -> exit({bind, Reason}) end. @@ -8600,13 +13607,17 @@ sc_rs_tcp_client_shutdown(Sock) -> exit({shutdown, Reason}) end. -sc_rs_tcp_client_close(Sock) -> +sc_rs_tcp_client_close(Sock, Path) -> i("sc_rs_tcp_client_close -> entry"), case socket:close(Sock) of ok -> + unlink_path(Path), ok; {error, Reason} -> - exit({close, Reason}) + ?SEV_EPRINT("failed closing: " + "~n Reason: ~p", [Reason]), + unlink_path(Path), + {error, {close, Reason}} end. sc_rs_tcp_client_await_terminate(Parent) -> @@ -8711,12 +13722,11 @@ sc_rs_recvmsg_send_shutdown_receive_tcp4(_Config) when is_list(_Config) -> MsgHdr = #{iov => [Data]}, socket:sendmsg(Sock, MsgHdr) end, - InitState = #{domain => inet, - type => stream, - protocol => tcp, - recv => Recv, - send => Send, - data => MsgData}, + InitState = #{domain => inet, + proto => tcp, + recv => Recv, + send => Send, + data => MsgData}, ok = sc_rs_send_shutdown_receive_tcp(InitState) end). @@ -8748,15 +13758,62 @@ sc_rs_recvmsg_send_shutdown_receive_tcp6(_Config) when is_list(_Config) -> end end, Send = fun(Sock, Data) when is_binary(Data) -> - MsgHdr = #{iov => [Data]}, - socket:sendmsg(Sock, MsgHdr) + MsgHdr = #{iov => [Data]}, + socket:sendmsg(Sock, MsgHdr) end, - InitState = #{domain => inet6, - type => stream, - protocol => tcp, - recv => Recv, - send => Send, - data => MsgData}, + InitState = #{domain => inet6, + proto => tcp, + recv => Recv, + send => Send, + data => MsgData}, + ok = sc_rs_send_shutdown_receive_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test what happens when a socket is +%% remotely closed while the process is calling the recvmsg function. +%% The remote client sends data, then shutdown(write) and then the +%% reader attempts a recv. +%% Socket is UNix Domain (stream) socket. + +sc_rs_recvmsg_send_shutdown_receive_tcpL(suite) -> + []; +sc_rs_recvmsg_send_shutdown_receive_tcpL(doc) -> + []; +sc_rs_recvmsg_send_shutdown_receive_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(10)), + tc_try(sc_rs_recvmsg_send_shutdown_receive_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + {ok, CWD} = file:get_cwd(), + ?SEV_IPRINT("CWD: ~s", [CWD]), + MsgData = ?DATA, + Recv = fun(Sock) -> + case socket:recvmsg(Sock) of + %% On some platforms, the address + %% is *not* provided (e.g. FreeBSD) + {ok, #{addr := undefined, + iov := [Data]}} -> + {ok, Data}; + %% On some platforms, the address + %% *is* provided (e.g. linux) + {ok, #{addr := #{family := local}, + iov := [Data]}} -> + {ok, Data}; + {error, _} = ERROR -> + ERROR + end + end, + Send = fun(Sock, Data) when is_binary(Data) -> + MsgHdr = #{iov => [Data]}, + socket:sendmsg(Sock, MsgHdr) + end, + InitState = #{domain => local, + proto => default, + recv => Recv, + send => Send, + data => MsgData}, ok = sc_rs_send_shutdown_receive_tcp(InitState) end). @@ -8773,10 +13830,11 @@ traffic_send_and_recv_chunks_tcp4(suite) -> traffic_send_and_recv_chunks_tcp4(doc) -> []; traffic_send_and_recv_chunks_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), tc_try(traffic_send_and_recv_chunks_tcp4, fun() -> - ?TT(?SECS(30)), - InitState = #{domain => inet}, + InitState = #{domain => inet, + proto => tcp}, ok = traffic_send_and_recv_chunks_tcp(InitState) end). @@ -8794,11 +13852,34 @@ traffic_send_and_recv_chunks_tcp6(suite) -> traffic_send_and_recv_chunks_tcp6(doc) -> []; traffic_send_and_recv_chunks_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), tc_try(traffic_send_and_recv_chunks_tcp6, fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(30)), - InitState = #{domain => inet6}, + InitState = #{domain => inet6, + proto => tcp}, + ok = traffic_send_and_recv_chunks_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test that the send and recv functions +%% behave as expected when sending and/or reading chunks. +%% First send data in one "big" chunk, and read it in "small" chunks. +%% Second, send in a bunch of "small" chunks, and read in one "big" chunk. +%% Socket is UNix Domain (Stream) socket. + +traffic_send_and_recv_chunks_tcpL(suite) -> + []; +traffic_send_and_recv_chunks_tcpL(doc) -> + []; +traffic_send_and_recv_chunks_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), + tc_try(traffic_send_and_recv_chunks_tcp6, + fun() -> has_support_unix_domain_socket() end, + fun() -> + InitState = #{domain => local, + proto => default}, ok = traffic_send_and_recv_chunks_tcp(InitState) end). @@ -8823,13 +13904,12 @@ traffic_send_and_recv_chunks_tcp(InitState) -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{local_sa => LSA}} end}, #{desc => "create listen socket", - cmd => fun(#{domain := Domain} = State) -> - case socket:open(Domain, stream, tcp) of + cmd => fun(#{domain := Domain, proto := Proto} = State) -> + case socket:open(Domain, stream, Proto) of {ok, Sock} -> {ok, State#{lsock => Sock}}; {error, _} = ERROR -> @@ -8837,7 +13917,17 @@ traffic_send_and_recv_chunks_tcp(InitState) -> end end}, #{desc => "bind to local address", - cmd => fun(#{lsock := LSock, local_sa := LSA} = State) -> + cmd => fun(#{domain := local, + lsock := LSock, + local_sa := LSA} = _State) -> + case socket:bind(LSock, LSA) of + {ok, _Port} -> + ok; % We do not care about the port for local + {error, _} = ERROR -> + ERROR + end; + (#{lsock := LSock, + local_sa := LSA} = State) -> case socket:bind(LSock, LSA) of {ok, Port} -> {ok, State#{lport => Port}}; @@ -8850,7 +13940,14 @@ traffic_send_and_recv_chunks_tcp(InitState) -> socket:listen(LSock) end}, #{desc => "announce ready (init)", - cmd => fun(#{tester := Tester, local_sa := LSA, lport := Port}) -> + cmd => fun(#{domain := local, + tester := Tester, + local_sa := LSA}) -> + ?SEV_ANNOUNCE_READY(Tester, init, LSA), + ok; + (#{tester := Tester, + local_sa := LSA, + lport := Port}) -> ServerSA = LSA#{port => Port}, ?SEV_ANNOUNCE_READY(Tester, init, ServerSA), ok @@ -9045,9 +14142,20 @@ traffic_send_and_recv_chunks_tcp(InitState) -> {ok, maps:remove(csock, State)} end}, #{desc => "close listen socket", - cmd => fun(#{lsock := Sock} = State) -> + cmd => fun(#{domain := local, + lsock := Sock, + local_sa := #{path := Path}} = State) -> + ok = socket:close(Sock), + State1 = + unlink_path(Path, + fun() -> + maps:remove(local_sa, State) + end, + fun() -> State end), + {ok, maps:remove(lsock, State1)}; + (#{lsock := Sock} = State) -> (catch socket:close(Sock)), - {ok, maps:remove(lsock, State)} + {ok, maps:remove(lsock, State)} end}, %% *** We are done *** @@ -9098,8 +14206,10 @@ traffic_send_and_recv_chunks_tcp(InitState) -> ok end}, #{desc => "order remote client to start", - cmd => fun(#{rclient := Client, server_sa := ServerSA}) -> - ?SEV_ANNOUNCE_START(Client, ServerSA), + cmd => fun(#{rclient := Client, + server_sa := ServerSA, + proto := Proto}) -> + ?SEV_ANNOUNCE_START(Client, {ServerSA, Proto}), ok end}, #{desc => "await remote client ready", @@ -9654,14 +14764,14 @@ traffic_snr_tcp_client_start(Node) -> erlang:spawn(Node, Fun). traffic_snr_tcp_client(Parent) -> - {Sock, ServerSA} = traffic_snr_tcp_client_init(Parent), + {Sock, ServerSA, Path} = traffic_snr_tcp_client_init(Parent), traffic_snr_tcp_client_announce_ready(Parent, init), traffic_snr_tcp_client_await_continue(Parent, connect), traffic_snr_tcp_client_connect(Sock, ServerSA), traffic_snr_tcp_client_announce_ready(Parent, connect), traffic_snr_tcp_client_send_loop(Parent, Sock), Reason = traffic_snr_tcp_client_await_terminate(Parent), - traffic_snr_tcp_client_close(Sock), + traffic_snr_tcp_client_close(Sock, Path), exit(Reason). @@ -9687,19 +14797,19 @@ traffic_snr_tcp_client_init(Parent) -> put(sname, "rclient"), ?SEV_IPRINT("init"), _MRef = erlang:monitor(process, Parent), - ServerSA = traffic_snr_tcp_client_await_start(Parent), + {ServerSA, Proto} = traffic_snr_tcp_client_await_start(Parent), Domain = maps:get(family, ServerSA), - Sock = traffic_snr_tcp_client_create(Domain), - traffic_snr_tcp_client_bind(Sock, Domain), - {Sock, ServerSA}. + Sock = traffic_snr_tcp_client_create(Domain, Proto), + Path = traffic_snr_tcp_client_bind(Sock, Domain), + {Sock, ServerSA, Path}. traffic_snr_tcp_client_await_start(Parent) -> i("traffic_snr_tcp_client_await_start -> entry"), ?SEV_AWAIT_START(Parent). -traffic_snr_tcp_client_create(Domain) -> +traffic_snr_tcp_client_create(Domain, Proto) -> i("traffic_snr_tcp_client_create -> entry"), - case socket:open(Domain, stream, tcp) of + case socket:open(Domain, stream, Proto) of {ok, Sock} -> Sock; {error, Reason} -> @@ -9708,12 +14818,17 @@ traffic_snr_tcp_client_create(Domain) -> traffic_snr_tcp_client_bind(Sock, Domain) -> i("traffic_snr_tcp_client_bind -> entry"), - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, - addr => LAddr}, + LSA = which_local_socket_addr(Domain), case socket:bind(Sock, LSA) of {ok, _} -> - ok; + case socket:sockname(Sock) of + {ok, #{family := local, path := Path}} -> + Path; + {ok, _} -> + undefined; + {error, Reason1} -> + exit({sockname, Reason1}) + end; {error, Reason} -> exit({bind, Reason}) end. @@ -9734,13 +14849,17 @@ traffic_snr_tcp_client_connect(Sock, ServerSA) -> exit({connect, Reason}) end. -traffic_snr_tcp_client_close(Sock) -> +traffic_snr_tcp_client_close(Sock, Path) -> i("traffic_snr_tcp_client_close -> entry"), case socket:close(Sock) of ok -> + unlink_path(Path), ok; {error, Reason} -> - exit({close, Reason}) + ?SEV_EPRINT("failed closing: " + "~n Reason: ~p", [Reason]), + unlink_path(Path), + {error, {close, Reason}} end. traffic_snr_tcp_client_await_terminate(Parent) -> @@ -9768,12 +14887,13 @@ traffic_ping_pong_small_send_and_recv_tcp4(suite) -> traffic_ping_pong_small_send_and_recv_tcp4(doc) -> []; traffic_ping_pong_small_send_and_recv_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(15)), Msg = l2b(?TPP_SMALL), Num = ?TPP_SMALL_NUM, tc_try(traffic_ping_pong_small_send_and_recv_tcp4, fun() -> - ?TT(?SECS(15)), InitState = #{domain => inet, + proto => tcp, msg => Msg, num => Num}, ok = traffic_ping_pong_send_and_recv_tcp(InitState) @@ -9795,13 +14915,42 @@ traffic_ping_pong_small_send_and_recv_tcp6(suite) -> traffic_ping_pong_small_send_and_recv_tcp6(doc) -> []; traffic_ping_pong_small_send_and_recv_tcp6(_Config) when is_list(_Config) -> + ?TT(?SECS(15)), Msg = l2b(?TPP_SMALL), Num = ?TPP_SMALL_NUM, tc_try(traffic_ping_pong_small_send_and_recv_tcp6, fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(15)), InitState = #{domain => inet6, + proto => tcp, + msg => Msg, + num => Num}, + ok = traffic_ping_pong_send_and_recv_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test that the send and recv functions +%% by repeatedly sending a meassage between two entities. +%% The same basic test case is used for three different message sizes; +%% small (8 bytes), medium (8K) and large (8M). +%% The message is sent from A to B and then back again. This is +%% repeated a set number of times (more times the small the message). +%% This is the 'small' message test case, for Unix Domain (stream) socket. + +traffic_ping_pong_small_send_and_recv_tcpL(suite) -> + []; +traffic_ping_pong_small_send_and_recv_tcpL(doc) -> + []; +traffic_ping_pong_small_send_and_recv_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(15)), + Msg = l2b(?TPP_SMALL), + Num = ?TPP_SMALL_NUM, + tc_try(traffic_ping_pong_small_send_and_recv_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + InitState = #{domain => local, + proto => default, msg => Msg, num => Num}, ok = traffic_ping_pong_send_and_recv_tcp(InitState) @@ -9828,6 +14977,7 @@ traffic_ping_pong_medium_send_and_recv_tcp4(_Config) when is_list(_Config) -> fun() -> ?TT(?SECS(30)), InitState = #{domain => inet, + proto => tcp, msg => Msg, num => Num}, ok = traffic_ping_pong_send_and_recv_tcp(InitState) @@ -9855,6 +15005,36 @@ traffic_ping_pong_medium_send_and_recv_tcp6(_Config) when is_list(_Config) -> fun() -> ?TT(?SECS(30)), InitState = #{domain => inet6, + proto => tcp, + msg => Msg, + num => Num}, + ok = traffic_ping_pong_send_and_recv_tcp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test that the send and recv functions +%% by repeatedly sending a meassage between two entities. +%% The same basic test case is used for three different message sizes; +%% small (8 bytes), medium (8K) and large (8M). +%% The message is sent from A to B and then back again. This is +%% repeated a set number of times (more times the small the message). +%% This is the 'medium' message test case, for Unix Domain (stream) socket. + +traffic_ping_pong_medium_send_and_recv_tcpL(suite) -> + []; +traffic_ping_pong_medium_send_and_recv_tcpL(doc) -> + []; +traffic_ping_pong_medium_send_and_recv_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(30)), + Msg = l2b(?TPP_MEDIUM), + Num = ?TPP_MEDIUM_NUM, + tc_try(traffic_ping_pong_medium_send_and_recv_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + InitState = #{domain => local, + proto => default, msg => Msg, num => Num}, ok = traffic_ping_pong_send_and_recv_tcp(InitState) @@ -9876,12 +15056,13 @@ traffic_ping_pong_large_send_and_recv_tcp4(suite) -> traffic_ping_pong_large_send_and_recv_tcp4(doc) -> []; traffic_ping_pong_large_send_and_recv_tcp4(_Config) when is_list(_Config) -> + ?TT(?SECS(45)), Msg = l2b(?TPP_LARGE), Num = ?TPP_LARGE_NUM, tc_try(traffic_ping_pong_large_send_and_recv_tcp4, fun() -> - ?TT(?SECS(45)), InitState = #{domain => inet, + proto => tcp, msg => Msg, num => Num}, ok = traffic_ping_pong_send_and_recv_tcp(InitState) @@ -9909,11 +15090,53 @@ traffic_ping_pong_large_send_and_recv_tcp6(_Config) when is_list(_Config) -> fun() -> ?TT(?SECS(45)), InitState = #{domain => inet6, + proto => tcp, + msg => Msg, + num => Num}, + ok = traffic_ping_pong_send_and_recv_tcp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test that the send and recv functions +%% by repeatedly sending a meassage between two entities. +%% The same basic test case is used for three different message sizes; +%% small (8 bytes), medium (8K) and large (8M). +%% The message is sent from A to B and then back again. This is +%% repeated a set number of times (more times the small the message). +%% This is the 'large' message test case, for UNix Domain (stream) socket. + +traffic_ping_pong_large_send_and_recv_tcpL(suite) -> + []; +traffic_ping_pong_large_send_and_recv_tcpL(doc) -> + []; +traffic_ping_pong_large_send_and_recv_tcpL(_Config) when is_list(_Config) -> + ?TT(?SECS(45)), + Msg = l2b(?TPP_LARGE), + Num = ?TPP_LARGE_NUM, + tc_try(traffic_ping_pong_large_send_and_recv_tcpL, + fun() -> + has_support_unix_domain_socket(), + traffic_ping_pong_large_host_cond() + end, + fun() -> + InitState = #{domain => local, + proto => default, msg => Msg, num => Num}, ok = traffic_ping_pong_send_and_recv_tcp(InitState) end). +%% This test case is a bit extreme and fails on some hosts +%% (e.g. OpenIndiana Hipster), so exclude them. +traffic_ping_pong_large_host_cond() -> + traffic_ping_pong_large_host_cond(os:type(), os:version()). + +traffic_ping_pong_large_host_cond({unix, sunos}, _) -> + skip("TC does not work on platform"); +traffic_ping_pong_large_host_cond(_, _) -> + ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -9936,6 +15159,7 @@ traffic_ping_pong_small_sendto_and_recvfrom_udp4(_Config) when is_list(_Config) fun() -> ?TT(?SECS(45)), InitState = #{domain => inet, + proto => udp, msg => Msg, num => Num}, ok = traffic_ping_pong_sendto_and_recvfrom_udp(InitState) @@ -9956,12 +15180,43 @@ traffic_ping_pong_small_sendto_and_recvfrom_udp6(suite) -> traffic_ping_pong_small_sendto_and_recvfrom_udp6(doc) -> []; traffic_ping_pong_small_sendto_and_recvfrom_udp6(_Config) when is_list(_Config) -> + ?TT(?SECS(45)), Msg = l2b(?TPP_SMALL), Num = ?TPP_SMALL_NUM, tc_try(traffic_ping_pong_small_sendto_and_recvfrom_udp6, + fun() -> has_support_ipv6() end, fun() -> - ?TT(?SECS(45)), - InitState = #{domain => inet, + InitState = #{domain => inet6, + proto => udp, + msg => Msg, + num => Num}, + ok = traffic_ping_pong_sendto_and_recvfrom_udp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test that the sendto and recvfrom +%% functions by repeatedly sending a meassage between two entities. +%% The same basic test case is used for two different message sizes; +%% small (8 bytes) and medium (8K). +%% The message is sent from A to B and then back again. This is +%% repeated a set number of times (more times the small the message). +%% This is the 'small' message test case, for Unix Domain (dgram) socket. + +traffic_ping_pong_small_sendto_and_recvfrom_udpL(suite) -> + []; +traffic_ping_pong_small_sendto_and_recvfrom_udpL(doc) -> + []; +traffic_ping_pong_small_sendto_and_recvfrom_udpL(_Config) when is_list(_Config) -> + ?TT(?SECS(45)), + Msg = l2b(?TPP_SMALL), + Num = ?TPP_SMALL_NUM, + tc_try(traffic_ping_pong_small_sendto_and_recvfrom_udpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + InitState = #{domain => local, + proto => default, msg => Msg, num => Num}, ok = traffic_ping_pong_sendto_and_recvfrom_udp(InitState) @@ -9989,6 +15244,7 @@ traffic_ping_pong_medium_sendto_and_recvfrom_udp4(_Config) when is_list(_Config) fun() -> ?TT(?SECS(45)), InitState = #{domain => inet, + proto => udp, msg => Msg, num => Num}, ok = traffic_ping_pong_sendto_and_recvfrom_udp(InitState) @@ -10016,6 +15272,36 @@ traffic_ping_pong_medium_sendto_and_recvfrom_udp6(_Config) when is_list(_Config) fun() -> ?TT(?SECS(45)), InitState = #{domain => inet6, + proto => udp, + msg => Msg, + num => Num}, + ok = traffic_ping_pong_sendto_and_recvfrom_udp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test that the sendto and recvfrom +%% functions by repeatedly sending a meassage between two entities. +%% The same basic test case is used for two different message sizes; +%% small (8 bytes) and medium (8K). +%% The message is sent from A to B and then back again. This is +%% repeated a set number of times (more times the small the message). +%% This is the 'medium' message test case, for Unix Domain (dgram) socket. + +traffic_ping_pong_medium_sendto_and_recvfrom_udpL(suite) -> + []; +traffic_ping_pong_medium_sendto_and_recvfrom_udpL(doc) -> + []; +traffic_ping_pong_medium_sendto_and_recvfrom_udpL(_Config) when is_list(_Config) -> + Msg = l2b(?TPP_MEDIUM), + Num = ?TPP_MEDIUM_NUM, + tc_try(traffic_ping_pong_medium_sendto_and_recvfrom_udpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + ?TT(?SECS(45)), + InitState = #{domain => local, + proto => default, msg => Msg, num => Num}, ok = traffic_ping_pong_sendto_and_recvfrom_udp(InitState) @@ -10043,6 +15329,7 @@ traffic_ping_pong_small_sendmsg_and_recvmsg_tcp4(_Config) when is_list(_Config) fun() -> ?TT(?SECS(20)), InitState = #{domain => inet, + proto => tcp, msg => Msg, num => Num}, ok = traffic_ping_pong_sendmsg_and_recvmsg_tcp(InitState) @@ -10070,6 +15357,35 @@ traffic_ping_pong_small_sendmsg_and_recvmsg_tcp6(_Config) when is_list(_Config) fun() -> ?TT(?SECS(20)), InitState = #{domain => inet6, + proto => tcp, + msg => Msg, + num => Num}, + ok = traffic_ping_pong_sendmsg_and_recvmsg_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test that the sendmsg and recvmsg functions +%% by repeatedly sending a meassage between two entities. +%% The same basic test case is used for three different message sizes; +%% small (8 bytes), medium (8K) and large (8M). +%% The message is sent from A to B and then back again. This is +%% repeated a set number of times (more times the small the message). +%% This is the 'small' message test case, for Unix Domain (stream) socket. + +traffic_ping_pong_small_sendmsg_and_recvmsg_tcpL(suite) -> + []; +traffic_ping_pong_small_sendmsg_and_recvmsg_tcpL(doc) -> + []; +traffic_ping_pong_small_sendmsg_and_recvmsg_tcpL(_Config) when is_list(_Config) -> + Msg = l2b(?TPP_SMALL), + Num = ?TPP_SMALL_NUM, + tc_try(traffic_ping_pong_small_sendmsg_and_recvmsg_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + ?TT(?SECS(20)), + InitState = #{domain => local, + proto => default, msg => Msg, num => Num}, ok = traffic_ping_pong_sendmsg_and_recvmsg_tcp(InitState) @@ -10096,6 +15412,7 @@ traffic_ping_pong_medium_sendmsg_and_recvmsg_tcp4(_Config) when is_list(_Config) fun() -> ?TT(?SECS(30)), InitState = #{domain => inet, + proto => tcp, msg => Msg, num => Num}, ok = traffic_ping_pong_sendmsg_and_recvmsg_tcp(InitState) @@ -10122,7 +15439,36 @@ traffic_ping_pong_medium_sendmsg_and_recvmsg_tcp6(_Config) when is_list(_Config) fun() -> has_support_ipv6() end, fun() -> ?TT(?SECS(20)), - InitState = #{domain => ine6, + InitState = #{domain => inet6, + proto => tcp, + msg => Msg, + num => Num}, + ok = traffic_ping_pong_sendmsg_and_recvmsg_tcp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test that the sendmsg and recvmsg functions +%% by repeatedly sending a meassage between two entities. +%% The same basic test case is used for three different message sizes; +%% small (8 bytes), medium (8K) and large (8M). +%% The message is sent from A to B and then back again. This is +%% repeated a set number of times (more times the small the message). +%% This is the 'medium' message test case, for Unix Domain (stream) socket. + +traffic_ping_pong_medium_sendmsg_and_recvmsg_tcpL(suite) -> + []; +traffic_ping_pong_medium_sendmsg_and_recvmsg_tcpL(doc) -> + []; +traffic_ping_pong_medium_sendmsg_and_recvmsg_tcpL(_Config) when is_list(_Config) -> + Msg = l2b(?TPP_MEDIUM), + Num = ?TPP_MEDIUM_NUM, + tc_try(traffic_ping_pong_medium_sendmsg_and_recvmsg_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + ?TT(?SECS(20)), + InitState = #{domain => local, + proto => default, msg => Msg, num => Num}, ok = traffic_ping_pong_sendmsg_and_recvmsg_tcp(InitState) @@ -10146,15 +15492,27 @@ traffic_ping_pong_large_sendmsg_and_recvmsg_tcp4(_Config) when is_list(_Config) Msg = l2b(?TPP_LARGE), Num = ?TPP_LARGE_NUM, tc_try(traffic_ping_pong_large_sendmsg_and_recvmsg_tcp4, + fun() -> traffic_ping_pong_large_sendmsg_and_recvmsg_cond() end, fun() -> ?TT(?SECS(30)), InitState = #{domain => inet, + proto => tcp, msg => Msg, num => Num}, ok = traffic_ping_pong_sendmsg_and_recvmsg_tcp(InitState) end). +traffic_ping_pong_large_sendmsg_and_recvmsg_cond() -> + traffic_ping_pong_large_sendmsg_and_recvmsg_cond(os:type(), os:version()). + +traffic_ping_pong_large_sendmsg_and_recvmsg_cond({unix, linux}, {M, _, _}) + when (M < 3) -> + skip("TC may not work on this version"); +traffic_ping_pong_large_sendmsg_and_recvmsg_cond(_, _) -> + ok. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case is intended to test that the sendmsg and recvmsg functions %% by repeatedly sending a meassage between two entities. @@ -10172,10 +15530,43 @@ traffic_ping_pong_large_sendmsg_and_recvmsg_tcp6(_Config) when is_list(_Config) Msg = l2b(?TPP_LARGE), Num = ?TPP_LARGE_NUM, tc_try(traffic_ping_pong_large_sendmsg_and_recvmsg_tcp6, - fun() -> has_support_ipv6() end, + fun() -> + has_support_ipv6(), + traffic_ping_pong_large_sendmsg_and_recvmsg_cond() + end, fun() -> ?TT(?SECS(30)), InitState = #{domain => inet6, + proto => tcp, + msg => Msg, + num => Num}, + ok = traffic_ping_pong_sendmsg_and_recvmsg_tcp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test that the sendmsg and recvmsg functions +%% by repeatedly sending a meassage between two entities. +%% The same basic test case is used for three different message sizes; +%% small (8 bytes), medium (8K) and large (8M). +%% The message is sent from A to B and then back again. This is +%% repeated a set number of times (more times the small the message). +%% This is the 'large' message test case, for Unix Domain (stream) socket. + +traffic_ping_pong_large_sendmsg_and_recvmsg_tcpL(suite) -> + []; +traffic_ping_pong_large_sendmsg_and_recvmsg_tcpL(doc) -> + []; +traffic_ping_pong_large_sendmsg_and_recvmsg_tcpL(_Config) when is_list(_Config) -> + Msg = l2b(?TPP_LARGE), + Num = ?TPP_LARGE_NUM, + tc_try(traffic_ping_pong_large_sendmsg_and_recvmsg_tcpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + ?TT(?SECS(30)), + InitState = #{domain => local, + proto => default, msg => Msg, num => Num}, ok = traffic_ping_pong_sendmsg_and_recvmsg_tcp(InitState) @@ -10203,6 +15594,7 @@ traffic_ping_pong_small_sendmsg_and_recvmsg_udp4(_Config) when is_list(_Config) fun() -> ?TT(?SECS(60)), InitState = #{domain => inet, + proto => udp, msg => Msg, num => Num}, ok = traffic_ping_pong_sendmsg_and_recvmsg_udp(InitState) @@ -10229,7 +15621,36 @@ traffic_ping_pong_small_sendmsg_and_recvmsg_udp6(_Config) when is_list(_Config) fun() -> has_support_ipv6() end, fun() -> ?TT(?SECS(30)), - InitState = #{domain => inet, + InitState = #{domain => inet6, + proto => udp, + msg => Msg, + num => Num}, + ok = traffic_ping_pong_sendmsg_and_recvmsg_udp(InitState) + end). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test that the sendmsg and recvmsg functions +%% by repeatedly sending a meassage between two entities. +%% The same basic test case is used for three different message sizes; +%% small (8 bytes) and medium (8K). +%% The message is sent from A to B and then back again. This is +%% repeated a set number of times (more times the small the message). +%% This is the 'small' message test case, for Unix Domain (dgram) socket. + +traffic_ping_pong_small_sendmsg_and_recvmsg_udpL(suite) -> + []; +traffic_ping_pong_small_sendmsg_and_recvmsg_udpL(doc) -> + []; +traffic_ping_pong_small_sendmsg_and_recvmsg_udpL(_Config) when is_list(_Config) -> + Msg = l2b(?TPP_SMALL), + Num = ?TPP_SMALL_NUM, + tc_try(traffic_ping_pong_small_sendmsg_and_recvmsg_udpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + ?TT(?SECS(30)), + InitState = #{domain => local, + proto => default, msg => Msg, num => Num}, ok = traffic_ping_pong_sendmsg_and_recvmsg_udp(InitState) @@ -10256,6 +15677,7 @@ traffic_ping_pong_medium_sendmsg_and_recvmsg_udp4(_Config) when is_list(_Config) fun() -> ?TT(?SECS(30)), InitState = #{domain => inet, + proto => udp, msg => Msg, num => Num}, ok = traffic_ping_pong_sendmsg_and_recvmsg_udp(InitState) @@ -10282,7 +15704,37 @@ traffic_ping_pong_medium_sendmsg_and_recvmsg_udp6(_Config) when is_list(_Config) fun() -> has_support_ipv6() end, fun() -> ?TT(?SECS(20)), - InitState = #{domain => ine6, + InitState = #{domain => inet6, + proto => udp, + msg => Msg, + num => Num}, + ok = traffic_ping_pong_sendmsg_and_recvmsg_udp(InitState) + end). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case is intended to test that the sendmsg and recvmsg +%% functions by repeatedly sending a meassage between two entities. +%% The same basic test case is used for three different message sizes; +%% small (8 bytes) and medium (8K). +%% The message is sent from A to B and then back again. This is +%% repeated a set number of times (more times the small the message). +%% This is the 'medium' message test case, for Unix Domain (dgram) socket. + +traffic_ping_pong_medium_sendmsg_and_recvmsg_udpL(suite) -> + []; +traffic_ping_pong_medium_sendmsg_and_recvmsg_udpL(doc) -> + []; +traffic_ping_pong_medium_sendmsg_and_recvmsg_udpL(_Config) when is_list(_Config) -> + Msg = l2b(?TPP_MEDIUM), + Num = ?TPP_MEDIUM_NUM, + tc_try(traffic_ping_pong_medium_sendmsg_and_recvmsg_udpL, + fun() -> has_support_unix_domain_socket() end, + fun() -> + ?TT(?SECS(20)), + InitState = #{domain => local, + proto => default, msg => Msg, num => Num}, ok = traffic_ping_pong_sendmsg_and_recvmsg_udp(InitState) @@ -10302,14 +15754,26 @@ traffic_ping_pong_send_and_recv_tcp(InitState) -> }, traffic_ping_pong_send_and_receive_tcp(InitState2). -traffic_ping_pong_sendmsg_and_recvmsg_tcp(InitState) -> - Send = fun(Sock, Data) when is_binary(Data) -> - MsgHdr = #{iov => [Data]}, - socket:sendmsg(Sock, MsgHdr); - (Sock, Data) when is_list(Data) -> %% We assume iovec... - MsgHdr = #{iov => Data}, - socket:sendmsg(Sock, MsgHdr) +traffic_ping_pong_sendmsg_and_recvmsg_tcp(#{domain := local} = InitState) -> + Recv = fun(Sock, Sz) -> + case socket:recvmsg(Sock, Sz, 0) of + %% On some platforms, the address + %% is *not* provided (e.g. FreeBSD) + {ok, #{addr := undefined, + iov := [Data]}} -> + {ok, Data}; + %% On some platforms, the address + %% *is* provided (e.g. linux) + {ok, #{addr := #{family := local}, + iov := [Data]}} -> + {ok, Data}; + {error, _} = ERROR -> + ERROR + end end, + InitState2 = InitState#{recv => Recv}, % Receive function + traffic_ping_pong_sendmsg_and_recvmsg_tcp2(InitState2); +traffic_ping_pong_sendmsg_and_recvmsg_tcp(InitState) -> Recv = fun(Sock, Sz) -> case socket:recvmsg(Sock, Sz, 0) of {ok, #{addr := undefined, @@ -10319,9 +15783,18 @@ traffic_ping_pong_sendmsg_and_recvmsg_tcp(InitState) -> ERROR end end, - InitState2 = InitState#{send => Send, % Send function - recv => Recv % Receive function - }, + InitState2 = InitState#{recv => Recv}, % Receive function + traffic_ping_pong_sendmsg_and_recvmsg_tcp2(InitState2). + +traffic_ping_pong_sendmsg_and_recvmsg_tcp2(InitState) -> + Send = fun(Sock, Data) when is_binary(Data) -> + MsgHdr = #{iov => [Data]}, + socket:sendmsg(Sock, MsgHdr); + (Sock, Data) when is_list(Data) -> %% We assume iovec... + MsgHdr = #{iov => Data}, + socket:sendmsg(Sock, MsgHdr) + end, + InitState2 = InitState#{send => Send}, % Send function traffic_ping_pong_send_and_receive_tcp(InitState2). @@ -10381,13 +15854,12 @@ traffic_ping_pong_send_and_receive_tcp2(InitState) -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{local_sa => LSA}} end}, #{desc => "create listen socket", - cmd => fun(#{domain := Domain} = State) -> - case socket:open(Domain, stream, tcp) of + cmd => fun(#{domain := Domain, proto := Proto} = State) -> + case socket:open(Domain, stream, Proto) of {ok, Sock} -> {ok, State#{lsock => Sock}}; {error, _} = ERROR -> @@ -10395,9 +15867,19 @@ traffic_ping_pong_send_and_receive_tcp2(InitState) -> end end}, #{desc => "bind to local address", - cmd => fun(#{lsock := LSock, local_sa := LSA} = State) -> + cmd => fun(#{domain := local, + lsock := LSock, + lsa := LSA} = _State) -> + case socket:bind(LSock, LSA) of + {ok, _Port} -> + ok; % We do not care about the port for local + {error, _} = ERROR -> + ERROR + end; + (#{lsock := LSock, local_sa := LSA} = State) -> case socket:bind(LSock, LSA) of {ok, Port} -> + ?SEV_IPRINT("bound to port: ~w", [Port]), {ok, State#{lport => Port}}; {error, _} = ERROR -> ERROR @@ -10412,7 +15894,11 @@ traffic_ping_pong_send_and_receive_tcp2(InitState) -> socket:listen(LSock) end}, #{desc => "announce ready (init)", - cmd => fun(#{tester := Tester, local_sa := LSA, lport := Port}) -> + cmd => fun(#{domain := local, + tester := Tester, local_sa := LSA}) -> + ?SEV_ANNOUNCE_READY(Tester, init, LSA), + ok; + (#{tester := Tester, local_sa := LSA, lport := Port}) -> ServerSA = LSA#{port => Port}, ?SEV_ANNOUNCE_READY(Tester, init, ServerSA), ok @@ -10523,9 +16009,20 @@ traffic_ping_pong_send_and_receive_tcp2(InitState) -> {ok, State1} end}, #{desc => "close listen socket", - cmd => fun(#{lsock := Sock} = State) -> + cmd => fun(#{domain := local, + lsock := Sock, + local_sa := #{path := Path}} = State) -> (catch socket:close(Sock)), - {ok, maps:remove(lsock, State)} + State1 = + unlink_path(Path, + fun() -> + maps:remove(local_sa, State) + end, + fun() -> State end), + {ok, maps:remove(lsock, State1)}; + (#{lsock := Sock} = State) -> + (catch socket:close(Sock)), + {ok, maps:remove(lsock, State)} end}, %% *** We are done *** @@ -10577,12 +16074,14 @@ traffic_ping_pong_send_and_receive_tcp2(InitState) -> end}, #{desc => "order remote client to start", cmd => fun(#{rclient := RClient, + proto := Proto, server_sa := ServerSA, buf_init := BufInit, send := Send, recv := Recv}) -> ?SEV_ANNOUNCE_START(RClient, - {ServerSA, BufInit, Send, Recv}), + {ServerSA, Proto, BufInit, + Send, Recv}), ok end}, #{desc => "await remote client ready", @@ -10883,6 +16382,7 @@ traffic_ping_pong_send_and_receive_tcp2(InitState) -> i("start server evaluator"), ServerInitState = #{domain => maps:get(domain, InitState), + proto => maps:get(proto, InitState), recv => maps:get(recv, InitState), send => maps:get(send, InitState), buf_init => maps:get(buf_init, InitState)}, @@ -10976,14 +16476,6 @@ tpp_tcp_handler_msg_exchange_loop(Sock, Send, Recv, N, Sent, Received, Start) -> ?SEV_EPRINT("send (~w): ~p", [N, SReason]), exit({send, SReason, N}) end; - %% {error, timeout} -> - %% ?SEV_IPRINT("timeout(~w) - try again", [N]), - %% case Send(Sock, list_to_binary("ping")) of - %% ok -> - %% exit({'ping-send', ok, N}); - %% {error, Reason} -> - %% exit({'ping-send', Reason, N}) - %% end; {error, closed} -> ?SEV_IPRINT("closed - we are done: ~w, ~w, ~w", [N, Sent, Received]), Stop = ?LIB:timestamp(), @@ -11002,10 +16494,10 @@ tpp_tcp_client_create(Node) -> tpp_tcp_client(Parent) -> tpp_tcp_client_init(Parent), - {ServerSA, BufInit, Send, Recv} = tpp_tcp_client_await_start(Parent), + {ServerSA, Proto, BufInit, Send, Recv} = tpp_tcp_client_await_start(Parent), Domain = maps:get(family, ServerSA), - Sock = tpp_tcp_client_sock_open(Domain, BufInit), - tpp_tcp_client_sock_bind(Sock, Domain), + Sock = tpp_tcp_client_sock_open(Domain, Proto, BufInit), + Path = tpp_tcp_client_sock_bind(Sock, Domain), tpp_tcp_client_announce_ready(Parent, init), tpp_tcp_client_await_continue(Parent, connect), tpp_tcp_client_sock_connect(Sock, ServerSA), @@ -11014,7 +16506,7 @@ tpp_tcp_client(Parent) -> Result = tpp_tcp_client_msg_exchange(Sock, Send, Recv, InitMsg, Num), tpp_tcp_client_announce_ready(Parent, send, Result), Reason = tpp_tcp_client_await_terminate(Parent), - tpp_tcp_client_sock_close(Sock), + tpp_tcp_client_sock_close(Sock, Path), ?SEV_IPRINT("terminating"), exit(Reason). @@ -11054,8 +16546,10 @@ tpp_tcp_client_await_terminate(Parent) -> ?SEV_IPRINT("await terminate"), case ?SEV_AWAIT_TERMINATE(Parent, parent) of ok -> - ok; + ?SEV_IPRINT("termination received: normal"), + normal; {error, Reason} -> + ?SEV_IPRINT("termination received: ~w", [Reason]), Reason end. @@ -11099,8 +16593,8 @@ tpp_tcp_client_msg_exchange_loop(Sock, Send, Recv, Data, exit({send, SReason, N}) end. -tpp_tcp_client_sock_open(Domain, BufInit) -> - case socket:open(Domain, stream, tcp) of +tpp_tcp_client_sock_open(Domain, Proto, BufInit) -> + case socket:open(Domain, stream, Proto) of {ok, Sock} -> ok = BufInit(Sock), Sock; @@ -11109,14 +16603,19 @@ tpp_tcp_client_sock_open(Domain, BufInit) -> end. tpp_tcp_client_sock_bind(Sock, Domain) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, - addr => LAddr}, + LSA = which_local_socket_addr(Domain), case socket:bind(Sock, LSA) of {ok, _} -> - ok; - {error, Reason} -> - exit({bind, Reason}) + case socket:sockname(Sock) of + {ok, #{family := local, path := Path}} -> + Path; + {ok, _} -> + undefined; + {error, Reason1} -> + exit({sockname, Reason1}) + end; + {error, Reason2} -> + exit({bind, Reason2}) end. tpp_tcp_client_sock_connect(Sock, ServerSA) -> @@ -11127,14 +16626,20 @@ tpp_tcp_client_sock_connect(Sock, ServerSA) -> exit({connect, Reason}) end. -tpp_tcp_client_sock_close(Sock) -> +tpp_tcp_client_sock_close(Sock, Path) -> case socket:close(Sock) of ok -> + unlink_path(Path), ok; {error, Reason} -> - exit({close, Reason}) + ?SEV_EPRINT("failed closing: " + "~n Reason: ~p", [Reason]), + unlink_path(Path), + {error, {close, Reason}} end. + + -define(TPP_REQUEST, 1). -define(TPP_REPLY, 2). @@ -11300,13 +16805,12 @@ traffic_ping_pong_send_and_receive_udp2(InitState) -> %% *** Init part *** #{desc => "which local address", cmd => fun(#{domain := Domain} = State) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, addr => LAddr}, + LSA = which_local_socket_addr(Domain), {ok, State#{local_sa => LSA}} end}, - #{desc => "create listen socket", - cmd => fun(#{domain := Domain} = State) -> - case socket:open(Domain, dgram, udp) of + #{desc => "create socket", + cmd => fun(#{domain := Domain, proto := Proto} = State) -> + case socket:open(Domain, dgram, Proto) of {ok, Sock} -> {ok, State#{sock => Sock}}; {error, _} = ERROR -> @@ -11314,7 +16818,15 @@ traffic_ping_pong_send_and_receive_udp2(InitState) -> end end}, #{desc => "bind to local address", - cmd => fun(#{sock := Sock, local_sa := LSA} = State) -> + cmd => fun(#{domain := local, + sock := Sock, local_sa := LSA} = _State) -> + case socket:bind(Sock, LSA) of + {ok, _} -> + ok; + {error, _} = ERROR -> + ERROR + end; + (#{sock := Sock, local_sa := LSA} = State) -> case socket:bind(Sock, LSA) of {ok, Port} -> {ok, State#{port => Port}}; @@ -11357,7 +16869,11 @@ traffic_ping_pong_send_and_receive_udp2(InitState) -> end end}, #{desc => "announce ready (init)", - cmd => fun(#{tester := Tester, local_sa := LSA, port := Port}) -> + cmd => fun(#{domain := local, + tester := Tester, local_sa := LSA}) -> + ?SEV_ANNOUNCE_READY(Tester, init, LSA), + ok; + (#{tester := Tester, local_sa := LSA, port := Port}) -> ServerSA = LSA#{port => Port}, ?SEV_ANNOUNCE_READY(Tester, init, ServerSA), ok @@ -11396,6 +16912,19 @@ traffic_ping_pong_send_and_receive_udp2(InitState) -> ERROR end end}, + #{desc => "(maybe) unlink socket", + cmd => fun(#{domain := local, + local_sa := #{path := Path}} = State) -> + unlink_path(Path, + fun() -> + {ok, maps:remove(local_sa, State)} + end, + fun() -> + ok + end); + (_) -> + ok + end}, #{desc => "announce ready (close)", cmd => fun(#{tester := Tester} = _State) -> ?SEV_ANNOUNCE_READY(Tester, close), @@ -11492,11 +17021,13 @@ traffic_ping_pong_send_and_receive_udp2(InitState) -> #{desc => "order remote handler to start", cmd => fun(#{handler := Handler, server_sa := ServerSA, + proto := Proto, buf_init := BufInit, send := Send, recv := Recv}) -> ?SEV_ANNOUNCE_START(Handler, - {ServerSA, BufInit, Send, Recv}), + {ServerSA, Proto, BufInit, + Send, Recv}), ok end}, #{desc => "await (remote) handler ready", @@ -11741,6 +17272,7 @@ traffic_ping_pong_send_and_receive_udp2(InitState) -> i("start server evaluator"), ServerInitState = #{domain => maps:get(domain, InitState), + proto => maps:get(proto, InitState), recv => maps:get(recv, InitState), send => maps:get(send, InitState), buf_init => maps:get(buf_init, InitState)}, @@ -11841,12 +17373,12 @@ tpp_udp_client_handler_create(Node) -> tpp_udp_client_handler(Parent) -> tpp_udp_client_handler_init(Parent), ?SEV_IPRINT("await start command"), - {ServerSA, BufInit, Send, Recv} = tpp_udp_handler_await_start(Parent), + {ServerSA, Proto, BufInit, Send, Recv} = tpp_udp_handler_await_start(Parent), ?SEV_IPRINT("start command with" "~n ServerSA: ~p", [ServerSA]), Domain = maps:get(family, ServerSA), - Sock = tpp_udp_sock_open(Domain, BufInit), - tpp_udp_sock_bind(Sock, Domain), + Sock = tpp_udp_sock_open(Domain, Proto, BufInit), + Path = tpp_udp_sock_bind(Sock, Domain), ?SEV_IPRINT("announce ready", []), tpp_udp_handler_announce_ready(Parent, init), {InitMsg, Num} = tpp_udp_handler_await_continue(Parent, send), @@ -11859,7 +17391,7 @@ tpp_udp_client_handler(Parent) -> ?SEV_IPRINT("await terminate"), Reason = tpp_udp_handler_await_terminate(Parent), ?SEV_IPRINT("terminate with ~p", [Reason]), - tpp_udp_sock_close(Sock), + tpp_udp_sock_close(Sock, Path), ?SEV_IPRINT("terminating"), exit(Reason). @@ -11997,8 +17529,8 @@ tpp_udp_handler_await_terminate(Parent) -> end. -tpp_udp_sock_open(Domain, BufInit) -> - case socket:open(Domain, dgram, udp) of +tpp_udp_sock_open(Domain, Proto, BufInit) -> + case socket:open(Domain, dgram, Proto) of {ok, Sock} -> ok = BufInit(Sock), Sock; @@ -12007,9 +17539,7 @@ tpp_udp_sock_open(Domain, BufInit) -> end. tpp_udp_sock_bind(Sock, Domain) -> - LAddr = which_local_addr(Domain), - LSA = #{family => Domain, - addr => LAddr}, + LSA = which_local_socket_addr(Domain), case socket:bind(Sock, LSA) of {ok, _} -> ok; @@ -12017,12 +17547,16 @@ tpp_udp_sock_bind(Sock, Domain) -> exit({bind, Reason}) end. -tpp_udp_sock_close(Sock) -> +tpp_udp_sock_close(Sock, Path) -> case socket:close(Sock) of ok -> + unlink_path(Path), ok; {error, Reason} -> - exit({close, Reason}) + ?SEV_EPRINT("Failed closing socket: " + "~n ~p", [Reason]), + unlink_path(Path), + {error, {close, Reason}} end. @@ -15104,6 +20638,30 @@ ttest_ssockf_csockf_small_tcp6(Config) when is_list(Config) -> %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = false %% Client: Transport = socket(tcp), Active = false +%% Message Size: small (=1) +%% Domain: local +%% + +ttest_ssockf_csockf_small_tcpL(suite) -> + []; +ttest_ssockf_csockf_small_tcpL(doc) -> + []; +ttest_ssockf_csockf_small_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_csockf_small_tcpL, + Runtime, + local, + sock, false, + sock, false, + 1, 200). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = false +%% Client: Transport = socket(tcp), Active = false %% Message Size: medium (=2) %% Domain: inet %% @@ -15152,6 +20710,30 @@ ttest_ssockf_csockf_medium_tcp6(Config) when is_list(Config) -> %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = false %% Client: Transport = socket(tcp), Active = false +%% Message Size: medium (=2) +%% Domain: local +%% + +ttest_ssockf_csockf_medium_tcpL(suite) -> + []; +ttest_ssockf_csockf_medium_tcpL(doc) -> + []; +ttest_ssockf_csockf_medium_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_csockf_medium_tcpL, + Runtime, + local, + sock, false, + sock, false, + 2, 20). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = false +%% Client: Transport = socket(tcp), Active = false %% Message Size: large (=3) %% Domain: inet %% @@ -15199,6 +20781,30 @@ ttest_ssockf_csockf_large_tcp6(Config) when is_list(Config) -> %% This test case uses the time test (ttest) utility to implement a %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = false +%% Client: Transport = socket(tcp), Active = false +%% Message Size: large (=3) +%% Domain: local +%% + +ttest_ssockf_csockf_large_tcpL(suite) -> + []; +ttest_ssockf_csockf_large_tcpL(doc) -> + []; +ttest_ssockf_csockf_large_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_csockf_large_tcpL, + Runtime, + local, + sock, false, + sock, false, + 3, 2). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = false %% Client: Transport = socket(tcp), Active = once %% Message Size: small (=1) %% Domain: inet @@ -15248,6 +20854,30 @@ ttest_ssockf_csocko_small_tcp6(Config) when is_list(Config) -> %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = false %% Client: Transport = socket(tcp), Active = once +%% Message Size: small (=1) +%% Domain: local +%% + +ttest_ssockf_csocko_small_tcpL(suite) -> + []; +ttest_ssockf_csocko_small_tcpL(doc) -> + []; +ttest_ssockf_csocko_small_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_csocko_small_tcpL, + Runtime, + local, + sock, false, + sock, once, + 1, 200). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = false +%% Client: Transport = socket(tcp), Active = once %% Message Size: medium (=2) %% Domain: inet %% @@ -15296,6 +20926,30 @@ ttest_ssockf_csocko_medium_tcp6(Config) when is_list(Config) -> %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = false %% Client: Transport = socket(tcp), Active = once +%% Message Size: medium (=2) +%% Domain: local +%% + +ttest_ssockf_csocko_medium_tcpL(suite) -> + []; +ttest_ssockf_csocko_medium_tcpL(doc) -> + []; +ttest_ssockf_csocko_medium_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_csocko_medium_tcpL, + Runtime, + local, + sock, false, + sock, once, + 2, 20). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = false +%% Client: Transport = socket(tcp), Active = once %% Message Size: large (=3) %% Domain: inet %% @@ -15343,6 +20997,30 @@ ttest_ssockf_csocko_large_tcp6(Config) when is_list(Config) -> %% This test case uses the time test (ttest) utility to implement a %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = false +%% Client: Transport = socket(tcp), Active = once +%% Message Size: large (=3) +%% Domain: local +%% + +ttest_ssockf_csocko_large_tcpL(suite) -> + []; +ttest_ssockf_csocko_large_tcpL(doc) -> + []; +ttest_ssockf_csocko_large_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_csocko_large_tcpL, + Runtime, + local, + sock, false, + sock, once, + 3, 2). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = false %% Client: Transport = socket(tcp), Active = true %% Message Size: small (=1) %% Domain: inet @@ -15390,6 +21068,30 @@ ttest_ssockf_csockt_small_tcp6(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case uses the time test (ttest) utility to implement a %% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = false +%% Client: Transport = socket(tcp), Active = true +%% Message Size: small (=1) +%% Domain: local +%% + +ttest_ssockf_csockt_small_tcpL(suite) -> + []; +ttest_ssockf_csockt_small_tcpL(doc) -> + []; +ttest_ssockf_csockt_small_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_csocko_small_tcpL, + Runtime, + local, + sock, false, + sock, true, + 1, 200). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. %% Server: Transport = socket(tcp), Active = false %% Client: Transport = socket(tcp), Active = true %% Message Size: medium (=2) @@ -15440,6 +21142,30 @@ ttest_ssockf_csockt_medium_tcp6(Config) when is_list(Config) -> %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = false %% Client: Transport = socket(tcp), Active = true +%% Message Size: medium (=2) +%% Domain: local +%% + +ttest_ssockf_csockt_medium_tcpL(suite) -> + []; +ttest_ssockf_csockt_medium_tcpL(doc) -> + []; +ttest_ssockf_csockt_medium_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_csockt_medium_tcpL, + Runtime, + local, + sock, false, + sock, true, + 2, 20). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = false +%% Client: Transport = socket(tcp), Active = true %% Message Size: large (=3) %% Domain: inet %% @@ -15486,6 +21212,30 @@ ttest_ssockf_csockt_large_tcp6(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case uses the time test (ttest) utility to implement a %% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = false +%% Client: Transport = socket(tcp), Active = true +%% Message Size: large (=3) +%% Domain: local +%% + +ttest_ssockf_csockt_large_tcpL(suite) -> + []; +ttest_ssockf_csockt_large_tcpL(doc) -> + []; +ttest_ssockf_csockt_large_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockf_csockt_large_tcpL, + Runtime, + local, + sock, false, + sock, true, + 3, 2). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. %% Server: Transport = socket(tcp), Active = once %% Client: Transport = gen_tcp, Active = false %% Message Size: small (=1) @@ -15968,6 +21718,30 @@ ttest_ssocko_csockf_small_tcp6(Config) when is_list(Config) -> %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = once %% Client: Transport = socket(tcp), Active = false +%% Message Size: small (=1) +%% Domain: local +%% + +ttest_ssocko_csockf_small_tcpL(suite) -> + []; +ttest_ssocko_csockf_small_tcpL(doc) -> + []; +ttest_ssocko_csockf_small_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_csockf_small_tcpL, + Runtime, + local, + sock, once, + sock, false, + 1, 200). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = once +%% Client: Transport = socket(tcp), Active = false %% Message Size: medium (=2) %% Domain: inet %% @@ -16016,6 +21790,30 @@ ttest_ssocko_csockf_medium_tcp6(Config) when is_list(Config) -> %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = once %% Client: Transport = socket(tcp), Active = false +%% Message Size: medium (=2) +%% Domain: local +%% + +ttest_ssocko_csockf_medium_tcpL(suite) -> + []; +ttest_ssocko_csockf_medium_tcpL(doc) -> + []; +ttest_ssocko_csockf_medium_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_csockf_medium_tcpL, + Runtime, + local, + sock, once, + sock, false, + 2, 20). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = once +%% Client: Transport = socket(tcp), Active = false %% Message Size: large (=3) %% Domain: inet %% @@ -16063,6 +21861,30 @@ ttest_ssocko_csockf_large_tcp6(Config) when is_list(Config) -> %% This test case uses the time test (ttest) utility to implement a %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = once +%% Client: Transport = socket(tcp), Active = false +%% Message Size: large (=3) +%% Domain: local +%% + +ttest_ssocko_csockf_large_tcpL(suite) -> + []; +ttest_ssocko_csockf_large_tcpL(doc) -> + []; +ttest_ssocko_csockf_large_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_csockf_large_tcpL, + Runtime, + local, + sock, once, + sock, false, + 3, 2). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = once %% Client: Transport = socket(tcp), Active = once %% Message Size: small (=1) %% Domain: inet @@ -16112,6 +21934,30 @@ ttest_ssocko_csocko_small_tcp6(Config) when is_list(Config) -> %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = once %% Client: Transport = socket(tcp), Active = once +%% Message Size: small (=1) +%% Domain: local +%% + +ttest_ssocko_csocko_small_tcpL(suite) -> + []; +ttest_ssocko_csocko_small_tcpL(doc) -> + []; +ttest_ssocko_csocko_small_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_csocko_small_tcpL, + Runtime, + local, + sock, once, + sock, once, + 1, 200). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = once +%% Client: Transport = socket(tcp), Active = once %% Message Size: medium (=2) %% Domain: inet %% @@ -16160,6 +22006,30 @@ ttest_ssocko_csocko_medium_tcp6(Config) when is_list(Config) -> %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = once %% Client: Transport = socket(tcp), Active = once +%% Message Size: medium (=2) +%% Domain: local +%% + +ttest_ssocko_csocko_medium_tcpL(suite) -> + []; +ttest_ssocko_csocko_medium_tcpL(doc) -> + []; +ttest_ssocko_csocko_medium_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_csocko_medium_tcpL, + Runtime, + local, + sock, once, + sock, once, + 2, 20). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = once +%% Client: Transport = socket(tcp), Active = once %% Message Size: large (=3) %% Domain: inet %% @@ -16207,6 +22077,30 @@ ttest_ssocko_csocko_large_tcp6(Config) when is_list(Config) -> %% This test case uses the time test (ttest) utility to implement a %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = once +%% Client: Transport = socket(tcp), Active = once +%% Message Size: large (=3) +%% Domain: local +%% + +ttest_ssocko_csocko_large_tcpL(suite) -> + []; +ttest_ssocko_csocko_large_tcpL(doc) -> + []; +ttest_ssocko_csocko_large_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_csocko_large_tcpL, + Runtime, + local, + sock, once, + sock, once, + 3, 2). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = once %% Client: Transport = socket(tcp), Active = true %% Message Size: small (=1) %% Domain: inet @@ -16254,6 +22148,30 @@ ttest_ssocko_csockt_small_tcp6(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case uses the time test (ttest) utility to implement a %% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = once +%% Client: Transport = socket(tcp), Active = true +%% Message Size: small (=1) +%% Domain: local +%% + +ttest_ssocko_csockt_small_tcpL(suite) -> + []; +ttest_ssocko_csockt_small_tcpL(doc) -> + []; +ttest_ssocko_csockt_small_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_csocko_small_tcpL, + Runtime, + local, + sock, once, + sock, true, + 1, 200). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. %% Server: Transport = socket(tcp), Active = once %% Client: Transport = socket(tcp), Active = true %% Message Size: medium (=2) @@ -16304,6 +22222,30 @@ ttest_ssocko_csockt_medium_tcp6(Config) when is_list(Config) -> %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = once %% Client: Transport = socket(tcp), Active = true +%% Message Size: medium (=2) +%% Domain: local +%% + +ttest_ssocko_csockt_medium_tcpL(suite) -> + []; +ttest_ssocko_csockt_medium_tcpL(doc) -> + []; +ttest_ssocko_csockt_medium_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_csockt_medium_tcpL, + Runtime, + local, + sock, once, + sock, true, + 2, 20). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = once +%% Client: Transport = socket(tcp), Active = true %% Message Size: large (=3) %% Domain: inet %% @@ -16350,6 +22292,30 @@ ttest_ssocko_csockt_large_tcp6(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case uses the time test (ttest) utility to implement a %% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = once +%% Client: Transport = socket(tcp), Active = true +%% Message Size: large (=3) +%% Domain: local +%% + +ttest_ssocko_csockt_large_tcpL(suite) -> + []; +ttest_ssocko_csockt_large_tcpL(doc) -> + []; +ttest_ssocko_csockt_large_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssocko_csockt_large_tcpL, + Runtime, + local, + sock, once, + sock, true, + 3, 2). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. %% Server: Transport = socket(tcp), Active = true %% Client: Transport = gen_tcp, Active = false %% Message Size: small (=1) @@ -16832,6 +22798,30 @@ ttest_ssockt_csockf_small_tcp6(Config) when is_list(Config) -> %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = true %% Client: Transport = socket(tcp), Active = false +%% Message Size: small (=1) +%% Domain: local +%% + +ttest_ssockt_csockf_small_tcpL(suite) -> + []; +ttest_ssockt_csockf_small_tcpL(doc) -> + []; +ttest_ssockt_csockf_small_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_csockf_small_tcpL, + Runtime, + local, + sock, true, + sock, false, + 1, 200). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = true +%% Client: Transport = socket(tcp), Active = false %% Message Size: medium (=2) %% Domain: inet %% @@ -16880,6 +22870,30 @@ ttest_ssockt_csockf_medium_tcp6(Config) when is_list(Config) -> %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = true %% Client: Transport = socket(tcp), Active = false +%% Message Size: medium (=2) +%% Domain: local +%% + +ttest_ssockt_csockf_medium_tcpL(suite) -> + []; +ttest_ssockt_csockf_medium_tcpL(doc) -> + []; +ttest_ssockt_csockf_medium_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_csockf_medium_tcpL, + Runtime, + local, + sock, true, + sock, false, + 2, 20). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = true +%% Client: Transport = socket(tcp), Active = false %% Message Size: large (=3) %% Domain: inet %% @@ -16927,6 +22941,30 @@ ttest_ssockt_csockf_large_tcp6(Config) when is_list(Config) -> %% This test case uses the time test (ttest) utility to implement a %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = true +%% Client: Transport = socket(tcp), Active = false +%% Message Size: large (=3) +%% Domain: local +%% + +ttest_ssockt_csockf_large_tcpL(suite) -> + []; +ttest_ssockt_csockf_large_tcpL(doc) -> + []; +ttest_ssockt_csockf_large_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_csockf_large_tcpL, + Runtime, + local, + sock, true, + sock, false, + 3, 2). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = true %% Client: Transport = socket(tcp), Active = once %% Message Size: small (=1) %% Domain: inet @@ -16976,6 +23014,30 @@ ttest_ssockt_csocko_small_tcp6(Config) when is_list(Config) -> %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = true %% Client: Transport = socket(tcp), Active = once +%% Message Size: small (=1) +%% Domain: local +%% + +ttest_ssockt_csocko_small_tcpL(suite) -> + []; +ttest_ssockt_csocko_small_tcpL(doc) -> + []; +ttest_ssockt_csocko_small_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_csocko_small_tcpL, + Runtime, + local, + sock, true, + sock, once, + 1, 200). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = true +%% Client: Transport = socket(tcp), Active = once %% Message Size: medium (=2) %% Domain: inet %% @@ -17024,6 +23086,30 @@ ttest_ssockt_csocko_medium_tcp6(Config) when is_list(Config) -> %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = true %% Client: Transport = socket(tcp), Active = once +%% Message Size: medium (=2) +%% Domain: local +%% + +ttest_ssockt_csocko_medium_tcpL(suite) -> + []; +ttest_ssockt_csocko_medium_tcpL(doc) -> + []; +ttest_ssockt_csocko_medium_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_csocko_medium_tcpL, + Runtime, + local, + sock, true, + sock, once, + 2, 20). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = true +%% Client: Transport = socket(tcp), Active = once %% Message Size: large (=3) %% Domain: inet %% @@ -17071,6 +23157,30 @@ ttest_ssockt_csocko_large_tcp6(Config) when is_list(Config) -> %% This test case uses the time test (ttest) utility to implement a %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = true +%% Client: Transport = socket(tcp), Active = once +%% Message Size: large (=3) +%% Domain: local +%% + +ttest_ssockt_csocko_large_tcpL(suite) -> + []; +ttest_ssockt_csocko_large_tcpL(doc) -> + []; +ttest_ssockt_csocko_large_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_csocko_large_tcpL, + Runtime, + local, + sock, true, + sock, once, + 3, 2). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = true %% Client: Transport = socket(tcp), Active = true %% Message Size: small (=1) %% Domain: inet @@ -17118,6 +23228,30 @@ ttest_ssockt_csockt_small_tcp6(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% This test case uses the time test (ttest) utility to implement a %% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = true +%% Client: Transport = socket(tcp), Active = true +%% Message Size: small (=1) +%% Domain: local +%% + +ttest_ssockt_csockt_small_tcpL(suite) -> + []; +ttest_ssockt_csockt_small_tcpL(doc) -> + []; +ttest_ssockt_csockt_small_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_csocko_small_tcpL, + Runtime, + local, + sock, true, + sock, true, + 1, 200). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. %% Server: Transport = socket(tcp), Active = true %% Client: Transport = socket(tcp), Active = true %% Message Size: medium (=2) @@ -17168,6 +23302,30 @@ ttest_ssockt_csockt_medium_tcp6(Config) when is_list(Config) -> %% ping-pong like test case. %% Server: Transport = socket(tcp), Active = true %% Client: Transport = socket(tcp), Active = true +%% Message Size: medium (=2) +%% Domain: local +%% + +ttest_ssockt_csockt_medium_tcpL(suite) -> + []; +ttest_ssockt_csockt_medium_tcpL(doc) -> + []; +ttest_ssockt_csockt_medium_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_csockt_medium_tcpL, + Runtime, + local, + sock, true, + sock, true, + 2, 20). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = true +%% Client: Transport = socket(tcp), Active = true %% Message Size: large (=3) %% Domain: inet %% @@ -17212,6 +23370,30 @@ ttest_ssockt_csockt_large_tcp6(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% This test case uses the time test (ttest) utility to implement a +%% ping-pong like test case. +%% Server: Transport = socket(tcp), Active = true +%% Client: Transport = socket(tcp), Active = true +%% Message Size: large (=3) +%% Domain: local +%% + +ttest_ssockt_csockt_large_tcpL(suite) -> + []; +ttest_ssockt_csockt_large_tcpL(doc) -> + []; +ttest_ssockt_csockt_large_tcpL(Config) when is_list(Config) -> + Runtime = which_ttest_runtime(Config), + ttest_tcp(ttest_ssockt_csockt_large_tcpL, + Runtime, + local, + sock, true, + sock, true, + 3, 2). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% which_ttest_runtime(Config) when is_list(Config) -> case lists:keysearch(esock_test_ttest_runtime, 1, Config) of @@ -17235,7 +23417,7 @@ which_ttest_runtime_env(false) -> %% ms: milliseconds %% s: seconds (default) %% m: minutes -which_ttest_runtime_env2([$m, $s | MS]) when (length(MS) > 0) -> +which_ttest_runtime_env2([$s, $m | MS]) when (length(MS) > 0) -> convert_time(MS, fun(X) -> X end); which_ttest_runtime_env2([$m | M]) when (length(M) > 0) -> convert_time(M, fun(X) -> ?MINS(X) end); @@ -17272,7 +23454,8 @@ ttest_tcp(TC, tc_try(TC, fun() -> if - (Domain =/= inet) -> has_support_ipv6(); + (Domain =:= local) -> has_support_unix_domain_socket(); + (Domain =:= inet6) -> has_support_ipv6(); true -> ok end end, @@ -17326,11 +23509,25 @@ ttest_tcp(InitState) -> ok end}, #{desc => "start ttest (remote) server", - cmd => fun(#{mod := Mod, + cmd => fun(#{domain := local = Domain, + mod := Mod, + active := Active, + node := Node} = State) -> + case ttest_tcp_server_start(Node, + Domain, Mod, Active) of + {ok, {{Pid, _}, Path}} -> + {ok, State#{rserver => Pid, + path => Path}}; + {error, _} = ERROR -> + ERROR + end; + (#{domain := Domain, + mod := Mod, active := Active, node := Node} = State) -> - case ttest_tcp_server_start(Node, Mod, Active) of - {ok, {{Pid, _MRef}, {Addr, Port}}} -> + case ttest_tcp_server_start(Node, + Domain, Mod, Active) of + {ok, {{Pid, _}, {Addr, Port}}} -> {ok, State#{rserver => Pid, addr => Addr, port => Port}}; @@ -17339,7 +23536,12 @@ ttest_tcp(InitState) -> end end}, #{desc => "announce ready (init)", - cmd => fun(#{tester := Tester, + cmd => fun(#{domain := local, + tester := Tester, + path := Path}) -> + ?SEV_ANNOUNCE_READY(Tester, init, Path), + ok; + (#{tester := Tester, addr := Addr, port := Port}) -> ?SEV_ANNOUNCE_READY(Tester, init, {Addr, Port}), @@ -17394,8 +23596,14 @@ ttest_tcp(InitState) -> [ %% *** Wait for start order part *** #{desc => "await start", - cmd => fun(State) -> - {Tester, {ServerAddr, ServerPort}} = ?SEV_AWAIT_START(), + cmd => fun(#{domain := local} = State) -> + {Tester, ServerPath} = + ?SEV_AWAIT_START(), + {ok, State#{tester => Tester, + server_path => ServerPath}}; + (State) -> + {Tester, {ServerAddr, ServerPort}} = + ?SEV_AWAIT_START(), {ok, State#{tester => Tester, server_addr => ServerAddr, server_port => ServerPort}} @@ -17436,7 +23644,32 @@ ttest_tcp(InitState) -> ok end}, #{desc => "start ttest (remote) client", - cmd => fun(#{node := Node, + cmd => fun(#{domain := local = Domain, + node := Node, + mod := Mod, + active := Active, + msg_id := MsgID, + max_outstanding := MaxOutstanding, + runtime := RunTime, + server_path := Path} = State) -> + Self = self(), + Notify = + fun(Result) -> + ?SEV_ANNOUNCE_READY(Self, ttest, Result) + end, + case ttest_tcp_client_start(Node, Notify, + Domain, Mod, + Path, + Active, + MsgID, MaxOutstanding, + RunTime) of + {ok, {Pid, _MRef}} -> + {ok, State#{rclient => Pid}}; + {error, _} = ERROR -> + ERROR + end; + (#{domain := Domain, + node := Node, mod := Mod, active := Active, msg_id := MsgID, @@ -17450,8 +23683,9 @@ ttest_tcp(InitState) -> ?SEV_ANNOUNCE_READY(Self, ttest, Result) end, case ttest_tcp_client_start(Node, Notify, - Mod, Active, - Addr, Port, + Domain, Mod, + {Addr, Port}, + Active, MsgID, MaxOutstanding, RunTime) of {ok, {Pid, _MRef}} -> @@ -17463,8 +23697,6 @@ ttest_tcp(InitState) -> #{desc => "await ttest ready", cmd => fun(#{tester := Tester, rclient := RClient} = State) -> - %% TTestResult = ?SEV_AWAIT_READY(RClient, rclient, ttest, - %% [{tester, Tester}]), case ?SEV_AWAIT_READY(RClient, rclient, ttest, [{tester, Tester}]) of {ok, Result} -> @@ -17535,8 +23767,13 @@ ttest_tcp(InitState) -> ok end}, #{desc => "await server ready (init)", - cmd => fun(#{server := Pid} = State) -> - {ok, {Addr, Port}} = ?SEV_AWAIT_READY(Pid, server, init), + cmd => fun(#{domain := local, + server := Pid} = State) -> + {ok, Path} = ?SEV_AWAIT_READY(Pid, server, init), + {ok, State#{server_path => Path}}; + (#{server := Pid} = State) -> + {ok, {Addr, Port}} = + ?SEV_AWAIT_READY(Pid, server, init), {ok, State#{server_addr => Addr, server_port => Port}} end}, @@ -17544,7 +23781,12 @@ ttest_tcp(InitState) -> %% Start the client #{desc => "order client start", - cmd => fun(#{client := Pid, + cmd => fun(#{domain := local, + client := Pid, + server_path := Path} = _State) -> + ?SEV_ANNOUNCE_START(Pid, Path), + ok; + (#{client := Pid, server_addr := Addr, server_port := Port} = _State) -> ?SEV_ANNOUNCE_START(Pid, {Addr, Port}), @@ -17600,12 +23842,23 @@ ttest_tcp(InitState) -> %% Present the results #{desc => "present the results", - cmd => fun(#{result := Result} = State) -> + cmd => fun(#{result := Result, + domain := Domain, + server_mod := ServerTrans, + server_active := ServerActive, + client_mod := ClientTrans, + client_active := ClientActive, + msg_id := MsgID} = State) -> case Result of #{status := ok, runtime := RunTime, cnt := Cnt, bcnt := BCnt} -> + ttest_report(Domain, + ServerTrans, ServerActive, + ClientTrans, ClientActive, + MsgID, + RunTime, BCnt, Cnt), ?SEV_IPRINT( "TTest results: " "~n Run Time: ~s" @@ -17683,7 +23936,8 @@ ttest_tcp(InitState) -> Client = ?SEV_START("client", ClientSeq, ClientInitState), i("start 'tester' evaluator"), - TesterInitState = #{server => Server#ev.pid, + TesterInitState = #{domain => maps:get(domain, InitState), + server => Server#ev.pid, client => Client#ev.pid}, Tester = ?SEV_START("tester", TesterSeq, TesterInitState), @@ -17692,12 +23946,14 @@ ttest_tcp(InitState) -> -ttest_tcp_server_start(Node, gen, Active) -> +ttest_tcp_server_start(Node, _Domain, gen, Active) -> Transport = socket_test_ttest_tcp_gen, socket_test_ttest_tcp_server:start_monitor(Node, Transport, Active); -ttest_tcp_server_start(Node, sock, Active) -> +ttest_tcp_server_start(Node, Domain, sock, Active) -> TransportMod = socket_test_ttest_tcp_socket, - Transport = {TransportMod, #{method => plain}}, + Transport = {TransportMod, #{domain => Domain, + async => true, + method => plain}}, socket_test_ttest_tcp_server:start_monitor(Node, Transport, Active). ttest_tcp_server_stop(Pid) -> @@ -17705,31 +23961,210 @@ ttest_tcp_server_stop(Pid) -> ttest_tcp_client_start(Node, Notify, - gen, - Active, Addr, Port, MsgID, MaxOutstanding, RunTime) -> + _Domain, gen, + ServerInfo, Active, MsgID, MaxOutstanding, RunTime) -> Transport = socket_test_ttest_tcp_gen, socket_test_ttest_tcp_client:start_monitor(Node, Notify, Transport, + ServerInfo, Active, - Addr, Port, MsgID, MaxOutstanding, RunTime); ttest_tcp_client_start(Node, Notify, - sock, - Active, Addr, Port, MsgID, MaxOutstanding, RunTime) -> + Domain, sock, + ServerInfo, Active, MsgID, MaxOutstanding, RunTime) -> TransportMod = socket_test_ttest_tcp_socket, - Transport = {TransportMod, #{method => plain}}, + Transport = {TransportMod, #{domain => Domain, + async => true, + method => plain}}, socket_test_ttest_tcp_client:start_monitor(Node, Notify, Transport, + ServerInfo, Active, - Addr, Port, MsgID, MaxOutstanding, RunTime). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-define(TTEST_MANAGER, esock_ttest_manager). + +-record(ttest_report_id, + {domain :: socket:domain(), + serv_trans :: gen | sock, + serv_active :: once | boolean(), + client_trans :: gen | sock, + client_active :: once | boolean(), + msg_id :: small | medium | large}). + +-record(ttest_report, {id :: #ttest_report_id{}, + time :: non_neg_integer(), + bytes :: non_neg_integer(), + msgs :: non_neg_integer()}). + +-spec ttest_report(Domain :: socket:domain(), + ServTrans :: gen | sock, ServActive :: once | boolean(), + ClientTrans :: gen | sock, ClientActive :: once | boolean(), + MsgID :: 1 | 2 | 3, + RunTime :: non_neg_integer(), + NumBytes :: non_neg_integer(), + NumMsgs :: non_neg_integer()) -> ok. + +ttest_report(Domain, + ServTrans, ServActive, + ClientTrans, ClientActive, + MsgID, + RunTime, + NumBytes, + NumMsgs) -> + ID = #ttest_report_id{domain = Domain, + serv_trans = ServTrans, + serv_active = ServActive, + client_trans = ClientTrans, + client_active = ClientActive, + msg_id = ttest_msg_id_num_to_name(MsgID)}, + Report = #ttest_report{id = ID, + time = RunTime, + bytes = NumBytes, + msgs = NumMsgs}, + %% If we run just one test case, the group init has never been run + %% and therefor the ttest manager is not running (we also don't actually + %% care about collecting reports in that case). + (catch global:send(?TTEST_MANAGER, Report)), + ok. + +ttest_msg_id_num_to_name(1) -> + small; +ttest_msg_id_num_to_name(2) -> + medium; +ttest_msg_id_num_to_name(3) -> + large. + +ttest_manager_start() -> + Self = self(), + {Pid, MRef} = spawn_monitor(fun() -> ttest_manager_init(Self) end), + receive + {ttest_manager_started, Pid} -> + erlang:demonitor(MRef, [flush]), + ok; + {'DOWN', MRef, process, Pid, Reason} -> + exit({failed_starting, ttest_manager, Reason}) + after 5000 -> + exit(Pid, kill), + exit({failed_starting, ttest_manager, timeout}) + end. + +ttest_manager_stop() -> + case global:whereis_name(?TTEST_MANAGER) of + Pid when is_pid(Pid) -> + erlang:monitor(process, Pid), + global:send(?TTEST_MANAGER, stop), + receive + {'DOWN', _MRef, process, Pid, _} -> + ok + after 10000 -> + exit(Pid, kill), + ok + end; + _ -> + ok + end. + +ttest_manager_init(Parent) -> + yes = global:register_name(?TTEST_MANAGER, self()), + ets:new(?TTEST_MANAGER, + [{keypos, #ttest_report.id}, named_table, protected, ordered_set]), + Parent ! {ttest_manager_started, self()}, + ttest_manager_loop(). + +ttest_manager_loop() -> + receive + stop -> + ?LOGGER:format("manager stopping~n", []), + ttest_manager_done(); + + #ttest_report{id = _ID, + time = _RunTime, + bytes = _NumBytes, + msgs = _NumMsgs} = Report -> + true = ets:insert_new(?TTEST_MANAGER, Report), + ttest_manager_loop() + end. + +%% We are supposed to pretty print the result here... +ttest_manager_done() -> + format_reports(inet), + %% format_reports(inet6), + ets:delete(?TTEST_MANAGER), + exit(normal). + +format_reports(Domain) -> + ?LOGGER:format("Domain ~w reports:~n~n", [Domain]), + format_reports(Domain, small), + format_reports(Domain, medium), + format_reports(Domain, large). + +format_reports(Domain, MsgID) when is_atom(MsgID) -> + case which_ttest_reports(Domain, MsgID) of + [] -> + ?LOGGER:format(" No ~w reports~n~n", [MsgID]); + Reports -> + ?LOGGER:format(" ~w reports: ~n", [MsgID]), + lists:foreach(fun(R) -> format_report(R) end, Reports) + end. + +%% This should really be a table like this: +%% +%% client +%% server gen(false) gen(once) gen(true) sock(false) sock(once) sock(true) +%% gen(false) nnn +%% gen(once) nnn +%% gen(true) nnn +%% sock(false) nnn +%% sock(once) nnn +%% sock(true) nnn +%% +format_report(#ttest_report{id = #ttest_report_id{serv_trans = STrans, + serv_active = SActive, + client_trans = CTrans, + client_active = CActive}, + time = RunTime, + bytes = BCnt, + msgs = MCnt}) -> + ?LOGGER:format(" server ~w[~w] - client ~w[~w] => " + "~n Run Time: ~s" + "~n Bytes: ~s" + "~n Messages: ~s" + "~n", [STrans, SActive, CTrans, CActive, + ?TTEST_LIB:format_time(RunTime), + if ((BCnt =:= 0) orelse (RunTime =:= 0)) -> + ?TTEST_LIB:format("~w, ~w", + [BCnt, RunTime]); + true -> + ?TTEST_LIB:format("~p => ~p byte / ms", + [BCnt, BCnt div RunTime]) + end, + if (RunTime =:= 0) -> + "-"; + true -> + ?TTEST_LIB:format("~p => ~p iterations / ms", + [MCnt, MCnt div RunTime]) + end]), + ok. + + +which_ttest_reports(Domain, all) -> + [R || R = #ttest_report{id = #ttest_report_id{domain = D}} <- + ets:tab2list(?TTEST_MANAGER), Domain =:= D]; +which_ttest_reports(Domain, MsgID) -> + [R || R = #ttest_report{id = #ttest_report_id{domain = D, msg_id = MID}} <- + ets:tab2list(?TTEST_MANAGER), (Domain =:= D) andalso (MsgID =:= MID)]. + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + %% This mechanism has only one purpose: So that we are able to kill %% the node-starter process if it takes to long. The node-starter %% runs on the local node. @@ -17801,19 +24236,6 @@ sock_open(Domain, Type, Proto) -> end. -sock_bind(Sock, SockAddr) -> - try socket:bind(Sock, SockAddr) of - {ok, Port} -> - Port; - {error, Reason} -> - i("sock_bind -> error: ~p", [Reason]), - ?FAIL({bind, Reason}) - catch - C:E:S -> - i("sock_bind -> failed: ~p, ~p, ~p", [C, E, S]), - ?FAIL({bind, C, E, S}) - end. - sock_connect(Sock, SockAddr) -> try socket:connect(Sock, SockAddr) of ok -> @@ -17899,47 +24321,176 @@ local_host() -> end. +%% The point of this is to "ensure" that paths from different test runs +%% don't clash. +mk_unique_path() -> + [NodeName | _] = string:tokens(atom_to_list(node()), [$@]), + Path = ?LIB:f("/tmp/esock_~s_~w", [NodeName, erlang:system_time(nanosecond)]), + ensure_unique_path(Path). + +ensure_unique_path(Path) -> + case file:read_file_info(Path) of + {ok, _} -> % Ouch, append a unique ID and try again + ensure_unique_path(Path, 1); + {error, _} -> + %% We assume this means it does not exist yet... + %% If we have several process in paralell trying to create + %% (unique) path's, then we are in trouble. To *really* be + %% on the safe side we should have a (central) path registry... + Path + end. + +ensure_unique_path(Path, ID) when (ID < 100) -> % If this is not enough... + NewPath = ?LIB:f("~s_~w", [Path, ID]), + case file:read_file_info(NewPath) of + {ok, _} -> % Ouch, this also existed, increment and try again + ensure_unique_path(Path, ID + 1); + {error, _} -> % We assume this means it does not exist yet... + NewPath + end; +ensure_unique_path(_, _) -> + skip("Could not create unique path"). + + +which_local_socket_addr(local = Domain) -> + #{family => Domain, + path => mk_unique_path()}; + %% This gets the local address (not 127.0...) %% We should really implement this using the (new) net module, %% but until that gets the necessary functionality... -which_local_addr(Domain) -> +which_local_socket_addr(Domain) -> + case which_local_host_info(Domain) of + {ok, {_Name, _Flags, Addr}} -> + #{family => Domain, + addr => Addr}; + {error, Reason} -> + ?FAIL(Reason) + end. + + +%% Returns the interface (name), flags and address (not 127...) +%% of the local host. +which_local_host_info(Domain) -> case inet:getifaddrs() of {ok, IFL} -> - which_addr(Domain, IFL); - {error, Reason} -> - ?FAIL({inet, getifaddrs, Reason}) + which_local_host_info(Domain, IFL); + {error, _} = ERROR -> + ERROR end. -which_addr(_Domain, []) -> +which_local_host_info(_Domain, []) -> ?FAIL(no_address); -which_addr(Domain, [{"lo" ++ _, _}|IFL]) -> - which_addr(Domain, IFL); -which_addr(Domain, [{_Name, IFO}|IFL]) -> - case which_addr2(Domain, IFO) of - {ok, Addr} -> - Addr; - {error, no_address} -> - which_addr(Domain, IFL) +which_local_host_info(Domain, [{"lo" ++ _, _}|IFL]) -> + which_local_host_info(Domain, IFL); +which_local_host_info(Domain, [{"docker" ++ _, _}|IFL]) -> + which_local_host_info(Domain, IFL); +which_local_host_info(Domain, [{"br-" ++ _, _}|IFL]) -> + which_local_host_info(Domain, IFL); +which_local_host_info(Domain, [{Name, IFO}|IFL]) -> + case which_local_host_info2(Domain, IFO) of + {ok, {Flags, Addr}} -> + {ok, {Name, Flags, Addr}}; + {error, _} -> + which_local_host_info(Domain, IFL) end; -which_addr(Domain, [_|IFL]) -> - which_addr(Domain, IFL). +which_local_host_info(Domain, [_|IFL]) -> + which_local_host_info(Domain, IFL). + +which_local_host_info2(Domain, IFO) -> + case lists:keysearch(flags, 1, IFO) of + {value, {flags, Flags}} -> + which_local_host_info2(Domain, IFO, Flags); + false -> + {error, no_flags} + end. -which_addr2(_Domain, []) -> +which_local_host_info2(_Domain, [], _Flags) -> {error, no_address}; -which_addr2(inet = _Domain, [{addr, Addr}|_IFO]) when (size(Addr) =:= 4) -> - {ok, Addr}; -which_addr2(inet6 = _Domain, [{addr, Addr}|_IFO]) when (size(Addr) =:= 8) -> - {ok, Addr}; -which_addr2(Domain, [_|IFO]) -> - which_addr2(Domain, IFO). +which_local_host_info2(inet = _Domain, [{addr, Addr}|_IFO], Flags) + when (size(Addr) =:= 4) andalso (element(1, Addr) =/= 127) -> + {ok, {Flags, Addr}}; +which_local_host_info2(inet6 = _Domain, [{addr, Addr}|_IFO], Flags) + when (size(Addr) =:= 8) andalso + (element(1, Addr) =/= 0) andalso + (element(1, Addr) =/= 16#fe80) -> + {ok, {Flags, Addr}}; +which_local_host_info2(Domain, [_|IFO], Flags) -> + which_local_host_info2(Domain, IFO, Flags). + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Here are all the *general* test case condition functions. + +%% We also need (be able) to figure out the the multicast address, +%% which we only support for some platforms (linux and sunos). +%% We don't do that here, but since we can only do that (find a +%% multicast address) for specific platforms, we check that we are +%% on of those platforms here. +has_ip_multicast_support() -> + case os:type() of + {unix, OsName} when (OsName =:= linux) orelse + (OsName =:= sunos) -> + case which_local_host_info(inet) of + {ok, {_Name, Flags, _Addr}} -> + case lists:member(multicast, Flags) of + true -> + ok; + false -> + not_supported(multicast) + end; + {error, Reason} -> + not_supported({multicast, Reason}) + end; + Type -> + not_supported({multicast, Type}) + end. +has_ip_add_membership_support() -> + has_socket_option_ip_support(add_membership). +has_ip_drop_membership_support() -> + has_socket_option_ip_support(drop_membership). +has_socket_option_ip_support(Opt) -> + has_socket_option_support(ip, Opt). + +has_socket_option_support(Level, Option) -> + case socket:supports(options, Level, Option) of + true -> + ok; + false -> + not_supported({options, Level, Option}) + end. + -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% Here are all the *general* test vase condition functions. + +unix_domain_socket_host_cond() -> + unix_domain_socket_host_cond(os:type(), os:version()). + +unix_domain_socket_host_cond({unix, linux}, {M, _, _}) when (M < 3) -> + skip("TC may not work on this version"); +unix_domain_socket_host_cond(_, _) -> + ok. + +has_support_unix_domain_socket() -> + case os:type() of + {win32, _} -> + skip("Not supported"); + _ -> + case socket:supports(local) of + true -> + ok; + false -> + skip("Not supported") + end + end. + %% The idea is that this function shall test if the test host has %% support for IPv6. If not, there is no point in running IPv6 tests. @@ -17957,6 +24508,34 @@ has_support_ipv6() -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +unlink_path(Path) -> + unlink_path(Path, fun() -> ok end, fun() -> ok end). + +unlink_path(Path, Success, Failure) when is_list(Path) andalso + is_function(Success, 0) andalso + is_function(Failure, 0) -> + ?SEV_IPRINT("try unlink path: " + "~n ~s", [Path]), + case os:cmd("unlink " ++ Path) of + "" -> + ?SEV_IPRINT("path unlinked: " + "~n Path: ~s", [Path]), + Success(); + Result -> + ?SEV_EPRINT("unlink maybe failed: " + "~n Path: ~s" + "~n Res: ~s", [Path, Result]), + Failure() + end; +unlink_path(_, _, _) -> + ok. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +not_supported(What) -> + skip({not_supported, What}). + not_yet_implemented() -> skip("not yet implemented"). diff --git a/erts/emulator/test/socket_test_logger.erl b/erts/emulator/test/socket_test_logger.erl index 26610e9ef3..f5d4c8c7b2 100644 --- a/erts/emulator/test/socket_test_logger.erl +++ b/erts/emulator/test/socket_test_logger.erl @@ -43,7 +43,7 @@ start(Quiet) -> ok; undefined -> Self = self(), - Pid = spawn_link(fun() -> init(Self, Quiet) end), + Pid = spawn(fun() -> init(Self, Quiet) end), yes = global:register_name(?LOGGER, Pid), ok end. diff --git a/erts/emulator/test/socket_test_ttest_tcp_client.erl b/erts/emulator/test/socket_test_ttest_tcp_client.erl index 5efa3fe491..b5c5300fd0 100644 --- a/erts/emulator/test/socket_test_ttest_tcp_client.erl +++ b/erts/emulator/test/socket_test_ttest_tcp_client.erl @@ -42,16 +42,16 @@ -export([ %% These are for the test suite - start_monitor/6, start_monitor/7, start_monitor/9, + start_monitor/5, start_monitor/6, start_monitor/8, %% These are for starting in a shell when run "manually" - start/4, start/5, start/7, start/8, + start/3, start/4, start/6, start/7, stop/1 ]). %% Internal exports -export([ - do_start/10 + do_start/9 ]). -include_lib("kernel/include/inet.hrl"). @@ -80,25 +80,25 @@ %% ========================================================================== -start_monitor(Node, Notify, Transport, Active, Addr, Port) -> - start_monitor(Node, Notify, Transport, Active, Addr, Port, ?MSG_ID_DEFAULT). +start_monitor(Node, Notify, Transport, ServerInfo, Active) -> + start_monitor(Node, Notify, Transport, ServerInfo, Active, ?MSG_ID_DEFAULT). -start_monitor(Node, Notify, Transport, Active, Addr, Port, 1 = MsgID) -> - start_monitor(Node, Notify, Transport, Active, Addr, Port, MsgID, +start_monitor(Node, Notify, Transport, ServerInfo, Active, 1 = MsgID) -> + start_monitor(Node, Notify, Transport, ServerInfo, Active, MsgID, ?MAX_OUTSTANDING_DEFAULT_1, ?RUNTIME_DEFAULT); -start_monitor(Node, Notify, Transport, Active, Addr, Port, 2 = MsgID) -> - start_monitor(Node, Notify, Transport, Active, Addr, Port, MsgID, +start_monitor(Node, Notify, Transport, ServerInfo, Active, 2 = MsgID) -> + start_monitor(Node, Notify, Transport, ServerInfo, Active, MsgID, ?MAX_OUTSTANDING_DEFAULT_2, ?RUNTIME_DEFAULT); -start_monitor(Node, Notify, Transport, Active, Addr, Port, 3 = MsgID) -> - start_monitor(Node, Notify, Transport, Active, Addr, Port, MsgID, +start_monitor(Node, Notify, Transport, ServerInfo, Active, 3 = MsgID) -> + start_monitor(Node, Notify, Transport, ServerInfo, Active, MsgID, ?MAX_OUTSTANDING_DEFAULT_3, ?RUNTIME_DEFAULT). -start_monitor(Node, Notify, Transport, Active, Addr, Port, +start_monitor(Node, Notify, Transport, ServerInfo, Active, MsgID, MaxOutstanding, RunTime) when (Node =/= node()) -> Args = [false, self(), Notify, - Transport, Active, Addr, Port, MsgID, MaxOutstanding, RunTime], + Transport, ServerInfo, Active, MsgID, MaxOutstanding, RunTime], case rpc:call(Node, ?MODULE, do_start, Args) of {badrpc, _} = Reason -> {error, Reason}; @@ -108,11 +108,11 @@ start_monitor(Node, Notify, Transport, Active, Addr, Port, {error, _} = ERROR -> ERROR end; -start_monitor(_, Notify, Transport, Active, Addr, Port, +start_monitor(_, Notify, Transport, ServerInfo, Active, MsgID, MaxOutstanding, RunTime) -> case do_start(false, self(), Notify, - Transport, Active, Addr, Port, + Transport, Active, ServerInfo, MsgID, MaxOutstanding, RunTime) of {ok, Pid} -> MRef = erlang:monitor(process, Pid), @@ -122,50 +122,48 @@ start_monitor(_, Notify, Transport, Active, Addr, Port, end. -start(Transport, Active, Addr, Port) -> - start(Transport, Active, Addr, Port, ?MSG_ID_DEFAULT). +start(Transport, ServerInfo, Active) -> + start(Transport, ServerInfo, Active, ?MSG_ID_DEFAULT). -start(Transport, Active, Addr, Port, 1 = MsgID) -> +start(Transport, ServerInfo, Active, 1 = MsgID) -> start(false, - Transport, Active, Addr, Port, MsgID, + Transport, ServerInfo, Active, MsgID, ?MAX_OUTSTANDING_DEFAULT_1, ?RUNTIME_DEFAULT); -start(Transport, Active, Addr, Port, 2 = MsgID) -> +start(Transport, ServerInfo, Active, 2 = MsgID) -> start(false, - Transport, Active, Addr, Port, MsgID, + Transport, ServerInfo, Active, MsgID, ?MAX_OUTSTANDING_DEFAULT_2, ?RUNTIME_DEFAULT); -start(Transport, Active, Addr, Port, 3 = MsgID) -> +start(Transport, ServerInfo, Active, 3 = MsgID) -> start(false, - Transport, Active, Addr, Port, MsgID, + Transport, ServerInfo, Active, MsgID, ?MAX_OUTSTANDING_DEFAULT_3, ?RUNTIME_DEFAULT). -start(Transport, Active, Addr, Port, MsgID, MaxOutstanding, RunTime) -> +start(Transport, ServerInfo, Active, MsgID, MaxOutstanding, RunTime) -> start(false, - Transport, Active, Addr, Port, MsgID, MaxOutstanding, RunTime). + Transport, ServerInfo, Active, MsgID, MaxOutstanding, RunTime). -start(Quiet, Transport, Active, Addr, Port, MsgID, MaxOutstanding, RunTime) -> +start(Quiet, Transport, ServerInfo, Active, MsgID, MaxOutstanding, RunTime) -> Notify = fun(R) -> present_results(R) end, do_start(Quiet, self(), Notify, - Transport, Active, Addr, Port, MsgID, MaxOutstanding, RunTime). + Transport, ServerInfo, Active, MsgID, MaxOutstanding, RunTime). -spec do_start(Quiet, Parent, Notify, Transport, + ServerInfo, Active, - Addr, - Port, MsgID, MaxOutstanding, RunTime) -> {ok, Pid} | {error, Reason} when - Quiet :: pid(), + Quiet :: boolean(), Parent :: pid(), Notify :: function(), Transport :: atom() | tuple(), + ServerInfo :: {inet:ip_address(), inet:port_number()} | string(), Active :: active(), - Addr :: inet:ip_address(), - Port :: inet:port_number(), MsgID :: msg_id(), MaxOutstanding :: max_outstanding(), RunTime :: runtime(), @@ -174,14 +172,13 @@ start(Quiet, Transport, Active, Addr, Port, MsgID, MaxOutstanding, RunTime) -> do_start(Quiet, Parent, Notify, - Transport, Active, Addr, Port, MsgID, MaxOutstanding, RunTime) + Transport, ServerInfo, Active, MsgID, MaxOutstanding, RunTime) when is_boolean(Quiet) andalso is_pid(Parent) andalso is_function(Notify) andalso (is_atom(Transport) orelse is_tuple(Transport)) andalso (is_boolean(Active) orelse (Active =:= once)) andalso - is_tuple(Addr) andalso - (is_integer(Port) andalso (Port > 0)) andalso + (is_tuple(ServerInfo) orelse is_list(ServerInfo)) andalso (is_integer(MsgID) andalso (MsgID >= 1) andalso (MsgID =< 3)) andalso (is_integer(MaxOutstanding) andalso (MaxOutstanding > 0)) andalso (is_integer(RunTime) andalso (RunTime > 0)) -> @@ -191,7 +188,7 @@ do_start(Quiet, Starter, Parent, Notify, - Transport, Active, Addr, Port, + Transport, Active, ServerInfo, MsgID, MaxOutstanding, RunTime) end, {Pid, MRef} = spawn_monitor(Init), @@ -217,25 +214,30 @@ stop(Pid) when is_pid(Pid) -> init(Quiet, Starter, Parent, Notify, - Transport, Active, Addr, Port, + Transport, Active, ServerInfo, MsgID, MaxOutstanding, RunTime) -> if not Quiet -> ?I("init with" "~n Transport: ~p" "~n Active: ~p" - "~n Addr: ~s" - "~n Port: ~p" + "~n ServerInfo: ~s" "~n Msg ID: ~p (=> 16 + ~w bytes)" "~n Max Outstanding: ~p" "~n (Suggested) Run Time: ~p ms", - [Transport, Active, inet:ntoa(Addr), Port, + [Transport, Active, + case ServerInfo of + {Addr, Port} -> + ?F("Addr: ~s, Port: ~w", [inet:ntoa(Addr), Port]); + Path -> + Path + end, MsgID, size(which_msg_data(MsgID)), MaxOutstanding, RunTime]); true -> ok end, {Mod, Connect} = process_transport(Transport), - case Connect(Addr, Port) of + case Connect(ServerInfo) of {ok, Sock} -> if not Quiet -> ?I("connected"); true -> ok @@ -269,9 +271,15 @@ init(Quiet, end. process_transport(Mod) when is_atom(Mod) -> - {Mod, fun(A, P) -> Mod:connect(A, P) end}; -process_transport({Mod, Opts}) -> - {Mod, fun(A, P) -> Mod:connect(A, P, Opts) end}. + %% In this case we assume it to be a plain tcp socket + {Mod, fun({A, P}) -> Mod:connect(A, P) end}; +process_transport({Mod, #{domain := Domain} = Opts}) -> + Connect = + case Domain of + local -> fun(Path) -> Mod:connect(Path, Opts) end; + _ -> fun({A, P}) -> Mod:connect(A, P, Opts) end + end, + {Mod, Connect}. which_msg_data(1) -> ?MSG_DATA1; diff --git a/erts/emulator/test/socket_test_ttest_tcp_client_gen.erl b/erts/emulator/test/socket_test_ttest_tcp_client_gen.erl index 0ec2e908d7..65a3a94d38 100644 --- a/erts/emulator/test/socket_test_ttest_tcp_client_gen.erl +++ b/erts/emulator/test/socket_test_ttest_tcp_client_gen.erl @@ -21,28 +21,28 @@ -module(socket_test_ttest_tcp_client_gen). -export([ - start/3, start/4, start/6, start/7, + start/2, start/3, start/5, start/6, stop/1 ]). -define(TRANSPORT_MOD, socket_test_ttest_tcp_gen). -start(Active, Addr, Port) -> - socket_test_ttest_tcp_client:start(?TRANSPORT_MOD, Active, Addr, Port). +start(ServerInfo, Active) -> + socket_test_ttest_tcp_client:start(?TRANSPORT_MOD, ServerInfo, Active). -start(Active, Addr, Port, MsgID) -> - socket_test_ttest_tcp_client:start(?TRANSPORT_MOD, Active, Addr, Port, MsgID). +start(ServerInfo, Active, MsgID) -> + socket_test_ttest_tcp_client:start(?TRANSPORT_MOD, ServerInfo, Active, MsgID). -start(Active, Addr, Port, MsgID, MaxOutstanding, RunTime) -> +start(ServerInfo, Active, MsgID, MaxOutstanding, RunTime) -> socket_test_ttest_tcp_client:start(false, ?TRANSPORT_MOD, - Active, Addr, Port, + ServerInfo, Active, MsgID, MaxOutstanding, RunTime). -start(Quiet, Active, Addr, Port, MsgID, MaxOutstanding, RunTime) -> +start(Quiet, ServerInfo, Active, MsgID, MaxOutstanding, RunTime) -> socket_test_ttest_tcp_client:start(Quiet, ?TRANSPORT_MOD, - Active, Addr, Port, + ServerInfo, Active, MsgID, MaxOutstanding, RunTime). stop(Pid) -> diff --git a/erts/emulator/test/socket_test_ttest_tcp_client_socket.erl b/erts/emulator/test/socket_test_ttest_tcp_client_socket.erl index acf2556793..ca7eff4437 100644 --- a/erts/emulator/test/socket_test_ttest_tcp_client_socket.erl +++ b/erts/emulator/test/socket_test_ttest_tcp_client_socket.erl @@ -26,25 +26,90 @@ ]). -define(TRANSPORT_MOD, socket_test_ttest_tcp_socket). --define(MOD(M), {?TRANSPORT_MOD, #{method => Method}}). +-define(MOD(D, A, M), {?TRANSPORT_MOD, #{domain => D, + async => A, + method => M}}). -start(Method, Active, Addr, Port) -> - socket_test_ttest_tcp_client:start_monitor(?MOD(Method), Active, Addr, Port). +start(Method, Async, Active, ServerInfo) + when is_list(ServerInfo) -> + Domain = local, + socket_test_ttest_tcp_client:start_monitor(?MOD(Domain, Async, Method), + Active, ServerInfo); +start(Method, Async, Active, ServerInfo = {Addr, _}) + when is_tuple(Addr) andalso (size(Addr) =:= 4) -> + Domain = inet, + socket_test_ttest_tcp_client:start_monitor(?MOD(Domain, Async, Method), + Active, ServerInfo); +start(Method, Async, Active, ServerInfo = {Addr, _}) + when is_tuple(Addr) andalso (size(Addr) =:= 8) -> + Domain = inet6, + socket_test_ttest_tcp_client:start_monitor(?MOD(Domain, Async, Method), + Active, ServerInfo). -start(Method, Active, Addr, Port, MsgID) -> - socket_test_ttest_tcp_client:start(?MOD(Method), - Active, Addr, Port, MsgID). +start(Method, Async, Active, ServerInfo, MsgID) + when is_list(ServerInfo) -> + %% This is just a simplification + Domain = local, + socket_test_ttest_tcp_client:start(?MOD(Domain, Async, Method), + Active, ServerInfo, MsgID); +start(Method, Async, Active, ServerInfo = {Addr, _}, MsgID) + when is_tuple(Addr) andalso (size(Addr) =:= 4) -> + %% This is just a simplification + Domain = inet, + socket_test_ttest_tcp_client:start(?MOD(Domain, Async, Method), + Active, ServerInfo, MsgID); +start(Method, Async, Active, ServerInfo = {Addr, _}, MsgID) + when is_tuple(Addr) andalso (size(Addr) =:= 8) -> + Domain = inet6, + socket_test_ttest_tcp_client:start(?MOD(Domain, Async, Method), + Active, ServerInfo, MsgID). -start(Method, Active, Addr, Port, MsgID, MaxOutstanding, RunTime) -> +start(Method, Async, Active, ServerInfo, MsgID, MaxOutstanding, RunTime) + when is_list(ServerInfo) -> + Domain = local, socket_test_ttest_tcp_client:start(false, - ?MOD(Method), - Active, Addr, Port, + ?MOD(Domain, Async, Method), + Active, ServerInfo, + MsgID, MaxOutstanding, RunTime); +start(Method, Async, Active, ServerInfo = {Addr, _}, + MsgID, MaxOutstanding, RunTime) + when is_tuple(Addr) andalso (size(Addr) =:= 4) -> + Domain = inet, + socket_test_ttest_tcp_client:start(false, + ?MOD(Domain, Async, Method), + Active, ServerInfo, + MsgID, MaxOutstanding, RunTime); +start(Method, Async, Active, ServerInfo = {Addr, _}, + MsgID, MaxOutstanding, RunTime) + when is_tuple(Addr) andalso (size(Addr) =:= 8) -> + Domain = inet6, + socket_test_ttest_tcp_client:start(false, + ?MOD(Domain, Async, Method), + Active, ServerInfo, MsgID, MaxOutstanding, RunTime). -start(Quiet, Method, Active, Addr, Port, MsgID, MaxOutstanding, RunTime) -> +start(Quiet, Async, Active, Method, ServerInfo, MsgID, MaxOutstanding, RunTime) + when is_list(ServerInfo) -> + Domain = local, + socket_test_ttest_tcp_client:start(Quiet, + ?MOD(Domain, Async, Method), + Active, ServerInfo, + MsgID, MaxOutstanding, RunTime); +start(Quiet, Async, Active, Method, ServerInfo = {Addr, _}, + MsgID, MaxOutstanding, RunTime) + when is_tuple(Addr) andalso (size(Addr) =:= 4) -> + Domain = inet, + socket_test_ttest_tcp_client:start(Quiet, + ?MOD(Domain, Async, Method), + Active, ServerInfo, + MsgID, MaxOutstanding, RunTime); +start(Quiet, Async, Active, Method, ServerInfo = {Addr, _}, + MsgID, MaxOutstanding, RunTime) + when is_tuple(Addr) andalso (size(Addr) =:= 8) -> + Domain = inet6, socket_test_ttest_tcp_client:start(Quiet, - ?MOD(Method), - Active, Addr, Port, + ?MOD(Domain, Async, Method), + Active, ServerInfo, MsgID, MaxOutstanding, RunTime). stop(Pid) -> diff --git a/erts/emulator/test/socket_test_ttest_tcp_gen.erl b/erts/emulator/test/socket_test_ttest_tcp_gen.erl index 604408c489..05b250e3d9 100644 --- a/erts/emulator/test/socket_test_ttest_tcp_gen.erl +++ b/erts/emulator/test/socket_test_ttest_tcp_gen.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2018-2018. All Rights Reserved. +%% Copyright Ericsson AB 2018-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -24,9 +24,9 @@ accept/1, accept/2, active/2, close/1, - connect/2, + connect/2, connect/3, controlling_process/2, - listen/0, listen/1, + listen/0, listen/1, listen/2, peername/1, port/1, recv/2, recv/3, @@ -80,6 +80,13 @@ close(Sock) -> connect(Addr, Port) -> Opts = [binary, {packet, raw}, {active, false}, {buffer, 32*1024}], + do_connect(Addr, Port, Opts). + +connect(Addr, Port, #{domain := Domain}) -> + Opts = [Domain, binary, {packet, raw}, {active, false}, {buffer, 32*1024}], + do_connect(Addr, Port, Opts). + +do_connect(Addr, Port, Opts) -> case gen_tcp:connect(Addr, Port, Opts) of {ok, Sock} -> {ok, Sock}; @@ -95,8 +102,12 @@ controlling_process(Sock, NewPid) -> listen() -> listen(0). -listen(Port) when is_integer(Port) andalso (Port >= 0) -> - Opts = [binary, {ip, {0,0,0,0}}, {packet, raw}, {active, false}, +listen(Port) -> + listen(Port, #{domain => inet}). + +listen(Port, #{domain := Domain}) when is_integer(Port) andalso (Port >= 0) -> + Opts = [Domain, + binary, {ip, {0,0,0,0}}, {packet, raw}, {active, false}, {buffer, 32*1024}], case gen_tcp:listen(Port, Opts) of {ok, Sock} -> diff --git a/erts/emulator/test/socket_test_ttest_tcp_server.erl b/erts/emulator/test/socket_test_ttest_tcp_server.erl index e8d626e3d8..27b561d4b7 100644 --- a/erts/emulator/test/socket_test_ttest_tcp_server.erl +++ b/erts/emulator/test/socket_test_ttest_tcp_server.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2018-2018. All Rights Reserved. +%% Copyright Ericsson AB 2018-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -90,14 +90,17 @@ start_monitor(_, Transport, Active) -> start(Transport, Active) -> do_start(self(), Transport, Active). - +%% Note that the Async option is actually only "used" for the +%% socket transport module (it details how to implement the +%% active feature). do_start(Parent, Transport, Active) when is_pid(Parent) andalso (is_atom(Transport) orelse is_tuple(Transport)) andalso (is_boolean(Active) orelse (Active =:= once)) -> Starter = self(), - ServerInit = fun() -> put(sname, "server"), - server_init(Starter, Parent, Transport, Active) + ServerInit = fun() -> + put(sname, "server"), + server_init(Starter, Parent, Transport, Active) end, {Pid, MRef} = spawn_monitor(ServerInit), receive @@ -126,17 +129,29 @@ server_init(Starter, Parent, Transport, Active) -> case Listen(0) of {ok, LSock} -> case Mod:port(LSock) of - {ok, Port} -> - Addr = which_addr(), % This is just for convenience - ?I("listening on:" - "~n Addr: ~p (~s)" - "~n Port: ~w" - "~n", [Addr, inet:ntoa(Addr), Port]), - Starter ! {?MODULE, self(), {ok, {Addr, Port}}}, + {ok, PortOrPath} -> + Result = + if + is_integer(PortOrPath) -> + %% This is just for convenience + Addr = which_addr(), + ?I("listening on:" + "~n Addr: ~p (~s)" + "~n Port: ~w" + "~n", [Addr, inet:ntoa(Addr), PortOrPath]), + {Addr, PortOrPath}; + is_list(PortOrPath) -> + ?I("listening on:" + "~n Path: ~s" + "~n", [PortOrPath]), + PortOrPath + end, + Starter ! {?MODULE, self(), {ok, Result}}, server_loop(#{parent => Parent, mod => Mod, active => Active, lsock => LSock, + port_or_path => PortOrPath, handlers => [], stats_interval => StatsInterval, %% Accumulation @@ -166,49 +181,68 @@ process_transport({Mod, Opts}, _Active) -> server_loop(State) -> - server_loop( server_handle_message( server_accept(State) ) ). + server_loop( server_handle_message( server_accept(State, ?ACC_TIMEOUT), 0) ). -server_accept(#{mod := Mod, - active := Active, - lsock := LSock, - handlers := Handlers} = State) -> - case Mod:accept(LSock, ?ACC_TIMEOUT) of +server_accept(#{mod := Mod, lsock := LSock} = State, Timeout) -> + case Mod:accept(LSock, Timeout) of {ok, Sock} -> - ?I("accepted connection from ~s", - [case Mod:peername(Sock) of - {ok, Peer} -> - format_peername(Peer); - {error, _} -> - "-" - end]), - {Pid, _} = handler_start(), - ?I("handler ~p started -> try transfer socket control", [Pid]), - case Mod:controlling_process(Sock, Pid) of - ok -> - maybe_start_stats_timer(State, Pid), - ?I("server-accept: handler ~p started", [Pid]), - handler_continue(Pid, Mod, Sock, Active), - Handlers2 = [Pid | Handlers], - State#{handlers => Handlers2}; - {error, CPReason} -> - (catch Mod:close(Sock)), - (catch Mod:close(LSock)), - exit({controlling_process, CPReason}) - end; - {error, timeout} -> + server_handle_accepted(State, Sock); + {error, timeout} when (Timeout =/= nowait) -> State; {error, AReason} -> (catch Mod:close(LSock)), exit({accept, AReason}) end. +%% server_accept(#{mod := Mod, +%% lsock := LSock} = State) -> +%% case Mod:accept(LSock, ?ACC_TIMEOUT) of +%% {ok, Sock} -> +%% server_handle_accepted(State, Sock); +%% {error, timeout} -> +%% State; +%% {error, AReason} -> +%% (catch Mod:close(LSock)), +%% exit({accept, AReason}) +%% end. + +server_handle_accepted(#{mod := Mod, + lsock := LSock, + active := Active, + handlers := Handlers} = State, + Sock) -> + ?I("accepted connection from ~s", + [case Mod:peername(Sock) of + {ok, Peer} -> + format_peername(Peer); + {error, _} -> + "-" + end]), + {Pid, _} = handler_start(), + ?I("handler ~p started -> try transfer socket control", [Pid]), + case Mod:controlling_process(Sock, Pid) of + ok -> + maybe_start_stats_timer(State, Pid), + ?I("server-accept: handler ~p started", [Pid]), + handler_continue(Pid, Mod, Sock, Active), + Handlers2 = [Pid | Handlers], + State#{handlers => Handlers2}; + {error, CPReason} -> + (catch Mod:close(Sock)), + (catch Mod:close(LSock)), + exit({controlling_process, CPReason}) + end. + + format_peername({Addr, Port}) -> case inet:gethostbyaddr(Addr) of {ok, #hostent{h_name = N}} -> ?F("~s (~s:~w)", [N, inet:ntoa(Addr), Port]); {error, _} -> ?F("~p, ~p", [Addr, Port]) - end. + end; +format_peername(Path) when is_list(Path) -> + Path. maybe_start_stats_timer(#{active := Active, stats_interval := Time}, Handler) when (Active =/= false) andalso (is_integer(Time) andalso (Time > 0)) -> @@ -219,7 +253,10 @@ maybe_start_stats_timer(_, _) -> start_stats_timer(Time, ProcStr, Pid) -> erlang:start_timer(Time, self(), {stats, Time, ProcStr, Pid}). -server_handle_message(#{parent := Parent, handlers := H} = State) -> +server_handle_message(#{mod := Mod, + lsock := LSock, + parent := Parent, + handlers := H} = State, Timeout) -> receive {timeout, _TRef, {stats, Interval, ProcStr, Pid}} -> case server_handle_stats(ProcStr, Pid) of @@ -229,16 +266,17 @@ server_handle_message(#{parent := Parent, handlers := H} = State) -> ok end, State; - + {?MODULE, Ref, Parent, stop} -> reply(Parent, Ref, ok), lists:foreach(fun(P) -> handler_stop(P) end, H), + (catch Mod:close(LSock)), exit(normal); {'DOWN', _MRef, process, Pid, Reason} -> server_handle_down(Pid, Reason, State) - after 0 -> + after Timeout -> State end. @@ -272,28 +310,26 @@ server_handle_handler_down(Pid, AccMCnt2 = AccMCnt + MCnt, AccBCnt2 = AccBCnt + BCnt, AccHCnt2 = AccHCnt + 1, - ?I("handler ~p (~w) done => accumulated results: " - "~n Run Time: ~s ms" + MsgCount2Str = + fun(RT, ART, MC, AMC) when (RT > 0) -> + ?F("~w => ~w (~w) msgs / ms", [MC, MC div RT, AMC div ART]); + (_, _, MC, AMC) -> + ?F("~w (~w)", [MC, AMC]) + end, + ByteCount2Str = + fun(RT, ART, BC, ABC) when (RT > 0) -> + ?F("~w => ~w (~w) bytes / ms", [BC, BC div RT, ABC div ART]); + (_, _, BC, ABC) -> + ?F("~w", [BC, ABC]) + end, + ?I("handler ~p (~w) done: " + "~n Run Time: ~s" "~n Message Count: ~s" "~n Byte Count: ~s", [Pid, AccHCnt2, - ?FORMAT_TIME(AccRunTime2), - if (AccRunTime2 > 0) -> - ?F("~w => ~w (~w) msgs / ms", - [AccMCnt2, - AccMCnt2 div AccRunTime2, - (AccMCnt2 div AccHCnt2) div AccRunTime2]); - true -> - ?F("~w", [AccMCnt2]) - end, - if (AccRunTime2 > 0) -> - ?F("~w => ~w (~w) bytes / ms", - [AccBCnt2, - AccBCnt2 div AccRunTime2, - (AccBCnt2 div AccHCnt2) div AccRunTime2]); - true -> - ?F("~w", [AccBCnt2]) - end]), + ?FORMAT_TIME(RunTime), + MsgCount2Str(RunTime, AccRunTime2, MCnt, AccMCnt2), + ByteCount2Str(RunTime, AccRunTime2, BCnt, AccBCnt2)]), State#{runtime => AccRunTime2, mcnt => AccMCnt2, bcnt => AccBCnt2, @@ -325,15 +361,15 @@ handler_init(Parent) -> ?I("received continue"), reply(Parent, Ref, ok), handler_initial_activation(Mod, Sock, Active), - handler_loop(#{parent => Parent, - mod => Mod, - sock => Sock, - active => Active, - start => ?T(), - mcnt => 0, - bcnt => 0, - last_reply => none, - acc => <<>>}) + handler_loop(#{parent => Parent, + mod => Mod, + sock => Sock, + active => Active, + start => ?T(), + mcnt => 0, + bcnt => 0, + last_reply => none, + acc => <<>>}) after 5000 -> ?I("timeout when message queue: " diff --git a/erts/emulator/test/socket_test_ttest_tcp_server_gen.erl b/erts/emulator/test/socket_test_ttest_tcp_server_gen.erl index b1b31f5158..fdf40f1369 100644 --- a/erts/emulator/test/socket_test_ttest_tcp_server_gen.erl +++ b/erts/emulator/test/socket_test_ttest_tcp_server_gen.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2018-2018. All Rights Reserved. +%% Copyright Ericsson AB 2018-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -21,20 +21,18 @@ -module(socket_test_ttest_tcp_server_gen). -export([ - start/1, + start/1, start/2, stop/1 ]). -define(TRANSPORT_MOD, socket_test_ttest_tcp_gen). +-define(MOD(D), {?TRANSPORT_MOD, #{domain => D}}). start(Active) -> - socket_test_ttest_tcp_server:start(?TRANSPORT_MOD, Active). - %% {ok, {Pid, AddrPort}} -> - %% MRef = erlang:monitor(process, Pid), - %% {ok, {Pid, MRef, AddrPort}}; - %% {error, _} = ERROR -> - %% ERROR - %% end. + start(inet, Active). + +start(Domain, Active) -> + socket_test_ttest_tcp_server:start(?MOD(Domain), Active). stop(Pid) -> diff --git a/erts/emulator/test/socket_test_ttest_tcp_server_socket.erl b/erts/emulator/test/socket_test_ttest_tcp_server_socket.erl index b7ea1e8e93..4045bf4e4e 100644 --- a/erts/emulator/test/socket_test_ttest_tcp_server_socket.erl +++ b/erts/emulator/test/socket_test_ttest_tcp_server_socket.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2018-2018. All Rights Reserved. +%% Copyright Ericsson AB 2018-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -21,17 +21,20 @@ -module(socket_test_ttest_tcp_server_socket). -export([ - start/2, + start/4, stop/1 ]). -define(TRANSPORT_MOD, socket_test_ttest_tcp_socket). -%% -define(MOD(M), {?TRANSPORT_MOD, #{method => M, +%% -define(MOD(M), {?TRANSPORT_MOD, #{async => false, +%% method => M, %% stats_interval => 10000}}). --define(MOD(M), {?TRANSPORT_MOD, #{method => M}}). +-define(MOD(D,M,A), {?TRANSPORT_MOD, #{domain => D, + async => A, + method => M}}). -start(Method, Active) -> - socket_test_ttest_tcp_server:start(?MOD(Method), Active). +start(Method, Domain, Async, Active) -> + socket_test_ttest_tcp_server:start(?MOD(Domain, Method, Async), Active). %% {ok, {Pid, AddrPort}} -> %% MRef = erlang:monitor(process, Pid), %% {ok, {Pid, MRef, AddrPort}}; diff --git a/erts/emulator/test/socket_test_ttest_tcp_socket.erl b/erts/emulator/test/socket_test_ttest_tcp_socket.erl index 0ae2412e4c..3aa3b2c504 100644 --- a/erts/emulator/test/socket_test_ttest_tcp_socket.erl +++ b/erts/emulator/test/socket_test_ttest_tcp_socket.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2018-2018. All Rights Reserved. +%% Copyright Ericsson AB 2018-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -24,7 +24,7 @@ accept/1, accept/2, active/2, close/1, - connect/2, connect/3, + connect/1, connect/2, connect/3, controlling_process/2, listen/0, listen/1, listen/2, port/1, @@ -36,6 +36,8 @@ ]). +-define(LIB, socket_test_lib). + -define(READER_RECV_TIMEOUT, 1000). -define(DATA_MSG(Sock, Method, Data), @@ -55,7 +57,7 @@ %% ========================================================================== -%% This does not really work. Its just a placeholder for the time beeing... +%% This does not really work. Its just a placeholder for the time being... %% getopt(Sock, Opt) when is_atom(Opt) -> %% socket:getopt(Sock, socket, Opt). @@ -66,22 +68,32 @@ %% ========================================================================== -accept(#{sock := LSock, opts := #{method := Method} = Opts}) -> +%% The way we use server async its no point in doing a async accept call +%% (we do never actually run the test with more than one client). +accept(#{sock := LSock, opts := #{async := Async, + method := Method} = Opts}) -> case socket:accept(LSock) of - {ok, Sock} -> + {ok, Sock} -> Self = self(), - Reader = spawn(fun() -> reader_init(Self, Sock, false, Method) end), + Reader = spawn(fun() -> + reader_init(Self, Sock, Async, false, Method) + end), maybe_start_stats_timer(Opts, Reader), {ok, #{sock => Sock, reader => Reader, method => Method}}; {error, _} = ERROR -> ERROR end. -accept(#{sock := LSock, opts := #{method := Method} = Opts}, Timeout) -> +%% If a timeout has been explictly specified, then we do not use +%% async here. We will pass it on to the reader process. +accept(#{sock := LSock, opts := #{async := Async, + method := Method} = Opts}, Timeout) -> case socket:accept(LSock, Timeout) of {ok, Sock} -> Self = self(), - Reader = spawn(fun() -> reader_init(Self, Sock, false, Method) end), + Reader = spawn(fun() -> + reader_init(Self, Sock, Async, false, Method) + end), maybe_start_stats_timer(Opts, Reader), {ok, #{sock => Sock, reader => Reader, method => Method}}; {error, _} = ERROR -> @@ -97,41 +109,92 @@ active(#{reader := Pid}, NewActive) close(#{sock := Sock, reader := Pid}) -> Pid ! {?MODULE, stop}, - socket:close(Sock). + Unlink = case socket:sockname(Sock) of + {ok, #{family := local, path := Path}} -> + fun() -> os:cmd("unlink " ++ Path), ok end; + _ -> + fun() -> ok end + end, + Res = socket:close(Sock), + Unlink(), + Res. %% Create a socket and connect it to a peer -connect(Addr, Port) -> - connect(Addr, Port, #{method => plain}). - -connect(Addr, Port, #{method := Method} = Opts) -> +connect(ServerPath) when is_list(ServerPath) -> + Domain = local, + ClientPath = mk_unique_path(), + LocalSA = #{family => Domain, + path => ClientPath}, + ServerSA = #{family => Domain, path => ServerPath}, + Opts = #{domain => Domain, + proto => default, + method => plain}, + Cleanup = fun() -> os:cmd("unlink " ++ ClientPath), ok end, + do_connect(LocalSA, ServerSA, Cleanup, Opts). + +connect(Addr, Port) when is_tuple(Addr) andalso is_integer(Port) -> + Domain = inet, + LocalSA = any, + ServerSA = #{family => Domain, + addr => Addr, + port => Port}, + Opts = #{domain => Domain, + proto => tcp, + method => plain}, + Cleanup = fun() -> ok end, + do_connect(LocalSA, ServerSA, Cleanup, Opts); +connect(ServerPath, + #{domain := local = Domain} = Opts) + when is_list(ServerPath) -> + ClientPath = mk_unique_path(), + LocalSA = #{family => Domain, + path => ClientPath}, + ServerSA = #{family => Domain, + path => ServerPath}, + Cleanup = fun() -> os:cmd("unlink " ++ ClientPath), ok end, + do_connect(LocalSA, ServerSA, Cleanup, Opts#{proto => default}). + +connect(Addr, Port, #{domain := Domain} = Opts) -> + LocalSA = any, + ServerSA = #{family => Domain, + addr => Addr, + port => Port}, + Cleanup = fun() -> ok end, + do_connect(LocalSA, ServerSA, Cleanup, Opts#{proto => tcp}). + +do_connect(LocalSA, ServerSA, Cleanup, #{domain := Domain, + proto := Proto, + async := Async, + method := Method} = Opts) -> try begin Sock = - case socket:open(inet, stream, tcp) of + case socket:open(Domain, stream, Proto) of {ok, S} -> S; {error, OReason} -> throw({error, {open, OReason}}) end, - case socket:bind(Sock, any) of + case socket:bind(Sock, LocalSA) of {ok, _} -> ok; {error, BReason} -> (catch socket:close(Sock)), + Cleanup(), throw({error, {bind, BReason}}) end, - SA = #{family => inet, - addr => Addr, - port => Port}, - case socket:connect(Sock, SA) of + case socket:connect(Sock, ServerSA) of ok -> ok; {error, CReason} -> (catch socket:close(Sock)), + Cleanup(), throw({error, {connect, CReason}}) end, Self = self(), - Reader = spawn(fun() -> reader_init(Self, Sock, false, Method) end), + Reader = spawn(fun() -> + reader_init(Self, Sock, Async, false, Method) + end), maybe_start_stats_timer(Opts, Reader), {ok, #{sock => Sock, reader => Reader, method => Method}} end @@ -140,6 +203,9 @@ connect(Addr, Port, #{method := Method} = Opts) -> ERROR end. +mk_unique_path() -> + [NodeName | _] = string:tokens(atom_to_list(node()), [$@]), + ?LIB:f("/tmp/esock_~s_~w", [NodeName, erlang:system_time(nanosecond)]). maybe_start_stats_timer(#{stats_to := Pid, stats_interval := T}, @@ -163,37 +229,59 @@ controlling_process(#{sock := Sock, reader := Pid}, NewPid) -> %% Create a listen socket listen() -> - listen(0, #{method => plain}). - -listen(Port) -> - listen(Port, #{method => plain}). -listen(Port, #{method := Method} = Opts) - when (is_integer(Port) andalso (Port >= 0)) andalso - ((Method =:= plain) orelse (Method =:= msg)) -> + listen(0). + +listen(Port) when is_integer(Port) -> + listen(Port, #{domain => inet, async => false, method => plain}); +listen(Path) when is_list(Path) -> + listen(Path, #{domain => local, async => false, method => plain}). + +listen(0, #{domain := local} = Opts) -> + listen(mk_unique_path(), Opts); +listen(Path, #{domain := local = Domain} = Opts) + when is_list(Path) andalso (Path =/= []) -> + SA = #{family => Domain, + path => Path}, + Cleanup = fun() -> os:cmd("unlink " ++ Path), ok end, + do_listen(SA, Cleanup, Opts#{proto => default}); +listen(Port, #{domain := Domain} = Opts) + when is_integer(Port) andalso (Port >= 0) -> + %% Bind fills in the rest + SA = #{family => Domain, + port => Port}, + Cleanup = fun() -> ok end, + do_listen(SA, Cleanup, Opts#{proto => tcp}). + +do_listen(SA, + Cleanup, + #{domain := Domain, proto := Proto, + async := Async, method := Method} = Opts) + when (Method =:= plain) orelse (Method =:= msg) andalso + is_boolean(Async) -> try begin - Sock = case socket:open(inet, stream, tcp) of + Sock = case socket:open(Domain, stream, Proto) of {ok, S} -> S; {error, OReason} -> throw({error, {open, OReason}}) end, - SA = #{family => inet, - port => Port}, case socket:bind(Sock, SA) of {ok, _} -> ok; {error, BReason} -> (catch socket:close(Sock)), + Cleanup(), throw({error, {bind, BReason}}) end, case socket:listen(Sock) of ok -> - ok; - {error, LReason} -> + ok; + {error, LReason} -> (catch socket:close(Sock)), - throw({error, {listen, LReason}}) - end, + Cleanup(), + throw({error, {listen, LReason}}) + end, {ok, #{sock => Sock, opts => Opts}} end catch @@ -204,6 +292,8 @@ listen(Port, #{method := Method} = Opts) port(#{sock := Sock}) -> case socket:sockname(Sock) of + {ok, #{family := local, path := Path}} -> + {ok, Path}; {ok, #{port := Port}} -> {ok, Port}; {error, _} = ERROR -> @@ -213,6 +303,8 @@ port(#{sock := Sock}) -> peername(#{sock := Sock}) -> case socket:peername(Sock) of + {ok, #{family := local, path := Path}} -> + {ok, Path}; {ok, #{addr := Addr, port := Port}} -> {ok, {Addr, Port}}; {error, _} = ERROR -> @@ -262,13 +354,18 @@ sockname(#{sock := Sock}) -> %% ========================================================================== -reader_init(ControllingProcess, Sock, Active, Method) +reader_init(ControllingProcess, Sock, Async, Active, Method) when is_pid(ControllingProcess) andalso + is_boolean(Async) andalso (is_boolean(Active) orelse (Active =:= once)) andalso ((Method =:= plain) orelse (Method =:= msg)) -> + put(verbose, false), MRef = erlang:monitor(process, ControllingProcess), reader_loop(#{ctrl_proc => ControllingProcess, ctrl_proc_mref => MRef, + async => Async, + select_info => undefined, + select_num => 0, % Count the number of select messages active => Active, sock => Sock, method => Method}). @@ -279,11 +376,11 @@ reader_loop(#{active := false, ctrl_proc := Pid} = State) -> receive {?MODULE, stop} -> - exit(normal); + reader_exit(State, stop); {?MODULE, Pid, controlling_process, NewPid} -> - MRef = maps:get(ctrl_proc_mref, State), - erlang:demonitor(MRef, [flush]), + OldMRef = maps:get(ctrl_proc_mref, State), + erlang:demonitor(OldMRef, [flush]), NewMRef = erlang:monitor(process, NewPid), Pid ! {?MODULE, self(), controlling_process}, reader_loop(State#{ctrl_proc => NewPid, @@ -294,10 +391,8 @@ reader_loop(#{active := false, {'DOWN', MRef, process, Pid, Reason} -> case maps:get(ctrl_proc_mref, State) of - MRef when (Reason =:= normal) -> - exit(normal); MRef -> - exit({controlling_process, Reason}); + reader_exit(State, {ctrl_exit, Reason}); _ -> reader_loop(State) end @@ -305,7 +400,8 @@ reader_loop(#{active := false, %% Read *once* and then change to false reader_loop(#{active := once, - sock := Sock, + async := false, + sock := Sock, method := Method, ctrl_proc := Pid} = State) -> case do_recv(Method, Sock) of @@ -315,25 +411,23 @@ reader_loop(#{active := once, {error, timeout} -> receive {?MODULE, stop} -> - exit(normal); + reader_exit(State, stop); {?MODULE, Pid, controlling_process, NewPid} -> - MRef = maps:get(ctrl_proc_mref, State), - erlang:demonitor(MRef, [flush]), - MRef = erlang:monitor(process, NewPid), + OldMRef = maps:get(ctrl_proc_mref, State), + erlang:demonitor(OldMRef, [flush]), + NewMRef = erlang:monitor(process, NewPid), Pid ! {?MODULE, self(), controlling_process}, reader_loop(State#{ctrl_proc => NewPid, - ctrl_proc_mref => MRef}); + ctrl_proc_mref => NewMRef}); {?MODULE, active, NewActive} -> reader_loop(State#{active => NewActive}); {'DOWN', MRef, process, Pid, Reason} -> case maps:get(ctrl_proc_mref, State) of - MRef when (Reason =:= normal) -> - exit(normal); - MRef -> - exit({controlling_process, Reason}); + MRef -> + reader_exit(State, {ctrl_exit, Reason}); _ -> reader_loop(State) end @@ -341,17 +435,86 @@ reader_loop(#{active := once, reader_loop(State) end; - {error, closed} -> + {error, closed} = E1 -> + Pid ! ?CLOSED_MSG(Sock, Method), + reader_exit(State, E1); + + {error, Reason} = E2 -> + Pid ! ?ERROR_MSG(Sock, Method, Reason), + reader_exit(State, E2) + end; +reader_loop(#{active := once, + async := true, + select_info := undefined, + sock := Sock, + method := Method, + ctrl_proc := Pid} = State) -> + case do_recv(Method, Sock, nowait) of + {select, SelectInfo} -> + reader_loop(State#{select_info => SelectInfo}); + {ok, Data} -> + Pid ! ?DATA_MSG(Sock, Method, Data), + reader_loop(State#{active => false}); + + {error, closed} = E1 -> Pid ! ?CLOSED_MSG(Sock, Method), - exit(normal); + reader_exit(State, E1); - {error, Reason} -> + {error, Reason} = E2 -> Pid ! ?ERROR_MSG(Sock, Method, Reason), - exit(Reason) + reader_exit(State, E2) + end; +reader_loop(#{active := once, + async := true, + select_info := {select_info, _, Ref}, + select_num := N, + sock := Sock, + method := Method, + ctrl_proc := Pid} = State) -> + receive + {?MODULE, stop} -> + reader_exit(State, stop); + + {?MODULE, Pid, controlling_process, NewPid} -> + OldMRef = maps:get(ctrl_proc_mref, State), + erlang:demonitor(OldMRef, [flush]), + NewMRef = erlang:monitor(process, NewPid), + Pid ! {?MODULE, self(), controlling_process}, + reader_loop(State#{ctrl_proc => NewPid, + ctrl_proc_mref => NewMRef}); + + {?MODULE, active, NewActive} -> + reader_loop(State#{active => NewActive}); + + {'DOWN', MRef, process, Pid, Reason} -> + case maps:get(ctrl_proc_mref, State) of + MRef -> + reader_exit(State, {ctrl_exit, Reason}); + _ -> + reader_loop(State) + end; + + {'$socket', Sock, select, Ref} -> + case do_recv(Method, Sock, nowait) of + {ok, Data} when is_binary(Data) -> + Pid ! ?DATA_MSG(Sock, Method, Data), + reader_loop(State#{active => false, + select_info => undefined, + select_num => N+1}); + + {error, closed} = E1 -> + Pid ! ?CLOSED_MSG(Sock, Method), + reader_exit(State, E1); + + {error, Reason} = E2 -> + Pid ! ?ERROR_MSG(Sock, Method, Reason), + reader_exit(State, E2) + end end; %% Read and forward data continuously reader_loop(#{active := true, + async := false, sock := Sock, method := Method, ctrl_proc := Pid} = State) -> @@ -362,25 +525,23 @@ reader_loop(#{active := true, {error, timeout} -> receive {?MODULE, stop} -> - exit(normal); + reader_exit(State, stop); {?MODULE, Pid, controlling_process, NewPid} -> - MRef = maps:get(ctrl_proc_mref, State), - erlang:demonitor(MRef, [flush]), - MRef = erlang:monitor(process, NewPid), + OldMRef = maps:get(ctrl_proc_mref, State), + erlang:demonitor(OldMRef, [flush]), + NewMRef = erlang:monitor(process, NewPid), Pid ! {?MODULE, self(), controlling_process}, reader_loop(State#{ctrl_proc => NewPid, - ctrl_proc_mref => MRef}); + ctrl_proc_mref => NewMRef}); {?MODULE, active, NewActive} -> reader_loop(State#{active => NewActive}); {'DOWN', MRef, process, Pid, Reason} -> case maps:get(ctrl_proc_mref, State) of - MRef when (Reason =:= normal) -> - exit(normal); MRef -> - exit({controlling_process, Reason}); + reader_exit(State, {ctrl_exit, Reason}); _ -> reader_loop(State) end @@ -388,27 +549,170 @@ reader_loop(#{active := true, reader_loop(State) end; - {error, closed} -> + {error, closed} = E1 -> Pid ! ?CLOSED_MSG(Sock, Method), - exit(normal); + reader_exit(State, E1); - {error, Reason} -> + {error, Reason} = E2 -> Pid ! ?ERROR_MSG(Sock, Method, Reason), - exit(Reason) + reader_exit(State, E2) + end; +reader_loop(#{active := true, + async := true, + select_info := undefined, + sock := Sock, + method := Method, + ctrl_proc := Pid} = State) -> + case do_recv(Method, Sock) of + {select, SelectInfo} -> + reader_loop(State#{select_info => SelectInfo}); + {ok, Data} -> + Pid ! ?DATA_MSG(Sock, Method, Data), + reader_loop(State); + + {error, closed} = E1 -> + Pid ! ?CLOSED_MSG(Sock, Method), + reader_exit(State, E1); + + {error, Reason} = E2 -> + Pid ! ?ERROR_MSG(Sock, Method, Reason), + reader_exit(State, E2) + end; +reader_loop(#{active := true, + async := true, + select_info := {select_info, _, Ref}, + select_num := N, + sock := Sock, + method := Method, + ctrl_proc := Pid} = State) -> + receive + {?MODULE, stop} -> + reader_exit(State, stop); + + {?MODULE, Pid, controlling_process, NewPid} -> + OldMRef = maps:get(ctrl_proc_mref, State), + erlang:demonitor(OldMRef, [flush]), + NewMRef = erlang:monitor(process, NewPid), + Pid ! {?MODULE, self(), controlling_process}, + reader_loop(State#{ctrl_proc => NewPid, + ctrl_proc_mref => NewMRef}); + + {?MODULE, active, NewActive} -> + reader_loop(State#{active => NewActive}); + + {'DOWN', MRef, process, Pid, Reason} -> + case maps:get(ctrl_proc_mref, State) of + MRef -> + reader_exit(State, {ctrl_exit, Reason}); + _ -> + reader_loop(State) + end; + + {'$socket', Sock, select, Ref} -> + case do_recv(Method, Sock, nowait) of + {ok, Data} when is_binary(Data) -> + Pid ! ?DATA_MSG(Sock, Method, Data), + reader_loop(State#{select_info => undefined, + select_num => N+1}); + + {error, closed} = E1 -> + Pid ! ?CLOSED_MSG(Sock, Method), + reader_exit(State, E1); + + {error, Reason} = E2 -> + Pid ! ?ERROR_MSG(Sock, Method, Reason), + reader_exit(State, E2) + end end. -do_recv(plain, Sock) -> - socket:recv(Sock, 0, ?READER_RECV_TIMEOUT); -do_recv(msg, Sock) -> - case socket:recvmsg(Sock, 0, 0, [], ?READER_RECV_TIMEOUT) of +do_recv(Method, Sock) -> + do_recv(Method, Sock, ?READER_RECV_TIMEOUT). + +do_recv(plain, Sock, Timeout) -> + socket:recv(Sock, 0, Timeout); +do_recv(msg, Sock, Timeout) -> + case socket:recvmsg(Sock, 0, 0, [], Timeout) of {ok, #{iov := [Bin]}} -> {ok, Bin}; + {select, _} = SELECT -> + SELECT; {error, _} = ERROR -> ERROR end. - - + + +reader_exit(#{async := false, active := Active}, stop) -> + vp("reader stopped when active: ~w", [Active]), + exit(normal); +reader_exit(#{async := true, + active := Active, + select_info := SelectInfo, + select_num := N}, stop) -> + vp("reader stopped when active: ~w" + "~n Current select info: ~p" + "~n Number of select messages: ~p", [Active, SelectInfo, N]), + exit(normal); +reader_exit(#{async := false, active := Active}, {ctrl_exit, normal}) -> + vp("reader ctrl exit when active: ~w", [Active]), + exit(normal); +reader_exit(#{async := true, + active := Active, + select_info := SelectInfo, + select_num := N}, {ctrl_exit, normal}) -> + vp("reader ctrl exit when active: ~w" + "~n Current select info: ~p" + "~n Number of select messages: ~p", [Active, SelectInfo, N]), + exit(normal); +reader_exit(#{async := false, active := Active}, {ctrl_exit, Reason}) -> + vp("reader exit when ctrl crash when active: ~w", [Active]), + exit({controlling_process, Reason}); +reader_exit(#{async := true, + active := Active, + select_info := SelectInfo, + select_num := N}, {ctrl_exit, Reason}) -> + vp("reader exit when ctrl crash when active: ~w" + "~n Current select info: ~p" + "~n Number of select messages: ~p", [Active, SelectInfo, N]), + exit({controlling_process, Reason}); +reader_exit(#{async := false, active := Active}, {error, closed}) -> + vp("reader exit when socket closed when active: ~w", [Active]), + exit(normal); +reader_exit(#{async := true, + active := Active, + select_info := SelectInfo, + select_num := N}, {error, closed}) -> + vp("reader exit when socket closed when active: ~w " + "~n Current select info: ~p" + "~n Number of select messages: ~p", [Active, SelectInfo, N]), + exit(normal); +reader_exit(#{async := false, active := Active}, {error, Reason}) -> + vp("reader exit when socket error when active: ~w", [Active]), + exit(Reason); +reader_exit(#{async := true, + active := Active, + select_info := SelectInfo, + select_num := N}, {error, Reason}) -> + vp("reader exit when socket error when active: ~w: " + "~n Current select info: ~p" + "~n Number of select messages: ~p", [Active, SelectInfo, N]), + exit(Reason). + + + + + %% ========================================================================== +vp(F, A) -> + vp(get(verbose), F, A). + +vp(true, F, A) -> + p(F, A); +vp(_, _, _) -> + ok. + +p(F, A) -> + io:format(F ++ "~n", A). + diff --git a/erts/emulator/test/system_info_SUITE.erl b/erts/emulator/test/system_info_SUITE.erl index 55b1162cfb..b48be3dd04 100644 --- a/erts/emulator/test/system_info_SUITE.erl +++ b/erts/emulator/test/system_info_SUITE.erl @@ -37,10 +37,13 @@ -export([process_count/1, system_version/1, misc_smoke_tests/1, heap_size/1, wordsize/1, memory/1, ets_limit/1, atom_limit/1, + procs_bug/1, ets_count/1, atom_count/1, system_logger/1]). -export([init/1, handle_event/2, handle_call/2]). +-export([init_per_testcase/2, end_per_testcase/2]). + suite() -> [{ct_hooks,[ts_install_cth]}, {timetrap, {minutes, 2}}]. @@ -48,8 +51,20 @@ suite() -> all() -> [process_count, system_version, misc_smoke_tests, ets_count, heap_size, wordsize, memory, ets_limit, atom_limit, atom_count, + procs_bug, system_logger]. + +init_per_testcase(procs_bug, Config) -> + procs_bug(init_per_testcase, Config); +init_per_testcase(_, Config) -> + Config. + +end_per_testcase(procs_bug, Config) -> + procs_bug(end_per_testcase, Config); +end_per_testcase(_, _) -> + ok. + %%% %%% The test cases ------------------------------------------------------------- %%% @@ -654,3 +669,41 @@ handle_call(Msg, State) -> handle_event(Event, State) -> State ! {report_handler, Event}, {ok, State}. + + +%% OTP-15909: Provoke bug that would cause VM crash +%% if doing system_info(procs) when process have queued exit/down signals. +procs_bug(init_per_testcase, Config) -> + %% Use single scheduler and process prio to starve monitoring processes + %% from handling their received DOWN signals. + OldSchedOnline = erlang:system_flag(schedulers_online,1), + [{schedulers_online, OldSchedOnline} | Config]; +procs_bug(end_per_testcase, Config) -> + erlang:system_flag(schedulers_online, + proplists:get_value(schedulers_online, Config)), + ok. + +procs_bug(Config) when is_list(Config) -> + {Monee,_} = spawn_opt(fun () -> receive die -> ok end end, + [monitor,{priority,max}]), + Papa = self(), + Pids = [begin + P = spawn_opt(fun () -> + erlang:monitor(process, Monee), + Papa ! {self(),ready}, + receive "nada" -> no end + end, + [link, {priority,normal}]), + {P, ready} = receive M -> M end, + P + end + || _ <- lists:seq(1,10)], + process_flag(priority,high), + Monee ! die, + {'DOWN',_,process,Monee,normal} = receive M -> M end, + + %% This call did crash VM as Pids have pending DOWN signals. + erlang:system_info(procs), + process_flag(priority,normal), + [begin unlink(P), exit(P, kill) end || P <- Pids], + ok. diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c index 8203c46a39..477a501876 100644 --- a/erts/etc/common/erlexec.c +++ b/erts/etc/common/erlexec.c @@ -255,7 +255,9 @@ static char* key_val_name = ERLANG_VERSION; /* Used by the registry * access functions. */ static char* boot_script = NULL; /* used by option -start_erl and -boot */ -static char* config_script = NULL; /* used by option -start_erl and -config */ +static char** config_scripts = NULL; /* used by option -start_erl and -config */ +static int config_script_cnt = 0; +static int got_start_erl = 0; static HANDLE this_module_handle; static int run_werl; @@ -392,6 +394,22 @@ add_extra_suffixes(char *prog) } #ifdef __WIN32__ +static void add_boot_config(void) +{ + int i; + if (boot_script) + add_args("-boot", boot_script, NULL); + for (i = 0; i < config_script_cnt; i++) { + add_args("-config", config_scripts[i], NULL); + } +} +# define ADD_BOOT_CONFIG add_boot_config() +#else +# define ADD_BOOT_CONFIG +#endif + + +#ifdef __WIN32__ __declspec(dllexport) int win_erlexec(int argc, char **argv, HANDLE module, int windowed) #else int main(int argc, char **argv) @@ -581,16 +599,6 @@ int main(int argc, char **argv) i = 1; -#ifdef __WIN32__ -#define ADD_BOOT_CONFIG \ - if (boot_script) \ - add_args("-boot", boot_script, NULL); \ - if (config_script) \ - add_args("-config", config_script, NULL); -#else -#define ADD_BOOT_CONFIG -#endif - get_home(); add_args("-home", home, NULL); @@ -610,7 +618,9 @@ int main(int argc, char **argv) case 'b': if (strcmp(argv[i], "-boot") == 0) { if (boot_script) - error("Conflicting -start_erl and -boot options"); + error("Conflicting -boot options"); + if (got_start_erl) + error("Conflicting -start_erl and -boot options"); if (i+1 >= argc) usage("-boot"); boot_script = strsave(argv[i+1]); @@ -634,11 +644,14 @@ int main(int argc, char **argv) } #ifdef __WIN32__ else if (strcmp(argv[i], "-config") == 0){ - if (config_script) + if (got_start_erl) error("Conflicting -start_erl and -config options"); if (i+1 >= argc) usage("-config"); - config_script = strsave(argv[i+1]); + config_script_cnt++; + config_scripts = erealloc(config_scripts, + config_script_cnt * sizeof(char*)); + config_scripts[config_script_cnt-1] = strsave(argv[i+1]); i++; } #endif @@ -1371,6 +1384,7 @@ strsave(char* string) static void get_start_erl_data(char *file) { + static char* a_config_script; int fp; char tmpbuffer[512]; char start_erl_data[512]; @@ -1381,7 +1395,7 @@ static void get_start_erl_data(char *file) char* tprogname; if (boot_script) error("Conflicting -start_erl and -boot options"); - if (config_script) + if (config_scripts) error("Conflicting -start_erl and -config options"); env = get_env("RELDIR"); if (env) @@ -1431,10 +1445,13 @@ static void get_start_erl_data(char *file) erts_snprintf(progname,strlen(tprogname) + 20,"%s -start_erl",tprogname); boot_script = emalloc(512); - config_script = emalloc(512); + a_config_script = emalloc(512); erts_snprintf(boot_script, 512, "%s/%s/start", reldir, otpstring); - erts_snprintf(config_script, 512, "%s/%s/sys", reldir, otpstring); + erts_snprintf(a_config_script, 512, "%s/%s/sys", reldir, otpstring); + config_scripts = &a_config_script; + config_script_cnt = 1; + got_start_erl = 1; } @@ -1673,9 +1690,9 @@ static char **build_args_from_string(char *string) for(;;) { switch (state) { case Start: - if (!*p) + if (!*p) goto done; - if (argc >= alloced - 1) { /* Make room for extra NULL */ + if (argc >= alloced - 2) { /* Make room for extra NULL and "--" */ argv = erealloc(argv, (alloced += 10) * sizeof(char *)); } cur_s = argc + argv; @@ -1764,11 +1781,14 @@ static char **build_args_from_string(char *string) } } done: - argv[argc] = NULL; /* Sure to be large enough */ if (!argc) { efree(argv); return NULL; } + argv[argc++] = "--"; /* Add a -- separator in order + for different from different environments + to effect each other */ + argv[argc++] = NULL; /* Sure to be large enough */ return argv; #undef ENSURE } @@ -2019,11 +2039,13 @@ initial_argv_massage(int *argc, char ***argv) argv_buf ab = {0}, xab = {0}; int ix, vix, ac; char **av; + char *sep = "--"; struct { int argc; char **argv; } avv[] = {{INT_MAX, NULL}, {INT_MAX, NULL}, {INT_MAX, NULL}, - {INT_MAX, NULL}, {INT_MAX, NULL}, {INT_MAX, NULL}}; + {INT_MAX, NULL}, {INT_MAX, NULL}, + {INT_MAX, NULL}, {INT_MAX, NULL}}; /* * The environment flag containing OTP release is intentionally * undocumented and intended for OTP internal use only. @@ -2043,6 +2065,8 @@ initial_argv_massage(int *argc, char ***argv) if (*argc > 1) { avv[vix].argc = *argc - 1; avv[vix++].argv = &(*argv)[1]; + avv[vix].argc = 1; + avv[vix++].argv = &sep; } av = build_args_from_env("ERL_FLAGS"); diff --git a/erts/etc/unix/cerl.src b/erts/etc/unix/cerl.src index 8cfc2d549e..59de9bdec8 100644 --- a/erts/etc/unix/cerl.src +++ b/erts/etc/unix/cerl.src @@ -292,25 +292,38 @@ if [ "x$GDB" = "x" ]; then valgrind_log="$log_file_prefix$VALGRIND_LOG_DIR/$VALGRIND_LOGFILE_PREFIX$VALGRIND_LOGFILE_INFIX$EMU_NAME.log" fi fi - if [ "x$VALGRIND_MISC_FLAGS" = "x" ]; then - valgrind_misc_flags= - else - valgrind_misc_flags="$VALGRIND_MISC_FLAGS" + # Add default flags + vgflags=$VALGRIND_MISC_FLAGS + if [ "x${vgflags#*--show-possibly-lost}" = "x$vgflags" ]; then + vgflags="$vgflags --show-possibly-lost=no" + fi + if [ "x${vgflags#*--child-silent-after-fork}" = "x$vgflags" ]; then + vgflags="$vgflags --child-silent-after-fork=yes" + fi + if [ "x${vgflags#*--suppressions}" = "x$vgflags" ]; then + if [ "x$ERL_TOP" != "x" ]; then + vgflags="$vgflags --suppressions=$ERL_TOP/erts/emulator/valgrind/suppress.standard" + else + echo "No valgrind suppression file found in \$VALGRIND_MISC_FLAGS and \$ERL_TOP not set." + fi fi if which taskset > /dev/null && test -e /proc/cpuinfo; then # We only let valgrind utilize one core with "taskset 1" as it can be very slow # on multiple cores (especially with async threads). Valgrind only run one pthread # at a time anyway so there is no point letting it utilize more than one core. # Use $sched_arg to force all schedulers online to emulate multicore. - taskset1="taskset 1" ncpu=`cat /proc/cpuinfo | grep -w processor | wc -l` + # Choose a random core in order to not collide with any other valgrind + # run on the same machine. + taskset1=$((1 << (`shuf -i 1-$ncpu -n 1` - 1) )) + taskset1="taskset $taskset1" sched_arg="-S$ncpu:$ncpu" else taskset1= sched_arg= fi - exec $taskset1 valgrind $valgrind_xml $valgrind_log $valgrind_misc_flags $BINDIR/$EMU_NAME $sched_arg $emu_xargs "$@" + exec $taskset1 valgrind $valgrind_xml $valgrind_log $vgflags $BINDIR/$EMU_NAME $sched_arg $emu_xargs "$@" elif [ $run_rr = yes ]; then if [ $1 = replay ]; then diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in index 66d6d20c4e..8b6abb5336 100644 --- a/erts/etc/unix/etp-commands.in +++ b/erts/etc/unix/etp-commands.in @@ -37,7 +37,7 @@ document etp-help % - GDB command toolbox for analyzing core dumps from the % Erlang emulator (BEAM). % -% Should work for 32-bit erts-5.2/R9B, ... +% Should work for 32-bit and 64-bit unix gdb % % The commands are prefixed with: % etp: Acronym for erts-term-print @@ -55,7 +55,8 @@ document etp-help % Special commands for not really terms: % etp-mfa, etp-cp, etp-disasm, % etp-msgq, etpf-msgq, -% etp-stacktrace, etp-stackdump, etpf-stackdump, etp-dictdump +% etp-stacktrace, etp-stacktrace-emu, etp-stackdump, etp-stackdump-emu, +% etpf-stackdump, etp-dictdump % etp-process-info, etp-process-memory-info % etp-port-info, etp-port-state, etp-port-sched-flags % etp-heapdump, etp-offheapdump, etpf-offheapdump, @@ -132,14 +133,14 @@ define etp-1 if (($arg0) & 0x3) == 1 # Cons pointer if $etp_flat - printf "<etpf-cons %#x>", ($arg0) + printf "<etpf-cons %p>", (($arg0)-1) else etp-list-1 ($arg0) ($arg1) end else if (($arg0) & 0x3) == 2 if $etp_flat - printf "<etpf-boxed %#x>", ($arg0) + printf "<etpf-boxed %p>", (($arg0)-2) else etp-boxed-1 ($arg0) ($arg1) end @@ -187,7 +188,7 @@ define etp-list-1 # Reentrant # if (($arg0) & 0x3) != 0x1 - printf "#NotCons<%#x>", ($arg0) + printf "#NotCons<%p>", ($arg0) else # Cons pointer if $etp_chart @@ -210,7 +211,7 @@ define etp-list-printable-1 # Returns: $etp_list_printable # if (($arg0) & 0x3) != 0x1 - printf "#NotCons<%#x>", ($arg0) + printf "#NotCons<%p>", ($arg0) else # Loop to check if it is a printable string set $etp_list_p = ($arg0) @@ -272,7 +273,7 @@ define etp-list-2 # Reentrant # if (($arg0) & 0x3) != 0x1 - printf "#NotCons<%#x>", ($arg0) + printf "#NotCons<%p>", ($arg0) else # Cons pointer if ($arg1) >= $etp_max_depth @@ -307,7 +308,7 @@ define etpf-cons # Reentrant capable # if ((Eterm)($arg0) & 0x3) != 0x1 - printf "#NotCons<%#x>", ($arg0) + printf "#NotCons<%p>", ($arg0) else # Cons pointer set $etp_flat = 1 @@ -336,13 +337,13 @@ define etp-boxed-1 # Reentrant # if (($arg0) & 0x3) != 0x2 - printf "#NotBoxed<%#x>", ($arg0) + printf "#NotBoxed<%p>", ($arg0) else if (((Eterm*)(($arg0) & ~0x3))[0] & 0x3) != 0x0 if $etp_chart etp-chart-entry-1 (($arg0)&~0x3) ($arg1) 1 end - printf "#BoxedError<%#x>", ($arg0) + printf "#BoxedError<%p>", ($arg0) else if $etp_chart etp-chart-entry-1 (($arg0)&~0x3) ($arg1) \ @@ -390,10 +391,10 @@ define etp-boxed-immediate-1 # Non-reentrant # if (($arg0) & 0x3) != 0x2 - printf "#NotBoxed<%#x>", ($arg0) + printf "#NotBoxed<%p>", ($arg0) else if (((Eterm*)(($arg0) & ~0x3))[0] & 0x3) != 0x0 - printf "#BoxedError<%#x>", ($arg0) + printf "#BoxedError<%p>", ($arg0) else set $etp_boxed_immediate_p = (Eterm*)(($arg0) & ~0x3) set $etp_boxed_immediate_h = ($etp_boxed_immediate_p[0] >> 2) & 0xF @@ -438,12 +439,12 @@ define etp-boxed-immediate-1 while $etp_boxed_immediate_arity > 0 set $etp_boxed_immediate_p++ if $etp_boxed_immediate_arity > 1 - printf "%#x,", *$etp_boxed_immediate_p + printf "%p,", *$etp_boxed_immediate_p else - printf "%#x", *$etp_boxed_immediate_p + printf "%p", *$etp_boxed_immediate_p if ($etp_boxed_immediate_h == 0xA) set $etp_boxed_immediate_p++ - printf ":%#x", *$etp_boxed_immediate_p + printf ":%p", *$etp_boxed_immediate_p end printf ">" end @@ -558,7 +559,7 @@ define etp-immediate-1 # Reentrant capable # if (($arg0) & 0x3) != 0x3 - printf "#NotImmediate<%#x>", ($arg0) + printf "#NotImmediate<%p>", ($arg0) else if (($arg0) & 0xF) == 0x3 etp-pid-1 ($arg0) @@ -580,7 +581,7 @@ define etp-immediate-1 if (($arg0) == $etp_nil) printf "[]" else - printf "#UnknownImmediate<%#x>", ($arg0) + printf "#UnknownImmediate<%p>", ($arg0) end end end @@ -598,7 +599,7 @@ define etp-atom-1 # Non-reentrant # if ((Eterm)($arg0) & 0x3f) != 0xb - printf "#NotAtom<%#x>", ($arg0) + printf "#NotAtom<%p>", ($arg0) else set $etp_atom_1_ap = (Atom*)erts_atom_table.seg_table[(Eterm)($arg0)>>16][((Eterm)($arg0)>>6)&0x3FF] set $etp_atom_1_i = ($etp_atom_1_ap)->len @@ -652,7 +653,7 @@ define etp-char-1 # Non-reentrant # if (($arg0) < 0) || (0377 < ($arg0)) - printf "#NotChar<%#x>", ($arg0) + printf "#NotChar<%p>", ($arg0) else if ($arg0) == ($arg1) printf "\\%c", ($arg0) @@ -787,7 +788,7 @@ define etp-pid-1 # Internal pid printf "<0.%u.%u>", $etp_pid_data & 0x7fff, ($etp_pid_data >> 15) & 0x1fff else - printf "#NotPid<%#x>", ($arg0) + printf "#NotPid<%p>", ($arg0) end end @@ -797,11 +798,11 @@ define etp-extpid-1 # Non-reentrant # if ((Eterm)($arg0) & 0x3) != 0x2 - printf "#NotBoxed<%#x>", (Eterm)($arg0) + printf "#NotBoxed<%p>", (Eterm)($arg0) else set $etp_extpid_1_p = (ExternalThing*)((Eterm)($arg0) & ~0x3) if ($etp_extpid_1_p->header & 0x3f) != 0x30 - printf "#NotExternalPid<%#x>", $etp_extpid_1_p->header + printf "#NotExternalPid<%p>", $etp_extpid_1_p->header else ## External pid set $etp_extpid_1_number = $etp_extpid_1_p->data.ui[0]&0x7fff @@ -812,7 +813,7 @@ define etp-extpid-1 set $etp_extpid_1_node = $etp_extpid_1_np->sysname if ($etp_extpid_1_node & 0x3f) != 0xb # Should be an atom - printf "#ExternalPidError<%#x>", ($arg0) + printf "#ExternalPidError<%p>", ($arg0) else if $etp_extpid_1_dep == erts_this_dist_entry printf "<0:" @@ -847,7 +848,7 @@ define etp-port-1 # Internal port printf "#Port<0.%u>", $etp_port_data else - printf "#NotPort<%#x>", ($arg0) + printf "#NotPort<%p>", ($arg0) end end @@ -857,11 +858,11 @@ define etp-extport-1 # Non-reentrant # if ((Eterm)($arg0) & 0x3) != 0x2 - printf "#NotBoxed<%#x>", (Eterm)($arg0) + printf "#NotBoxed<%p>", (Eterm)($arg0) else set $etp_extport_1_p = (ExternalThing*)((Eterm)($arg0) & ~0x3) if ($etp_extport_1_p->header & 0x3F) != 0x34 - printf "#NotExternalPort<%#x>", $etp_extport_1->header + printf "#NotExternalPort<%p>", $etp_extport_1->header else ## External port set $etp_extport_1_number = $etp_extport_1_p->data.ui[0]&0x3ffff @@ -871,7 +872,7 @@ define etp-extport-1 set $etp_extport_1_node = $etp_extport_1_np->sysname if ($etp_extport_1_node & 0x3f) != 0xb # Should be an atom - printf "#ExternalPortError<%#x>", ($arg0) + printf "#ExternalPortError<%p>", ($arg0) else if $etp_extport_1_dep == erts_this_dist_entry printf "#Port<0:" @@ -893,15 +894,15 @@ define etp-bignum-1 # Non-reentrant # if ((Eterm)($arg0) & 0x3) != 0x2 - printf "#NotBoxed<%#x>", (Eterm)($arg0) + printf "#NotBoxed<%p>", (Eterm)($arg0) else set $etp_bignum_1_p = (Eterm*)((Eterm)($arg0) & ~0x3) if ($etp_bignum_1_p[0] & 0x3b) != 0x08 - printf "#NotBignum<%#x>", $etp_bignum_1_p[0] + printf "#NotBignum<%p>", $etp_bignum_1_p[0] else set $etp_bignum_1_i = ($etp_bignum_1_p[0] >> 6) if $etp_bignum_1_i < 1 - printf "#BignumError<%#x>", (Eterm)($arg0) + printf "#BignumError<%p>", (Eterm)($arg0) else if $etp_bignum_1_p[0] & 0x04 printf "-" @@ -932,11 +933,11 @@ define etp-float-1 # Non-reentrant # if ((Eterm)($arg0) & 0x3) != 0x2 - printf "#NotBoxed<%#x>", (Eterm)($arg0) + printf "#NotBoxed<%p>", (Eterm)($arg0) else set $etp_float_1_p = (Eterm*)((Eterm)($arg0) & ~0x3) if ($etp_float_1_p[0] & 0x3f) != 0x18 - printf "#NotFloat<%#x>", $etp_float_1_p[0] + printf "#NotFloat<%p>", $etp_float_1_p[0] else printf "%f", *(double*)($etp_float_1_p+1) end @@ -951,14 +952,14 @@ define etp-ref-1 # Non-reentrant # if ((Eterm)($arg0) & 0x3) != 0x2 - printf "#NotBoxed<%#x>", (Eterm)($arg0) + printf "#NotBoxed<%p>", (Eterm)($arg0) else set $etp_ref_1_p = (ErtsORefThing *)((Eterm)($arg0) & ~0x3) if ($etp_ref_1_p->header & 0x3b) != 0x10 - printf "#NotRef<%#x>", $etp_ref_1_p->header + printf "#NotRef<%p>", $etp_ref_1_p->header else if $etp_ref_1_p->header != etp_ref_header && $etp_ref_1_p->header != etp_magic_ref_header - printf "#InternalRefError<%#x>", ($arg0) + printf "#InternalRefError<%p>", ($arg0) else set $etp_magic_ref = 0 set $etp_ref_1_i = 3 @@ -998,11 +999,11 @@ define etp-extref-1 # Non-reentrant # if ((Eterm)($arg0) & 0x3) != 0x2 - printf "#NotBoxed<%#x>", (Eterm)($arg0) + printf "#NotBoxed<%p>", (Eterm)($arg0) else set $etp_extref_1_p = (ExternalThing*)((Eterm)($arg0) & ~0x3) if ($etp_extref_1_p->header & 0x3F) != 0x38 - printf "#NotExternalRef<%#x>", $etp_extref_1->header + printf "#NotExternalRef<%p>", $etp_extref_1->header else ## External ref set $etp_extref_1_nump = (Uint32 *) 0 @@ -1041,7 +1042,7 @@ define etp-extref-1 end end if $etp_extref_1_error - printf "#ExternalRefError<%#x>", ($arg0) + printf "#ExternalRefError<%p>", ($arg0) else set $etp_extref_1_i-- while $etp_extref_1_i >= 0 @@ -1166,7 +1167,7 @@ define etp-cp-1 if *(Eterm*)($etp_cp) == beam_return_to_trace[0] printf "#Cp<return to trace>" else - printf "#Cp<%#x>", $etp_cp + printf "#Cp<%p>", $etp_cp end end end @@ -1452,30 +1453,74 @@ end define etp-stack-preamble set $etp_stack_p = ($arg0)->stop set $etp_stack_end = ($arg0)->hend + if ($arg0)->state.counter & 0x8000 + printf "%%%%%% WARNING: The process is currently running, so c_p->stop will not be correct\r\n" + printf "%%%%%% Consider using %s-emu instead\r\n", $arg1 + end printf "%% Stacktrace (%u)\n", $etp_stack_end-$etp_stack_p if ($arg0)->i != 0 - etp-1 ((Eterm)($arg0)->i) 0 - printf " (I)\n" + printf "I: " + etp ((Eterm)($arg0)->i) + end + if ($arg0)->cp != 0 + printf "cp:" + etp ((Eterm)($arg0)->cp) end +end + +define etp-stack-preamble-emu + set $etp_stack_p = E + set $etp_stack_end = ($arg0)->hend + printf "%% Stacktrace (%u)\n", $etp_stack_end-$etp_stack_p + printf "I: " + etp ((BeamInstr)I) if ($arg0)->cp != 0 - etp-1 ((Eterm)($arg0)->cp) 0 - printf " (cp)\n" + printf "cp: " + etp ((Eterm)($arg0)->cp) end end +define etp-stacktrace-1 + set $etp_stack_stop = (Eterm*)($arg0) + set $etp_stack_send = (Eterm*)($arg1) + set $etp_stack_cnt = 0 + while $etp_stack_stop < $etp_stack_send + if ($etp_stack_stop[0] & 0x3) == 0x0 + # Continuation pointer + printf "%d: ", $etp_stack_cnt + etp $etp_stack_stop[0] + end + set $etp_stack_stop++ + set $etp_stack_cnt++ + end +end + +define etp-stacktrace-emu +# Args: Process* +# +# Non-reentrant +# + etp-stack-preamble-emu ($arg0) + etp-stacktrace-1 $etp_stack_p $etp_stack_end +end + +document etp-stacktrace-emu +%--------------------------------------------------------------------------- +% etp-stacktrace-emu Process* +% +% Take an Process* and print a stactrace for the process. +% This macro assumes that the current frame is the process_main frame +% and that E is not optimized out. +%--------------------------------------------------------------------------- +end + define etp-stacktrace # Args: Process* # # Non-reentrant # - etp-stack-preamble ($arg0) - while $etp_stack_p < $etp_stack_end - if ($etp_stack_p[0] & 0x3) == 0x0 - # Continuation pointer - etp $etp_stack_p[0] - end - set $etp_stack_p++ - end + etp-stack-preamble ($arg0) "etp-stacktrace" + etp-stacktrace-1 $etp_stack_p $etp_stack_end end document etp-stacktrace @@ -1488,16 +1533,48 @@ document etp-stacktrace %--------------------------------------------------------------------------- end +define etp-stackdump-1 + # Args: Eterm *stop, Eterm *hend + # + # Non-reentrant + # + set $etp_stackdump_stop = (Eterm*)($arg0) + set $etp_stackdump_send = (Eterm*)($arg1) + set $etp_stackdump_cnt = 0 + while $etp_stackdump_stop < $etp_stackdump_send + printf "%d: ", $etp_stackdump_cnt + etp $etp_stackdump_stop[0] + set $etp_stackdump_stop++ + set $etp_stackdump_cnt++ + end +end + +define etp-stackdump-emu +# Args: Process* +# +# Non-reentrant +# + etp-stack-preamble-emu ($arg0) + etp-stackdump-1 $etp_stack_p $etp_stack_end +end + +document etp-stacktrace-emu +%--------------------------------------------------------------------------- +% etp-stacktrace-emu Process* +% +% Take an Process* and print a stactdump for the process. +% This macro assumes that the current frame is the process_main frame +% and that E is not optimized out. +%--------------------------------------------------------------------------- +end + define etp-stackdump # Args: Process* # # Non-reentrant # - etp-stack-preamble ($arg0) - while $etp_stack_p < $etp_stack_end - etp $etp_stack_p[0] - set $etp_stack_p++ - end + etp-stack-preamble ($arg0) "etp-stackdump" + etp-stackdump-1 $etp_stack_p $etp_stack_end end document etp-stackdump @@ -1743,7 +1820,7 @@ define etp-term-dump-pid # Internal pid printf "| <0.%04u.%03u> ", $etp_pid_data & 0x7fff, ($etp_pid_data >> 15) & 0x1fff else - printf "| #NotPid<%#x> ", ($arg0) + printf "| #NotPid<%p> ", ($arg0) end end @@ -2959,12 +3036,21 @@ define etp-fds end end +document etp-fds +%--------------------------------------------------------------------------- +% etp-fds +% +% Print the state of the fds currently in check_io. Only works in running systems. +%--------------------------------------------------------------------------- +end + define etp-disasm-1 - set $code_ptr = ((BeamInstr*)$arg0) - set $addr = *$code_ptr + set $code_ptr = ((BeamInstr*)($arg0)) + set $addr32 = (BeamInstr)(Uint32)*$code_ptr + set $addr64 = (BeamInstr)(Uint64)*$code_ptr set $i = 0 - while $i < (sizeof(opc) / sizeof(OpEntry)) - if $addr == beam_ops[$i] + while $i < num_instructions + if $addr32 == beam_ops[$i] || $addr64 == beam_ops[$i] printf "%s %d", opc[$i].name, opc[$i].sz set $next_i = $code_ptr + opc[$i].sz set $i += 4999 @@ -2974,6 +3060,11 @@ define etp-disasm-1 end define etp-disasm + if $argc == 1 + set $code_end = $arg0 + else + set $code_end = $arg1 + end etp-cp-func-info-1 $arg0 if $etp_cp_p == 0 printf "invalid argument" @@ -2982,7 +3073,7 @@ define etp-disasm printf ": " etp-disasm-1 $arg0 printf "\r\n" - while $next_i < ((BeamInstr*)$arg1) + while $next_i < ((BeamInstr*)$code_end) set $prev_i = $next_i etp-cp-func-info-1 $next_i etp-mfa-1 $etp_cp_p $cp_cp_p_offset @@ -2998,6 +3089,16 @@ define etp-disasm end end +document etp-disasm +%--------------------------------------------------------------------------- +% etp-fds BeamInstr* (BeamInstr*) +% +% Disassemble the instructions inbetween arg0 and arg1, +% if no second argument is given only the current +% instruction is printed. +%--------------------------------------------------------------------------- +end + ############################################################################ # # Timer Wheel @@ -3324,7 +3425,7 @@ define etp-offheapdump set $etp_offheapdump_p = $etp_offheapdump_p->next set $etp_offheapdump_i++ else - printf "#TaggedPtr<%#x>", $etp_offheapdump_p + printf "#TaggedPtr<%p>", $etp_offheapdump_p set $etp_offheapdump_p = 0 end end @@ -3363,12 +3464,12 @@ define etp-search-heaps # # Non-reentrant # - printf "%% Search all (<%u) process heaps for ", erts_max_processes + printf "%% Search all (<%u) process heaps for ", erts_proc.r.o.max set $etp_flat = 1 etp-1 ($arg0) 0 set $etp_flat = 0 printf ":...\n" - etp-search-heaps-1 ((Eterm*)((Eterm)($arg0)&~3)) + etp-search-heaps-1 ((Eterm*)((Eterm)($arg0)&~(Eterm)3)) end define etp-search-heaps-1 @@ -3376,8 +3477,8 @@ define etp-search-heaps-1 # # Non-reentrant # - set $etp_search_heaps_q = erts_max_processes / 10 - set $etp_search_heaps_r = erts_max_processes % 10 + set $etp_search_heaps_q = erts_proc.r.o.max / 10 + set $etp_search_heaps_r = erts_proc.r.o.max % 10 set $etp_search_heaps_t = 10 set $etp_search_heaps_m = $etp_search_heaps_q if $etp_search_heaps_r > 0 @@ -3387,7 +3488,7 @@ define etp-search-heaps-1 set $etp_search_heaps_i = 0 set $etp_search_heaps_found = 0 while $etp_search_heaps_i < erts_proc.r.o.max - set $proc = (Process *) *((UWord *) &erts_proc.r.o.tab[$proc_ix]) + set $proc = (Process *) *((UWord *) &erts_proc.r.o.tab[$etp_search_heaps_i]) if $proc if ($proc->heap <= ($arg0)) && \ (($arg0) < $proc->hend) @@ -3404,7 +3505,7 @@ define etp-search-heaps-1 while $etp_search_heaps_p && ($etp_search_heaps_cnt < $etp_max_depth) set $etp_search_heaps_cnt++ if (&($etp_search_heaps_p->mem) <= ($arg0)) && \ - (($arg0) < &($etp_search_heaps_p->mem)+$etp_search_heaps_p->size) + (($arg0) < &($etp_search_heaps_p->mem)+$etp_search_heaps_p->used_size) printf "process_tab[%d]->mbuf(%d)+%d\n", \ $etp_search_heaps_i, $etp_search_heaps_cnt, \ ($arg0)-&($etp_search_heaps_p->mem) @@ -3700,8 +3801,8 @@ define etp-overlapped-heaps # Non-reentrant # printf "%% Dumping heap addresses to \"etp-commands.bin\"\n" - set $etp_overlapped_heaps_q = erts_max_processes / 10 - set $etp_overlapped_heaps_r = erts_max_processes % 10 + set $etp_overlapped_heaps_q = erts_proc.r.o.max / 10 + set $etp_overlapped_heaps_r = erts_proc.r.o.max % 10 set $etp_overlapped_heaps_t = 10 set $etp_overlapped_heaps_m = $etp_overlapped_heaps_q if $etp_overlapped_heaps_r > 0 @@ -3727,7 +3828,7 @@ define etp-overlapped-heaps append binary value etp-commands.bin 'p' append binary value etp-commands.bin 's' append binary value etp-commands.bin '\0' - while $etp_overlapped_heaps_i < erts_max_processes + while $etp_overlapped_heaps_i < erts_proc.r.o.max if process_tab[$etp_overlapped_heaps_i] append binary value etp-commands.bin \ (Eterm)$etp_overlapped_heaps_i @@ -3868,7 +3969,7 @@ define etp-chart-entry-1 append binary value etp-commands.bin (Eterm)(($arg2)*sizeof(Eterm)) append binary value etp-commands.bin (Eterm)$etp_chart_id append binary value etp-commands.bin (Eterm)($arg1) -# printf "<dumped %#x %lu %lu %lu>", ($arg0)&~0x3, \ +# printf "<dumped %p %lu %lu %lu>", ($arg0)&~0x3, \ # (Eterm)(($arg2)*sizeof(Eterm)), (Eterm)$etp_chart_id, (Eterm)($arg1) end diff --git a/erts/etc/unix/gcov-gen-html b/erts/etc/unix/gcov-gen-html new file mode 100755 index 0000000000..3fd9f1ca49 --- /dev/null +++ b/erts/etc/unix/gcov-gen-html @@ -0,0 +1,62 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +@ARGV == 2 or die "Usage: gcov-gen-html \$ERL_TOP <output directory>\n"; + +my $srcdir = shift @ARGV; +my $outdir = shift @ARGV; + +my $verbose = 1; +my $flavor = "smp"; + +# setup filenames and paths, observe geninfos --base-directory +# it needs a correct path just after the $geninfo + +my $lcov_path = ""; #/usr/local/bin/"; + +my $geninfo = $lcov_path . "geninfo --no-checksum --base-directory"; +my $genhtml = $lcov_path . "genhtml"; + +# src paths + +my $emu_src_path = "$srcdir/erts/emulator"; +my $elib_src_path = "$srcdir/erts/lib_src"; +my $pcre_src_path = "$emu_src_path"; +my $zlib_src_path = "$emu_src_path"; + +# obj paths + +my $emu_obj_path = <$emu_src_path/obj/*-linux-*/gcov/$flavor>; +my $elib_obj_path = <$elib_src_path/obj/*-linux-*/gcov>; +my $pcre_obj_path = <$emu_src_path/pcre/obj/*-linux-*/gcov>; +my $zlib_obj_path = <$emu_src_path/zlib/obj/*-linux-*/gcov>; + +# info files + +my $emu_info = "$srcdir/emulator-cover.info"; +my $elib_info = "$srcdir/elib-cover.info"; +my $pcre_info = "$srcdir/pcre-cover.info"; + +my $infofiles = "$emu_info $elib_info $pcre_info"; + +run("$geninfo $emu_src_path -o $emu_info $emu_obj_path"); +run("$geninfo $elib_src_path -o $elib_info $elib_obj_path"); +run("$geninfo $pcre_src_path -o $pcre_info $pcre_obj_path"); + +if (<$zlib_obj_path/*.o>) { + my $zlib_info = "$srcdir/zlib-cover.info"; + $infofiles .= " $zlib_info"; + run("$geninfo $zlib_src_path -o $zlib_info $zlib_obj_path"); +} + +run("$genhtml -o $outdir $infofiles"); + + + +sub run { + my $cmd = shift; + print STDERR "\nrun($cmd)\n" if $verbose > 0; + system("$cmd 2>&1") == 0 or die "\nCan't run \"$cmd\": $?"; +} diff --git a/erts/etc/win32/msys_tools/vc/cc.sh b/erts/etc/win32/msys_tools/vc/cc.sh index 2b0482e876..71af14b9b2 100644 --- a/erts/etc/win32/msys_tools/vc/cc.sh +++ b/erts/etc/win32/msys_tools/vc/cc.sh @@ -195,7 +195,6 @@ mkdir $TMPOBJDIR # Compile for x in $SOURCES; do - start_time=`date '+%s'` # Compile each source if [ $LINKING = false ]; then # We should have an output defined, which is a directory @@ -260,16 +259,28 @@ for x in $SOURCES; do else tail -n +2 $ERR_FILE >&2 if test $DEPENDENCIES = true; then - if test `grep -v $x $MSG_FILE | grep -c '#line'` != "0"; then - o=`echo $x | sed 's,.*/,,' | sed 's,\.cp*$,.o,'` - echo -n $o':' -# cat $MSG_FILE | grep '#line' | grep -v $x | awk -F\" '{printf("%s\n",$2)}' | sort -u | grep -v " " | xargs -n 1 win2msys_path.sh | awk '{printf("\\\n %s ",$0)}' - cat $MSG_FILE | grep '#line' | grep -v $x | awk -F\" '{printf("%s\n",$2)}' | sort -u | grep -v " " | sed 's,^\([A-Za-z]\):[\\/]*,/\1/,;s,\\\\*,/,g'| awk '{printf("\\\n %s ",$0)}' - echo - echo - after_sed=`date '+%s'` - echo Made dependencies for $x':' `expr $after_sed '-' $start_time` 's' >&2 - fi + perl -e ' +my $file = "'$x'"; +while (<>) { + next unless /^#line/; + next if /$file/o; + (undef,$_) = split(/\"/); + next if / /; + $all{$_} = 1; +} +foreach (sort keys %all) { + s@^([A-Za-z]):@/$1@; + s@\\\\@/@g; + push @f, "\\\n $_ "; +} +if (@f) { + my $oname = $file; + $oname =~ s@.*/@@; + $oname =~ s@[.]cp*@.o@; + print $oname, ":", @f; + print "\n\n"; + print STDERR "Made dependencies for $file\n"; +}' $MSG_FILE else cat $MSG_FILE fi diff --git a/erts/preloaded/ebin/atomics.beam b/erts/preloaded/ebin/atomics.beam Binary files differindex ef402b5fee..c74ce3ce2d 100644 --- a/erts/preloaded/ebin/atomics.beam +++ b/erts/preloaded/ebin/atomics.beam diff --git a/erts/preloaded/ebin/counters.beam b/erts/preloaded/ebin/counters.beam Binary files differindex 674d0d27fa..2aec433bcb 100644 --- a/erts/preloaded/ebin/counters.beam +++ b/erts/preloaded/ebin/counters.beam diff --git a/erts/preloaded/ebin/erl_init.beam b/erts/preloaded/ebin/erl_init.beam Binary files differindex 0313988e3e..0c032e8e91 100644 --- a/erts/preloaded/ebin/erl_init.beam +++ b/erts/preloaded/ebin/erl_init.beam diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam Binary files differindex 661bcd8413..13fdd7908b 100644 --- a/erts/preloaded/ebin/erl_prim_loader.beam +++ b/erts/preloaded/ebin/erl_prim_loader.beam diff --git a/erts/preloaded/ebin/erl_tracer.beam b/erts/preloaded/ebin/erl_tracer.beam Binary files differindex ec4d6153d1..16e17b870b 100644 --- a/erts/preloaded/ebin/erl_tracer.beam +++ b/erts/preloaded/ebin/erl_tracer.beam diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam Binary files differindex 62dc8702e7..866f9df79f 100644 --- a/erts/preloaded/ebin/erlang.beam +++ b/erts/preloaded/ebin/erlang.beam diff --git a/erts/preloaded/ebin/erts_code_purger.beam b/erts/preloaded/ebin/erts_code_purger.beam Binary files differindex 669149df82..d81bc08282 100644 --- a/erts/preloaded/ebin/erts_code_purger.beam +++ b/erts/preloaded/ebin/erts_code_purger.beam diff --git a/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam b/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam Binary files differindex 6d3528c2dc..a8d492dfa5 100644 --- a/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam +++ b/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam Binary files differindex b3af713809..dd08111aad 100644 --- a/erts/preloaded/ebin/erts_internal.beam +++ b/erts/preloaded/ebin/erts_internal.beam diff --git a/erts/preloaded/ebin/erts_literal_area_collector.beam b/erts/preloaded/ebin/erts_literal_area_collector.beam Binary files differindex fc2bf6f6bd..0f7dd6efbe 100644 --- a/erts/preloaded/ebin/erts_literal_area_collector.beam +++ b/erts/preloaded/ebin/erts_literal_area_collector.beam diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam Binary files differindex 1d89174b25..942f29a11c 100644 --- a/erts/preloaded/ebin/init.beam +++ b/erts/preloaded/ebin/init.beam diff --git a/erts/preloaded/ebin/net.beam b/erts/preloaded/ebin/net.beam Binary files differindex f61b2b4a69..88d546a3af 100644 --- a/erts/preloaded/ebin/net.beam +++ b/erts/preloaded/ebin/net.beam diff --git a/erts/preloaded/ebin/persistent_term.beam b/erts/preloaded/ebin/persistent_term.beam Binary files differindex c882e4fad4..7871b64991 100644 --- a/erts/preloaded/ebin/persistent_term.beam +++ b/erts/preloaded/ebin/persistent_term.beam diff --git a/erts/preloaded/ebin/prim_buffer.beam b/erts/preloaded/ebin/prim_buffer.beam Binary files differindex cf671bf8f4..90d9596fe6 100644 --- a/erts/preloaded/ebin/prim_buffer.beam +++ b/erts/preloaded/ebin/prim_buffer.beam diff --git a/erts/preloaded/ebin/prim_eval.beam b/erts/preloaded/ebin/prim_eval.beam Binary files differindex 24911123f9..19935eeee3 100644 --- a/erts/preloaded/ebin/prim_eval.beam +++ b/erts/preloaded/ebin/prim_eval.beam diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam Binary files differindex 0efd954e50..a2c5f2f336 100644 --- a/erts/preloaded/ebin/prim_file.beam +++ b/erts/preloaded/ebin/prim_file.beam diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam Binary files differindex ff9268ad38..f67b660a08 100644 --- a/erts/preloaded/ebin/prim_inet.beam +++ b/erts/preloaded/ebin/prim_inet.beam diff --git a/erts/preloaded/ebin/prim_zip.beam b/erts/preloaded/ebin/prim_zip.beam Binary files differindex d319d7a343..bd51c3b271 100644 --- a/erts/preloaded/ebin/prim_zip.beam +++ b/erts/preloaded/ebin/prim_zip.beam diff --git a/erts/preloaded/ebin/socket.beam b/erts/preloaded/ebin/socket.beam Binary files differindex 558a886565..134b4eac13 100644 --- a/erts/preloaded/ebin/socket.beam +++ b/erts/preloaded/ebin/socket.beam diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam Binary files differindex 9610c94ac2..8b7c5fe2ef 100644 --- a/erts/preloaded/ebin/zlib.beam +++ b/erts/preloaded/ebin/zlib.beam diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index ac73946dc0..06f0ee1dc6 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -3361,7 +3361,7 @@ dist_ctrl_get_opt(_DHandle, _Opt) -> DHandle :: dist_handle(), InputPackets :: non_neg_integer(), OutputPackets :: non_neg_integer(), - PendingOutputPackets :: boolean(), + PendingOutputPackets :: non_neg_integer(), Res :: {'ok', InputPackets, OutputPackets, PendingOutputPackets}. dist_get_stat(_DHandle) -> diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl index d5abdd2483..374facb2a3 100644 --- a/erts/preloaded/src/prim_inet.erl +++ b/erts/preloaded/src/prim_inet.erl @@ -553,34 +553,49 @@ send(S, Data) -> %% "sendto" is for UDP. IP and Port are set by the caller to 0 if the socket %% is known to be connected. -sendto(S, Addr, _, Data) when is_port(S), tuple_size(Addr) =:= 2 -> - case type_value(set, addr, Addr) of - true -> - ?DBG_FORMAT("prim_inet:sendto(~p, ~p, ~p)~n", [S,Addr,Data]), - try - erlang:port_command(S, [enc_value(set, addr, Addr),Data]) - of - true -> - receive - {inet_reply,S,Reply} -> - ?DBG_FORMAT( - "prim_inet:sendto() -> ~p~n", [Reply]), - Reply - end - catch - error:_ -> - ?DBG_FORMAT( - "prim_inet:sendto() -> {error,einval}~n", []), - {error,einval} - end; - false -> - ?DBG_FORMAT( - "prim_inet:sendto() -> {error,einval}~n", []), - {error,einval} - end; -sendto(S, IP, Port, Data) -> - sendto(S, {IP, Port}, 0, Data). - +sendto(S, {_, _} = Address, AncOpts, Data) + when is_port(S), is_list(AncOpts) -> + case encode_opt_val(AncOpts) of + {ok, AncData} -> + AncDataLen = iolist_size(AncData), + case + type_value(set, addr, Address) andalso + type_value(set, uint32, AncDataLen) + of + true -> + ?DBG_FORMAT("prim_inet:sendto(~p, ~p, ~p, ~p)~n", + [S,Address,AncOpts,Data]), + PortCommandData = + [enc_value(set, addr, Address), + enc_value(set, uint32, AncDataLen), AncData, + Data], + try erlang:port_command(S, PortCommandData) of + true -> + receive + {inet_reply,S,Reply} -> + ?DBG_FORMAT( + "prim_inet:sendto() -> ~p~n", [Reply]), + Reply + end + catch + _:_ -> + ?DBG_FORMAT( + "prim_inet:sendto() -> {error,einval}~n", []), + {error,einval} + end; + false -> + ?DBG_FORMAT( + "prim_inet:sendto() -> {error,einval}~n", []), + {error,einval} + end; + {error,_} -> + ?DBG_FORMAT( + "prim_inet:sendto() -> {error,einval}~n", []), + {error,einval} + end; +sendto(S, IP, Port, Data) + when is_port(S), is_integer(Port) -> + sendto(S, {IP, Port}, [], Data). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% @@ -1993,15 +2008,15 @@ enc_value_2(addr, {File,_}) when is_list(File); is_binary(File) -> [?INET_AF_LOCAL,iolist_size(File)|File]; %% enc_value_2(addr, {inet,{any,Port}}) -> - [?INET_AF_INET,?int16(Port),0,0,0,0]; + [?INET_AF_INET,?int16(Port)|ip4_to_bytes({0,0,0,0})]; enc_value_2(addr, {inet,{loopback,Port}}) -> - [?INET_AF_INET,?int16(Port),127,0,0,1]; + [?INET_AF_INET,?int16(Port)|ip4_to_bytes({127,0,0,1})]; enc_value_2(addr, {inet,{IP,Port}}) -> [?INET_AF_INET,?int16(Port)|ip4_to_bytes(IP)]; enc_value_2(addr, {inet6,{any,Port}}) -> - [?INET_AF_INET6,?int16(Port),0,0,0,0,0,0,0,0]; + [?INET_AF_INET6,?int16(Port)|ip6_to_bytes({0,0,0,0,0,0,0,0})]; enc_value_2(addr, {inet6,{loopback,Port}}) -> - [?INET_AF_INET6,?int16(Port),0,0,0,0,0,0,0,1]; + [?INET_AF_INET6,?int16(Port)|ip6_to_bytes({0,0,0,0,0,0,0,1})]; enc_value_2(addr, {inet6,{IP,Port}}) -> [?INET_AF_INET6,?int16(Port)|ip6_to_bytes(IP)]; enc_value_2(addr, {local,Addr}) -> @@ -2149,10 +2164,10 @@ enum_name(_, []) -> false. %% encode opt/val REVERSED since options are stored in reverse order %% i.e. the recent options first (we must process old -> new) encode_opt_val(Opts) -> - try - enc_opt_val(Opts, []) + try + {ok, enc_opt_val(Opts, [])} catch - Reason -> {error,Reason} + throw:Reason -> {error,Reason} end. %% {active, once} and {active, N} are specially optimized because they will @@ -2171,17 +2186,21 @@ enc_opt_val([binary|Opts], Acc) -> enc_opt_val(Opts, Acc, mode, binary); enc_opt_val([list|Opts], Acc) -> enc_opt_val(Opts, Acc, mode, list); -enc_opt_val([_|_], _) -> {error,einval}; -enc_opt_val([], Acc) -> {ok,Acc}. +enc_opt_val([_|_], _) -> + throw(einval); +enc_opt_val([], Acc) -> + Acc. enc_opt_val(Opts, Acc, Opt, Val) when is_atom(Opt) -> Type = type_opt(set, Opt), case type_value(set, Type, Val) of true -> enc_opt_val(Opts, [enc_opt(Opt),enc_value(set, Type, Val)|Acc]); - false -> {error,einval} + false -> + throw(einval) end; -enc_opt_val(_, _, _, _) -> {error,einval}. +enc_opt_val(_, _, _, _) -> + throw(einval). diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 126db66cdd..ae1ffdb4ac 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -53,10 +53,16 @@ getopt/3, sockname/1, - peername/1 + peername/1, + + cancel/2 ]). -export_type([ + select_tag/0, + select_ref/0, + select_info/0, + domain/0, type/0, protocol/0, @@ -585,6 +591,17 @@ #{level := integer(), type := integer(), data := binary()}. +-opaque select_tag() :: atom(). +-opaque select_ref() :: reference(). + +-record(select_info, {tag :: select_tag(), ref :: select_ref()}). + +-type select_info() :: #select_info{}. + +-define(SELECT_INFO(T, R), #select_info{tag = T, ref = R}). +-define(SELECT(T, R), {select, ?SELECT_INFO(T, R)}). + + %% This is used in messages sent from the nif-code to erlang processes: %% %% {?SOCKET_TAG, Socket :: socket(), Tag :: atom(), Info :: term()} @@ -602,12 +619,13 @@ %% -define(SOCKET_TYPE_RDM, 4). -define(SOCKET_TYPE_SEQPACKET, 5). --define(SOCKET_PROTOCOL_IP, 1). --define(SOCKET_PROTOCOL_TCP, 2). --define(SOCKET_PROTOCOL_UDP, 3). --define(SOCKET_PROTOCOL_SCTP, 4). --define(SOCKET_PROTOCOL_ICMP, 5). --define(SOCKET_PROTOCOL_IGMP, 6). +-define(SOCKET_PROTOCOL_DEFAULT, 0). +-define(SOCKET_PROTOCOL_IP, 1). +-define(SOCKET_PROTOCOL_TCP, 2). +-define(SOCKET_PROTOCOL_UDP, 3). +-define(SOCKET_PROTOCOL_SCTP, 4). +-define(SOCKET_PROTOCOL_ICMP, 5). +-define(SOCKET_PROTOCOL_IGMP, 6). -define(SOCKET_LISTEN_BACKLOG_DEFAULT, 5). @@ -820,6 +838,7 @@ -define(SOCKET_SUPPORTS_OPTIONS, 16#0001). -define(SOCKET_SUPPORTS_SCTP, 16#0002). -define(SOCKET_SUPPORTS_IPV6, 16#0003). +-define(SOCKET_SUPPORTS_LOCAL, 16#0004). %% =========================================================================== @@ -875,18 +894,21 @@ info() -> -spec supports() -> [{options, supports_options()} | {sctp, boolean()} | - {ipv6, boolean()}]. + {ipv6, boolean()} | + {local, boolean()}]. supports() -> [{options, supports(options)}, {sctp, supports(sctp)}, - {ipv6, supports(ipv6)}]. + {ipv6, supports(ipv6)}, + {local, supports(local)}]. -dialyzer({nowarn_function, supports/1}). -spec supports(options) -> supports_options(); (sctp) -> boolean(); (ipv6) -> boolean(); + (local) -> boolean(); (Key1) -> false when Key1 :: term(). @@ -896,6 +918,8 @@ supports(sctp) -> nif_supports(?SOCKET_SUPPORTS_SCTP); supports(ipv6) -> nif_supports(?SOCKET_SUPPORTS_IPV6); +supports(local) -> + nif_supports(?SOCKET_SUPPORTS_LOCAL); supports(_Key1) -> false. @@ -1006,12 +1030,12 @@ supports(_Key1, _Key2, _Key3) -> Reason :: term(). open(Domain, Type) -> - open(Domain, Type, null). + open(Domain, Type, default). -spec open(Domain, Type, Protocol) -> {ok, Socket} | {error, Reason} when Domain :: domain(), Type :: type(), - Protocol :: null | protocol(), + Protocol :: default | protocol(), Socket :: socket(), Reason :: term(). @@ -1021,15 +1045,14 @@ open(Domain, Type, Protocol) -> -spec open(Domain, Type, Protocol, Extra) -> {ok, Socket} | {error, Reason} when Domain :: domain(), Type :: type(), - Protocol :: null | protocol(), + Protocol :: default | protocol(), Extra :: map(), Socket :: socket(), Reason :: term(). -open(Domain, Type, Protocol0, Extra) when is_map(Extra) -> +open(Domain, Type, Protocol, Extra) when is_map(Extra) -> try begin - Protocol = default_protocol(Protocol0, Type), EDomain = enc_domain(Domain), EType = enc_type(Domain, Type), EProtocol = enc_protocol(Type, Protocol), @@ -1052,15 +1075,6 @@ open(Domain, Type, Protocol0, Extra) when is_map(Extra) -> {error, Reason} end. -%% Note that this is just a convenience function for when the protocol was -%% not specified. If its actually specified, then that will be selected. -%% Also, this only works for the some of the type's (stream, dgram and -%% seqpacket). -default_protocol(null, stream) -> tcp; -default_protocol(null, dgram) -> udp; -default_protocol(null, seqpacket) -> sctp; -default_protocol(null, Type) -> throw({error, {no_default_protocol, Type}}); -default_protocol(Protocol, _) -> Protocol. %% =========================================================================== @@ -1192,18 +1206,24 @@ validate_inet6_addrs(Addrs) -> %% -spec connect(Socket, SockAddr) -> ok | {error, Reason} when - Socket :: socket(), - SockAddr :: sockaddr(), - Reason :: term(). + Socket :: socket(), + SockAddr :: sockaddr(), + Reason :: term(). connect(Socket, SockAddr) -> connect(Socket, SockAddr, infinity). --spec connect(Socket, SockAddr, Timeout) -> ok | {error, Reason} when - Socket :: socket(), - SockAddr :: sockaddr(), - Timeout :: timeout(), - Reason :: term(). +-spec connect(Socket, SockAddr, nowait) -> + ok | {select, SelectInfo} | {error, Reason} when + Socket :: socket(), + SockAddr :: sockaddr(), + SelectInfo :: select_info(), + Reason :: term() + ; (Socket, SockAddr, Timeout) -> ok | {error, Reason} when + Socket :: socket(), + SockAddr :: sockaddr(), + Timeout :: timeout(), + Reason :: term(). %% <KOLLA> %% Is it possible to connect with family = local for the (dest) sockaddr? @@ -1213,12 +1233,18 @@ connect(_Socket, _SockAddr, Timeout) {error, timeout}; connect(#socket{ref = SockRef}, #{family := Fam} = SockAddr, Timeout) when ((Fam =:= inet) orelse (Fam =:= inet6) orelse (Fam =:= local)) andalso - ((Timeout =:= infinity) orelse is_integer(Timeout)) -> + ((Timeout =:= nowait) orelse + (Timeout =:= infinity) orelse is_integer(Timeout)) -> TS = timestamp(Timeout), case nif_connect(SockRef, SockAddr) of ok -> %% Connected! ok; + + {ok, Ref} when (Timeout =:= nowait) -> + %% Connecting, but the caller does not want to wait... + ?SELECT(connect, Ref); + {ok, Ref} -> %% Connecting... NewTimeout = next_timeout(TS, Timeout), @@ -1271,17 +1297,27 @@ listen(#socket{ref = SockRef}, Backlog) accept(Socket) -> accept(Socket, ?SOCKET_ACCEPT_TIMEOUT_DEFAULT). --spec accept(LSocket, Timeout) -> {ok, Socket} | {error, Reason} when - LSocket :: socket(), - Timeout :: timeout(), - Socket :: socket(), - Reason :: term(). +-spec accept(LSocket, nowait) -> + {ok, Socket} | + {select, SelectInfo} | + {error, Reason} when + LSocket :: socket(), + Socket :: socket(), + SelectInfo :: select_info(), + Reason :: term() + ; (LSocket, Timeout) -> {ok, Socket} | {error, Reason} when + LSocket :: socket(), + Timeout :: timeout(), + Socket :: socket(), + Reason :: term(). %% Do we really need this optimization? accept(_, Timeout) when is_integer(Timeout) andalso (Timeout =< 0) -> {error, timeout}; accept(#socket{ref = LSockRef}, Timeout) - when is_integer(Timeout) orelse (Timeout =:= infinity) -> + when is_integer(Timeout) orelse + (Timeout =:= infinity) orelse + (Timeout =:= nowait) -> do_accept(LSockRef, Timeout). do_accept(LSockRef, Timeout) -> @@ -1292,6 +1328,11 @@ do_accept(LSockRef, Timeout) -> Socket = #socket{ref = SockRef}, {ok, Socket}; + + {error, eagain} when (Timeout =:= nowait) -> + ?SELECT(accept, AccRef); + + {error, eagain} -> %% Each call is non-blocking, but even then it takes %% *some* time, so just to be sure, recalculate before @@ -1330,33 +1371,56 @@ send(Socket, Data) -> send(Socket, Data, ?SOCKET_SEND_FLAGS_DEFAULT, ?SOCKET_SEND_TIMEOUT_DEFAULT). -spec send(Socket, Data, Flags) -> ok | {error, Reason} when - Socket :: socket(), - Data :: iodata(), - Flags :: send_flags(), - Reason :: term() + Socket :: socket(), + Data :: iodata(), + Flags :: send_flags(), + Reason :: term() + ; (Socket, Data, Timeout :: nowait) -> ok | + {select, SelectInfo} | + {ok, {RestData, SelectInfo}} | + {error, Reason} when + Socket :: socket(), + Data :: iodata(), + RestData :: binary(), + SelectInfo :: select_info(), + Reason :: term() ; (Socket, Data, Timeout) -> ok | {error, Reason} when - Socket :: socket(), - Data :: iodata(), - Timeout :: timeout(), - Reason :: term(). + Socket :: socket(), + Data :: iodata(), + Timeout :: timeout(), + Reason :: term(). send(Socket, Data, Flags) when is_list(Flags) -> send(Socket, Data, Flags, ?SOCKET_SEND_TIMEOUT_DEFAULT); send(Socket, Data, Timeout) -> send(Socket, Data, ?SOCKET_SEND_FLAGS_DEFAULT, Timeout). --spec send(Socket, Data, Flags, Timeout) -> ok | {error, Reason} when - Socket :: socket(), - Data :: iodata(), - Flags :: send_flags(), - Timeout :: timeout(), - Reason :: term(). +-spec send(Socket, Data, Flags, nowait) -> ok | + {select, SelectInfo} | + {ok, {RestData, SelectInfo}} | + {error, Reason} when + Socket :: socket(), + Data :: iodata(), + Flags :: send_flags(), + RestData :: binary(), + SelectInfo :: select_info(), + Reason :: term() + ; (Socket, Data, Flags, Timeout) -> ok | {error, Reason} when + Socket :: socket(), + Data :: iodata(), + Flags :: send_flags(), + Timeout :: timeout(), + Reason :: term(). send(Socket, Data, Flags, Timeout) when is_list(Data) -> Bin = erlang:list_to_binary(Data), send(Socket, Bin, Flags, Timeout); send(#socket{ref = SockRef}, Data, Flags, Timeout) - when is_binary(Data) andalso is_list(Flags) -> + when is_binary(Data) andalso + is_list(Flags) andalso + ((Timeout =:= nowait) orelse + (Timeout =:= infinity) orelse + (is_integer(Timeout) andalso (Timeout > 0))) -> EFlags = enc_send_flags(Flags), do_send(SockRef, Data, EFlags, Timeout). @@ -1366,6 +1430,15 @@ do_send(SockRef, Data, EFlags, Timeout) -> case nif_send(SockRef, SendRef, Data, EFlags) of ok -> ok; + + + {ok, Written} when (Timeout =:= nowait) -> + <<_:Written/binary, Rest/binary>> = Data, + %% We are partially done, but the user don't want to wait (here) + %% for completion + {ok, {Rest, ?SELECT_INFO(send, SendRef)}}; + + {ok, Written} -> NewTimeout = next_timeout(TS, Timeout), %% We are partially done, wait for continuation @@ -1387,6 +1460,12 @@ do_send(SockRef, Data, EFlags, Timeout) -> cancel(SockRef, send, SendRef), {error, {timeout, size(Data)}} end; + + + {error, eagain} when (Timeout =:= nowait) -> + ?SELECT(send, SendRef); + + {error, eagain} -> receive {?SOCKET_TAG, #socket{ref = SockRef}, select, SendRef} -> @@ -1422,17 +1501,25 @@ sendto(Socket, Data, Dest) -> sendto(Socket, Data, Dest, ?SOCKET_SENDTO_FLAGS_DEFAULT). -spec sendto(Socket, Data, Dest, Flags) -> ok | {error, Reason} when - Socket :: socket(), - Data :: binary(), - Dest :: null | sockaddr(), - Flags :: send_flags(), - Reason :: term() + Socket :: socket(), + Data :: binary(), + Dest :: null | sockaddr(), + Flags :: send_flags(), + Reason :: term() + ; (Socket, Data, Dest, Timeout :: nowait) -> ok | + {select, SelectInfo} | + {error, Reason} when + Socket :: socket(), + Data :: iodata(), + Dest :: null | sockaddr(), + SelectInfo :: select_info(), + Reason :: term() ; (Socket, Data, Dest, Timeout) -> ok | {error, Reason} when - Socket :: socket(), - Data :: iodata(), - Dest :: null | sockaddr(), - Timeout :: timeout(), - Reason :: term(). + Socket :: socket(), + Data :: iodata(), + Dest :: null | sockaddr(), + Timeout :: timeout(), + Reason :: term(). sendto(Socket, Data, Dest, Flags) when is_list(Flags) -> sendto(Socket, Data, Dest, Flags, ?SOCKET_SENDTO_TIMEOUT_DEFAULT); @@ -1440,13 +1527,22 @@ sendto(Socket, Data, Dest, Timeout) -> sendto(Socket, Data, Dest, ?SOCKET_SENDTO_FLAGS_DEFAULT, Timeout). --spec sendto(Socket, Data, Dest, Flags, Timeout) -> ok | {error, Reason} when - Socket :: socket(), - Data :: binary(), - Dest :: null | sockaddr(), - Flags :: send_flags(), - Timeout :: timeout(), - Reason :: term(). +-spec sendto(Socket, Data, Dest, Flags, nowait) -> ok | + {select, SelectInfo} | + {error, Reason} when + Socket :: socket(), + Data :: binary(), + Dest :: null | sockaddr(), + Flags :: send_flags(), + SelectInfo :: select_info(), + Reason :: term() + ; (Socket, Data, Dest, Flags, Timeout) -> ok | {error, Reason} when + Socket :: socket(), + Data :: binary(), + Dest :: null | sockaddr(), + Flags :: send_flags(), + Timeout :: timeout(), + Reason :: term(). sendto(Socket, Data, Dest, Flags, Timeout) when is_list(Data) -> Bin = erlang:list_to_binary(Data), @@ -1455,14 +1551,18 @@ sendto(#socket{ref = SockRef}, Data, Dest, Flags, Timeout) when is_binary(Data) andalso (Dest =:= null) andalso is_list(Flags) andalso - (is_integer(Timeout) orelse (Timeout =:= infinity)) -> + ((Timeout =:= nowait) orelse + (Timeout =:= infinity) orelse + (is_integer(Timeout) andalso (Timeout > 0))) -> EFlags = enc_send_flags(Flags), do_sendto(SockRef, Data, Dest, EFlags, Timeout); sendto(#socket{ref = SockRef}, Data, #{family := Fam} = Dest, Flags, Timeout) when is_binary(Data) andalso ((Fam =:= inet) orelse (Fam =:= inet6) orelse (Fam =:= local)) andalso is_list(Flags) andalso - (is_integer(Timeout) orelse (Timeout =:= infinity)) -> + ((Timeout =:= nowait) orelse + (Timeout =:= infinity) orelse + (is_integer(Timeout) andalso (Timeout > 0))) -> EFlags = enc_send_flags(Flags), do_sendto(SockRef, Data, Dest, EFlags, Timeout). @@ -1474,6 +1574,11 @@ do_sendto(SockRef, Data, Dest, EFlags, Timeout) -> %% We are done ok; + {ok, Written} when (Timeout =:= nowait) -> + <<_:Written/binary, Rest/binary>> = Data, + {ok, {Rest, ?SELECT_INFO(sendto, SendRef)}}; + + {ok, Written} -> %% We are partially done, wait for continuation receive @@ -1495,6 +1600,11 @@ do_sendto(SockRef, Data, Dest, EFlags, Timeout) -> {error, timeout} end; + + {error, eagain} when (Timeout =:= nowait) -> + ?SELECT(sendto, SendRef); + + {error, eagain} -> receive {?SOCKET_TAG, #socket{ref = SockRef}, select, SendRef} -> @@ -1538,11 +1648,18 @@ sendmsg(Socket, MsgHdr) -> MsgHdr :: msghdr(), Flags :: send_flags(), Reason :: term() + ; (Socket, MsgHdr, Timeout :: nowait) -> ok | + {select, SelectInfo} | + {error, Reason} when + Socket :: socket(), + MsgHdr :: msghdr(), + SelectInfo :: select_info(), + Reason :: term() ; (Socket, MsgHdr, Timeout) -> ok | {error, Reason} when - Socket :: socket(), - MsgHdr :: msghdr(), - Timeout :: timeout(), - Reason :: term(). + Socket :: socket(), + MsgHdr :: msghdr(), + Timeout :: timeout(), + Reason :: term(). sendmsg(Socket, MsgHdr, Flags) when is_list(Flags) -> sendmsg(Socket, MsgHdr, Flags, ?SOCKET_SENDMSG_TIMEOUT_DEFAULT); @@ -1551,19 +1668,34 @@ sendmsg(Socket, MsgHdr, Timeout) sendmsg(Socket, MsgHdr, ?SOCKET_SENDMSG_FLAGS_DEFAULT, Timeout). --spec sendmsg(Socket, MsgHdr, Flags, Timeout) -> - ok | {ok, Remaining} | {error, Reason} when - Socket :: socket(), - MsgHdr :: msghdr(), - Flags :: send_flags(), - Timeout :: timeout(), - Remaining :: erlang:iovec(), - Reason :: term(). +-spec sendmsg(Socket, MsgHdr, Flags, nowait) -> + ok | + {ok, Remaining} | + {select, SelectInfo} | + {error, Reason} when + Socket :: socket(), + MsgHdr :: msghdr(), + Flags :: send_flags(), + Remaining :: erlang:iovec(), + SelectInfo :: select_info(), + Reason :: term() + ; (Socket, MsgHdr, Flags, Timeout) -> + ok | + {ok, Remaining} | + {error, Reason} when + Socket :: socket(), + MsgHdr :: msghdr(), + Flags :: send_flags(), + Timeout :: timeout(), + Remaining :: erlang:iovec(), + Reason :: term(). sendmsg(#socket{ref = SockRef}, #{iov := IOV} = MsgHdr, Flags, Timeout) when is_list(IOV) andalso is_list(Flags) andalso - (is_integer(Timeout) orelse (Timeout =:= infinity)) -> + ((Timeout =:= nowait) orelse + (Timeout =:= infinity) orelse + (is_integer(Timeout) andalso (Timeout > 0))) -> try ensure_msghdr(MsgHdr) of M -> EFlags = enc_send_flags(Flags), @@ -1583,6 +1715,7 @@ do_sendmsg(SockRef, MsgHdr, EFlags, Timeout) -> %% We are done ok; + {ok, Written} when is_integer(Written) andalso (Written > 0) -> %% We should not retry here since the protocol may not %% be able to handle a message being split. Leave it to @@ -1594,6 +1727,11 @@ do_sendmsg(SockRef, MsgHdr, EFlags, Timeout) -> cancel(SockRef, sendmsg, SendRef), {ok, do_sendmsg_rest(maps:get(iov, MsgHdr), Written)}; + + {error, eagain} when (Timeout =:= nowait) -> + ?SELECT(sendmsg, SendRef); + + {error, eagain} -> receive {?SOCKET_TAG, #socket{ref = SockRef}, select, SendRef} -> @@ -1667,13 +1805,24 @@ recv(Socket, Length) -> ?SOCKET_RECV_FLAGS_DEFAULT, ?SOCKET_RECV_TIMEOUT_DEFAULT). --spec recv(Socket, Length, Flags) -> {ok, Data} | {error, Reason} when +-spec recv(Socket, Length, Flags) -> {ok, Data} | + {error, Reason} when Socket :: socket(), Length :: non_neg_integer(), Flags :: recv_flags(), Data :: binary(), Reason :: term() - ; (Socket, Length, Timeout) -> {ok, Data} | {error, Reason} when + ; (Socket, Length, Timeout :: nowait) -> {ok, Data} | + {select, SelectInfo} | + {ok, {Data, SelectInfo}} | + {error, Reason} when + Socket :: socket(), + Length :: non_neg_integer(), + Data :: binary(), + SelectInfo :: select_info(), + Reason :: term() + ; (Socket, Length, Timeout) -> {ok, Data} | + {error, Reason} when Socket :: socket(), Length :: non_neg_integer(), Timeout :: timeout(), @@ -1685,18 +1834,31 @@ recv(Socket, Length, Flags) when is_list(Flags) -> recv(Socket, Length, Timeout) -> recv(Socket, Length, ?SOCKET_RECV_FLAGS_DEFAULT, Timeout). --spec recv(Socket, Length, Flags, Timeout) -> {ok, Data} | {error, Reason} when - Socket :: socket(), - Length :: non_neg_integer(), - Flags :: recv_flags(), - Timeout :: timeout(), - Data :: binary(), - Reason :: term(). +-spec recv(Socket, Length, Flags, nowait) -> {ok, Data} | + {select, SelectInfo} | + {ok, {Data, SelectInfo}} | + {error, Reason} when + Socket :: socket(), + Length :: non_neg_integer(), + Flags :: recv_flags(), + Data :: binary(), + SelectInfo :: select_info(), + Reason :: term() + ; (Socket, Length, Flags, Timeout) -> {ok, Data} | + {error, Reason} when + Socket :: socket(), + Length :: non_neg_integer(), + Flags :: recv_flags(), + Timeout :: timeout(), + Data :: binary(), + Reason :: term(). recv(#socket{ref = SockRef}, Length, Flags, Timeout) when (is_integer(Length) andalso (Length >= 0)) andalso is_list(Flags) andalso - (is_integer(Timeout) orelse (Timeout =:= infinity)) -> + (is_integer(Timeout) orelse + (Timeout =:= infinity) orelse + (Timeout =:= nowait)) -> EFlags = enc_recv_flags(Flags), do_recv(SockRef, undefined, Length, EFlags, <<>>, Timeout). @@ -1704,8 +1866,12 @@ recv(#socket{ref = SockRef}, Length, Flags, Timeout) %% with Length = 0. This case makes it neccessary to have a timeout function %% clause since we may never wait for anything (no receive select), and so the %% the only timeout check will be the function clause. +%% Note that the Timeout value of 'nowait' has a special meaning. It means +%% that we will either return with data or with the with {error, NNNN}. In +%% wich case the caller will receive a select message at some later time. do_recv(SockRef, _OldRef, Length, EFlags, Acc, Timeout) - when (Timeout =:= infinity) orelse + when (Timeout =:= nowait) orelse + (Timeout =:= infinity) orelse (is_integer(Timeout) andalso (Timeout > 0)) -> TS = timestamp(Timeout), RecvRef = make_ref(), @@ -1726,6 +1892,15 @@ do_recv(SockRef, _OldRef, Length, EFlags, Acc, Timeout) <<Acc/binary, Bin/binary>>, next_timeout(TS, Timeout)); + + %% Did not get all the user asked for, but the user also + %% specified 'nowait', so deliver what we got and the + %% select info. + {ok, false = _Completed, Bin} when (Timeout =:= nowait) andalso + (size(Acc) =:= 0) -> + {ok, {Bin, ?SELECT_INFO(recv, RecvRef)}}; + + {ok, false = _Completed, Bin} when (size(Acc) =:= 0) -> %% We got the first chunk of it. %% We will be notified (select message) when there @@ -1764,6 +1939,17 @@ do_recv(SockRef, _OldRef, Length, EFlags, Acc, Timeout) {error, {timeout, Acc}} end; + + %% The user does not want to wait! + %% The user will be informed that there is something to read + %% via the select socket message (see below). + + {error, eagain} when (Timeout =:= nowait) andalso (size(Acc) =:= 0) -> + ?SELECT(recv, RecvRef); + {error, eagain} when (Timeout =:= nowait) -> + {ok, {Acc, ?SELECT_INFO(recv, RecvRef)}}; + + %% We return with the accumulated binary (if its non-empty) {error, eagain} when (Length =:= 0) andalso (size(Acc) > 0) -> %% CAN WE REALLY DO THIS? THE NIF HAS SELECTED!! OR? @@ -1854,30 +2040,51 @@ recvfrom(Socket, BufSz) -> ?SOCKET_RECV_FLAGS_DEFAULT, ?SOCKET_RECV_TIMEOUT_DEFAULT). --spec recvfrom(Socket, Flags, Timeout) -> - {ok, {Source, Data}} | {error, Reason} when - Socket :: socket(), - Flags :: recv_flags(), - Timeout :: timeout(), - Source :: sockaddr() | undefined, - Data :: binary(), - Reason :: term() +-spec recvfrom(Socket, Flags, nowait) -> + {ok, {Source, Data}} | + {select, SelectInfo} | + {error, Reason} when + Socket :: socket(), + Flags :: recv_flags(), + Source :: sockaddr() | undefined, + Data :: binary(), + SelectInfo :: select_info(), + Reason :: term() + ; (Socket, Flags, Timeout) -> + {ok, {Source, Data}} | + {error, Reason} when + Socket :: socket(), + Flags :: recv_flags(), + Timeout :: timeout(), + Source :: sockaddr() | undefined, + Data :: binary(), + Reason :: term() ; (Socket, BufSz, Flags) -> {ok, {Source, Data}} | {error, Reason} when - Socket :: socket(), - BufSz :: non_neg_integer(), - Flags :: recv_flags(), - Source :: sockaddr() | undefined, - Data :: binary(), - Reason :: term() + Socket :: socket(), + BufSz :: non_neg_integer(), + Flags :: recv_flags(), + Source :: sockaddr() | undefined, + Data :: binary(), + Reason :: term() + ; (Socket, BufSz, nowait) -> + {ok, {Source, Data}} | + {select, SelectInfo} | + {error, Reason} when + Socket :: socket(), + BufSz :: non_neg_integer(), + Source :: sockaddr() | undefined, + Data :: binary(), + SelectInfo :: select_info(), + Reason :: term() ; (Socket, BufSz, Timeout) -> {ok, {Source, Data}} | {error, Reason} when - Socket :: socket(), - BufSz :: non_neg_integer(), - Timeout :: timeout(), - Source :: sockaddr() | undefined, - Data :: binary(), - Reason :: term(). + Socket :: socket(), + BufSz :: non_neg_integer(), + Timeout :: timeout(), + Source :: sockaddr() | undefined, + Data :: binary(), + Reason :: term(). recvfrom(Socket, Flags, Timeout) when is_list(Flags) -> recvfrom(Socket, 0, Flags, Timeout); @@ -1886,20 +2093,34 @@ recvfrom(Socket, BufSz, Flags) when is_list(Flags) -> recvfrom(Socket, BufSz, Timeout) -> recvfrom(Socket, BufSz, ?SOCKET_RECV_FLAGS_DEFAULT, Timeout). --spec recvfrom(Socket, BufSz, Flags, Timeout) -> - {ok, {Source, Data}} | {error, Reason} when - Socket :: socket(), - BufSz :: non_neg_integer(), - Flags :: recv_flags(), - Timeout :: timeout(), - Source :: sockaddr() | undefined, - Data :: binary(), - Reason :: term(). +-spec recvfrom(Socket, BufSz, Flags, nowait) -> + {ok, {Source, Data}} | + {select, SelectInfo} | + {error, Reason} when + Socket :: socket(), + BufSz :: non_neg_integer(), + Flags :: recv_flags(), + Source :: sockaddr() | undefined, + Data :: binary(), + SelectInfo :: select_info(), + Reason :: term() + ; (Socket, BufSz, Flags, Timeout) -> + {ok, {Source, Data}} | + {error, Reason} when + Socket :: socket(), + BufSz :: non_neg_integer(), + Flags :: recv_flags(), + Timeout :: timeout(), + Source :: sockaddr() | undefined, + Data :: binary(), + Reason :: term(). recvfrom(#socket{ref = SockRef}, BufSz, Flags, Timeout) when (is_integer(BufSz) andalso (BufSz >= 0)) andalso is_list(Flags) andalso - (is_integer(Timeout) orelse (Timeout =:= infinity)) -> + (is_integer(Timeout) orelse + (Timeout =:= infinity) orelse + (Timeout =:= nowait)) -> EFlags = enc_recv_flags(Flags), do_recvfrom(SockRef, BufSz, EFlags, Timeout). @@ -1910,6 +2131,11 @@ do_recvfrom(SockRef, BufSz, EFlags, Timeout) -> {ok, {_Source, _NewData}} = OK -> OK; + + {error, eagain} when (Timeout =:= nowait) -> + ?SELECT(recvfrom, RecvRef); + + {error, eagain} -> %% There is nothing just now, but we will be notified when there %% is something to read (a select message). @@ -1950,29 +2176,44 @@ recvmsg(Socket) -> Flags :: recv_flags(), MsgHdr :: msghdr(), Reason :: term() + ; (Socket, Timeout :: nowait) -> {ok, MsgHdr} | + {select, SelectInfo} | + {error, Reason} when + Socket :: socket(), + MsgHdr :: msghdr(), + SelectInfo :: select_info(), + Reason :: term() ; (Socket, Timeout) -> {ok, MsgHdr} | {error, Reason} when - Socket :: socket(), - Timeout :: timeout(), - MsgHdr :: msghdr(), - Reason :: term(). + Socket :: socket(), + Timeout :: timeout(), + MsgHdr :: msghdr(), + Reason :: term(). recvmsg(Socket, Flags) when is_list(Flags) -> recvmsg(Socket, 0, 0, Flags, ?SOCKET_RECV_TIMEOUT_DEFAULT); recvmsg(Socket, Timeout) -> recvmsg(Socket, 0, 0, ?SOCKET_RECV_FLAGS_DEFAULT, Timeout). --spec recvmsg(Socket, Flags, Timeout) -> {ok, MsgHdr} | {error, Reason} when - Socket :: socket(), - Flags :: recv_flags(), - Timeout :: timeout(), - MsgHdr :: msghdr(), - Reason :: term() +-spec recvmsg(Socket, Flags, nowait) -> {ok, MsgHdr} | + {select, SelectInfo} | + {error, Reason} when + Socket :: socket(), + Flags :: recv_flags(), + MsgHdr :: msghdr(), + SelectInfo :: select_info(), + Reason :: term() + ; (Socket, Flags, Timeout) -> {ok, MsgHdr} | {error, Reason} when + Socket :: socket(), + Flags :: recv_flags(), + Timeout :: timeout(), + MsgHdr :: msghdr(), + Reason :: term() ; (Socket, BufSz, CtrlSz) -> {ok, MsgHdr} | {error, Reason} when - Socket :: socket(), - BufSz :: non_neg_integer(), - CtrlSz :: non_neg_integer(), - MsgHdr :: msghdr(), - Reason :: term(). + Socket :: socket(), + BufSz :: non_neg_integer(), + CtrlSz :: non_neg_integer(), + MsgHdr :: msghdr(), + Reason :: term(). recvmsg(Socket, Flags, Timeout) when is_list(Flags) -> recvmsg(Socket, 0, 0, Flags, Timeout); @@ -1983,20 +2224,34 @@ recvmsg(Socket, BufSz, CtrlSz) when is_integer(BufSz) andalso is_integer(CtrlSz) -spec recvmsg(Socket, BufSz, CtrlSz, - Flags, Timeout) -> {ok, MsgHdr} | {error, Reason} when - Socket :: socket(), - BufSz :: non_neg_integer(), - CtrlSz :: non_neg_integer(), - Flags :: recv_flags(), - Timeout :: timeout(), - MsgHdr :: msghdr(), - Reason :: term(). + Flags, nowait) -> {ok, MsgHdr} | + {select, SelectInfo} | + {error, Reason} when + Socket :: socket(), + BufSz :: non_neg_integer(), + CtrlSz :: non_neg_integer(), + Flags :: recv_flags(), + MsgHdr :: msghdr(), + SelectInfo :: select_info(), + Reason :: term() + ; (Socket, + BufSz, CtrlSz, + Flags, Timeout) -> {ok, MsgHdr} | {error, Reason} when + Socket :: socket(), + BufSz :: non_neg_integer(), + CtrlSz :: non_neg_integer(), + Flags :: recv_flags(), + Timeout :: timeout(), + MsgHdr :: msghdr(), + Reason :: term(). recvmsg(#socket{ref = SockRef}, BufSz, CtrlSz, Flags, Timeout) when (is_integer(BufSz) andalso (BufSz >= 0)) andalso (is_integer(CtrlSz) andalso (CtrlSz >= 0)) andalso is_list(Flags) andalso - (is_integer(Timeout) orelse (Timeout =:= infinity)) -> + (is_integer(Timeout) orelse + (Timeout =:= infinity) orelse + (Timeout =:= nowait)) -> EFlags = enc_recv_flags(Flags), do_recvmsg(SockRef, BufSz, CtrlSz, EFlags, Timeout). @@ -2007,6 +2262,11 @@ do_recvmsg(SockRef, BufSz, CtrlSz, EFlags, Timeout) -> {ok, _MsgHdr} = OK -> OK; + + {error, eagain} when (Timeout =:= nowait) -> + ?SELECT(recvmsg, RecvRef); + + {error, eagain} -> %% There is nothing just now, but we will be notified when there %% is something to read (a select message). @@ -2342,6 +2602,25 @@ peername(#socket{ref = SockRef}) -> nif_peername(SockRef). +%% =========================================================================== +%% +%% cancel - cancel an operation resulting in a select +%% +%% A call to accept, recv/recvfrom/recvmsg and send/sendto/sendmsg +%% can result in a select if they are called with the Timeout argument +%% set to nowait. This is indicated by the return of the select-info. +%% Such a operation can be cancelled by calling this function. +%% + +-spec cancel(Socket, SelectInfo) -> ok | {error, Reason} when + Socket :: socket(), + SelectInfo :: select_info(), + Reason :: term(). + +cancel(#socket{ref = SockRef}, #select_info{tag = Tag, ref = Ref}) -> + cancel(SockRef, Tag, Ref). + + %% =========================================================================== %% @@ -2355,7 +2634,7 @@ peername(#socket{ref = SockRef}) -> enc_domain(local) -> ?SOCKET_DOMAIN_LOCAL; enc_domain(inet) -> ?SOCKET_DOMAIN_INET; enc_domain(inet6) -> ?SOCKET_DOMAIN_INET6; -enc_domain(Domain) -> throw({error, {invalid_domain, Domain}}). +enc_domain(Domain) -> invalid_domain(Domain). -spec enc_type(Domain, Type) -> non_neg_integer() when Domain :: domain(), @@ -2366,22 +2645,23 @@ enc_type(_, stream) -> ?SOCKET_TYPE_STREAM; enc_type(_, dgram) -> ?SOCKET_TYPE_DGRAM; enc_type(_, raw) -> ?SOCKET_TYPE_RAW; enc_type(_, seqpacket) -> ?SOCKET_TYPE_SEQPACKET; -enc_type(_, Type) -> throw({error, {invalid_type, Type}}). +enc_type(_, Type) -> invalid_type(Type). -spec enc_protocol(Type, Protocol) -> non_neg_integer() | {raw, non_neg_integer()} when Type :: type(), Protocol :: protocol(). -enc_protocol(dgram, ip) -> ?SOCKET_PROTOCOL_IP; -enc_protocol(stream, tcp) -> ?SOCKET_PROTOCOL_TCP; -enc_protocol(dgram, udp) -> ?SOCKET_PROTOCOL_UDP; -enc_protocol(seqpacket, sctp) -> ?SOCKET_PROTOCOL_SCTP; -enc_protocol(raw, icmp) -> ?SOCKET_PROTOCOL_ICMP; -enc_protocol(raw, igmp) -> ?SOCKET_PROTOCOL_IGMP; +enc_protocol(_, default) -> ?SOCKET_PROTOCOL_DEFAULT; +enc_protocol(dgram, ip) -> ?SOCKET_PROTOCOL_IP; +enc_protocol(stream, tcp) -> ?SOCKET_PROTOCOL_TCP; +enc_protocol(dgram, udp) -> ?SOCKET_PROTOCOL_UDP; +enc_protocol(seqpacket, sctp) -> ?SOCKET_PROTOCOL_SCTP; +enc_protocol(raw, icmp) -> ?SOCKET_PROTOCOL_ICMP; +enc_protocol(raw, igmp) -> ?SOCKET_PROTOCOL_IGMP; enc_protocol(raw, {raw, P} = RAW) when is_integer(P) -> RAW; enc_protocol(Type, Proto) -> - throw({error, {invalid_protocol, {Type, Proto}}}). + invalid_protocol(Type, Proto). -spec enc_send_flags(Flags) -> non_neg_integer() when @@ -2532,7 +2812,7 @@ enc_setopt_value(otp, rcvbuf, V, _, _, _) when is_integer(V) andalso (V > 0) -> V; %% N: Number of reads (when specifying length = 0) %% V: Size of the "read" buffer -enc_setopt_value(otp, rcvbuf, {N, BufSz} = V, _, stream = _T, tcp = _P) +enc_setopt_value(otp, rcvbuf, {N, BufSz} = V, _, stream = _T, _P) when (is_integer(N) andalso (N > 0)) andalso (is_integer(BufSz) andalso (BufSz > 0)) -> V; @@ -3464,15 +3744,18 @@ flush_select_msgs(SockRef, Ref) -> %% A timestamp in ms +timestamp(nowait = T) -> + T; timestamp(infinity) -> undefined; timestamp(_) -> timestamp(). timestamp() -> - {A,B,C} = os:timestamp(), - A*1000000000+B*1000+(C div 1000). + erlang:monotonic_time(milli_seconds). +next_timeout(_, nowait = Timeout) -> + Timeout; next_timeout(_, infinity = Timeout) -> Timeout; next_timeout(TS, Timeout) -> @@ -3510,6 +3793,25 @@ tdiff(T1, T2) -> %% %% =========================================================================== +-spec invalid_domain(Domain) -> no_return() when + Domain :: term(). + +invalid_domain(Domain) -> + error({invalid_domain, Domain}). + +-spec invalid_type(Type) -> no_return() when + Type :: term(). + +invalid_type(Type) -> + error({invalid_type, Type}). + +-spec invalid_protocol(Type, Proto) -> no_return() when + Type :: term(), + Proto :: term(). + +invalid_protocol(Type, Proto) -> + error({invalid_protocol, {Type, Proto}}). + -spec not_supported(What) -> no_return() when What :: term(). diff --git a/erts/test/erlexec_SUITE.erl b/erts/test/erlexec_SUITE.erl index 602dc5ce2e..952e6da4dc 100644 --- a/erts/test/erlexec_SUITE.erl +++ b/erts/test/erlexec_SUITE.erl @@ -30,7 +30,7 @@ -export([all/0, suite/0, init_per_testcase/2, end_per_testcase/2]). -export([args_file/1, evil_args_file/1, env/1, args_file_env/1, - otp_7461/1, otp_7461_remote/1, otp_8209/1, + otp_7461/1, otp_7461_remote/1, argument_separation/1, zdbbl_dist_buf_busy_limit/1]). -include_lib("common_test/include/ct.hrl"). @@ -51,21 +51,28 @@ suite() -> all() -> [args_file, evil_args_file, env, args_file_env, - otp_7461, otp_8209, zdbbl_dist_buf_busy_limit]. + otp_7461, argument_separation, zdbbl_dist_buf_busy_limit]. %% Test that plain first argument does not -%% destroy -home switch [OTP-8209] -otp_8209(Config) when is_list(Config) -> +%% destroy -home switch [OTP-8209] or interact with environments +argument_separation(Config) when is_list(Config) -> {ok,[[PName]]} = init:get_argument(progname), SNameS = "erlexec_test_01", SName = list_to_atom(SNameS++"@"++ hd(tl(string:lexemes(atom_to_list(node()),"@")))), - Cmd = PName ++ " dummy_param -sname "++SNameS++" -setcookie "++ - atom_to_list(erlang:get_cookie()), - open_port({spawn,Cmd},[]), + Cmd = PName ++ " cmd_param -sname "++SNameS++" -setcookie "++ + atom_to_list(erlang:get_cookie()) ++ " -cmd_test", + open_port({spawn,Cmd},[{env,[{"ERL_AFLAGS","-atest"}, + {"ERL_FLAGS","env_param -test"}, + {"ERL_ZFLAGS","zenv_param"}]}]), pong = loop_ping(SName,40), + ct:log("emu_args: ~p",[rpc:call(SName,erlang,system_info,[emu_args])]), {ok,[[_]]} = rpc:call(SName,init,get_argument,[home]), - ["dummy_param"] = rpc:call(SName,init,get_plain_arguments,[]), + {ok,[[]]} = rpc:call(SName,init,get_argument,[atest]), + {ok,[[]]} = rpc:call(SName,init,get_argument,[cmd_test]), + {ok,[[]]} = rpc:call(SName,init,get_argument,[test]), + error = rpc:call(SName,init,get_argument,[unkown]), + ["cmd_param","env_param","zenv_param"] = rpc:call(SName,init,get_plain_arguments,[]), ok = cleanup_nodes(), ok. @@ -85,6 +92,7 @@ cleanup_node(SNameS,N) -> end. loop_ping(_,0) -> + flush(), pang; loop_ping(Node,N) -> case net_adm:ping(Node) of @@ -98,6 +106,14 @@ loop_ping(Node,N) -> pong end. +flush() -> + receive M -> + ct:pal("~p",[M]), + flush() + after 10 -> + ok + end. + args_file(Config) when is_list(Config) -> AFN1 = privfile("1", Config), AFN2 = privfile("2", Config), diff --git a/erts/vsn.mk b/erts/vsn.mk index 224570fb09..f06fd08540 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -18,7 +18,7 @@ # %CopyrightEnd% # -VSN = 10.4 +VSN = 10.4.4 # Port number 4365 in 4.2 # Port number 4366 in 4.3 diff --git a/lib/common_test/doc/src/notes.xml b/lib/common_test/doc/src/notes.xml index a64818da7b..c454608bbe 100644 --- a/lib/common_test/doc/src/notes.xml +++ b/lib/common_test/doc/src/notes.xml @@ -62,6 +62,37 @@ </section> +<section><title>Common_Test 1.17.2.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + If a ct hook is installed in the <c>suite/0</c> function + in a test suite, then the hook's <c>terminate/1</c> + function would be called several times without it's + <c>init/2</c> function being called first. This is now + corrected.</p> + <p> + Own Id: OTP-15863 Aux Id: ERIERL-370 </p> + </item> + <item> + <p> + If <c>init_per_testcase</c> fails, the test itself is + skipped. According to the documentation, it should be + possible to change the result to failed in a hook + function. The only available hook function in this case + is <c>post_init_per_testcase</c>, but changing the return + value there did not affect the test case result. This is + now corrected.</p> + <p> + Own Id: OTP-15869 Aux Id: ERIERL-350 </p> + </item> + </list> + </section> + +</section> + <section><title>Common_Test 1.17.2</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -239,6 +270,37 @@ </section> +<section><title>Common_Test 1.15.4.3</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + If a ct hook is installed in the <c>suite/0</c> function + in a test suite, then the hook's <c>terminate/1</c> + function would be called several times without it's + <c>init/2</c> function being called first. This is now + corrected.</p> + <p> + Own Id: OTP-15863 Aux Id: ERIERL-370 </p> + </item> + <item> + <p> + If <c>init_per_testcase</c> fails, the test itself is + skipped. According to the documentation, it should be + possible to change the result to failed in a hook + function. The only available hook function in this case + is <c>post_init_per_testcase</c>, but changing the return + value there did not affect the test case result. This is + now corrected.</p> + <p> + Own Id: OTP-15869 Aux Id: ERIERL-350 </p> + </item> + </list> + </section> + +</section> + <section><title>Common_Test 1.15.4.2</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/common_test/src/ct_hooks.erl b/lib/common_test/src/ct_hooks.erl index 97c349578f..94551d6815 100644 --- a/lib/common_test/src/ct_hooks.erl +++ b/lib/common_test/src/ct_hooks.erl @@ -363,7 +363,16 @@ terminate_if_scope_ends(HookId, Function0, Hooks) -> Function = strip_config(Function0), case lists:keyfind(HookId, #ct_hook_config.id, Hooks) of #ct_hook_config{ id = HookId, scope = Function} = Hook -> - terminate([Hook]), + case Function of + [AllOrGroup,_] when AllOrGroup=:=post_all; + AllOrGroup=:=post_groups -> + %% The scope only contains one function (post_all + %% or post_groups), and init has not been called, + %% so skip terminate as well. + ok; + _ -> + terminate([Hook]) + end, lists:keydelete(HookId, #ct_hook_config.id, Hooks); _ -> Hooks diff --git a/lib/common_test/src/ct_release_test.erl b/lib/common_test/src/ct_release_test.erl index ac3dcab7c9..839fb300c7 100644 --- a/lib/common_test/src/ct_release_test.erl +++ b/lib/common_test/src/ct_release_test.erl @@ -475,7 +475,7 @@ fetch_all_apps(Node) -> A = list_to_atom(filename:basename(filename:rootname(F))), _ = rpc:call(Node,application,load,[A]), case rpc:call(Node,application,get_key,[A,vsn]) of - {ok,V} -> [{A,V}]; + {ok,V} -> [{A,V,rpc:call(Node,code,lib_dir,[A])}]; _ -> [] end end, @@ -517,7 +517,7 @@ upgrade(Apps,Level,Callback,CreateDir,InstallDir,Config) -> target_system(Apps,CreateDir,InstallDir,{FromVsn,_,AllAppsVsns,Path}) -> RelName0 = "otp-"++FromVsn, - AppsVsns = [{A,V} || {A,V} <- AllAppsVsns, lists:member(A,Apps)], + AppsVsns = [{A,V,D} || {A,V,D} <- AllAppsVsns, lists:member(A,Apps)], {RelName,ErtsVsn} = create_relfile(AppsVsns,CreateDir,RelName0,FromVsn), %% Create .script and .boot @@ -636,8 +636,7 @@ do_upgrade({Cb,InitState},FromVsn,FromAppsVsns,ToRel,ToAppsVsns,InstallDir) -> {ok,Node} = start_node(Start,FromVsn,FromAppsVsns), ct:log("Node started: ~p",[Node]), - CtData = #ct_data{from = [{A,V,code:lib_dir(A)} || {A,V} <- FromAppsVsns], - to=[{A,V,code:lib_dir(A)} || {A,V} <- ToAppsVsns]}, + CtData = #ct_data{from = FromAppsVsns,to=ToAppsVsns}, State1 = do_callback(Node,Cb,upgrade_init,[CtData,InitState]), [{"OTP upgrade test",FromVsn,_,permanent}] = @@ -724,14 +723,14 @@ previous_major(Rel) -> integer_to_list(list_to_integer(Rel)-1). create_relfile(AppsVsns,CreateDir,RelName0,RelVsn) -> - UpgradeAppsVsns = [{A,V,restart_type(A)} || {A,V} <- AppsVsns], + UpgradeAppsVsns = [{A,V,restart_type(A)} || {A,V,_D} <- AppsVsns], CoreAppVsns0 = get_vsns([kernel,stdlib,sasl]), CoreAppVsns = - [{A,V,restart_type(A)} || {A,V} <- CoreAppVsns0, + [{A,V,restart_type(A)} || {A,V,_D} <- CoreAppVsns0, false == lists:keymember(A,1,AppsVsns)], - Apps = [App || {App,_} <- AppsVsns], + Apps = [App || {App,_,_} <- AppsVsns], StartDepsVsns = get_start_deps(Apps,CoreAppVsns), StartApps = [StartApp || {StartApp,_,_} <- StartDepsVsns] ++ Apps, @@ -744,7 +743,7 @@ create_relfile(AppsVsns,CreateDir,RelName0,RelVsn) -> %% processes of these applications will not be running. TestToolAppsVsns0 = get_vsns([common_test]), TestToolAppsVsns = - [{A,V,none} || {A,V} <- TestToolAppsVsns0, + [{A,V,none} || {A,V,_D} <- TestToolAppsVsns0, false == lists:keymember(A,1,AllAppsVsns0)], AllAppsVsns1 = AllAppsVsns0 ++ TestToolAppsVsns, @@ -766,7 +765,7 @@ get_vsns(Apps) -> [begin _ = application:load(A), {ok,V} = application:get_key(A,vsn), - {A,V} + {A,V,code:lib_dir(A)} end || A <- Apps]. get_start_deps([App|Apps],Acc) -> @@ -880,8 +879,9 @@ start_node(Start,ExpVsn,ExpAppsVsns) -> erlang:port_close(Port), wait_node_up(permanent,ExpVsn,ExpAppsVsns). -wait_node_up(ExpStatus,ExpVsn,ExpAppsVsns) -> +wait_node_up(ExpStatus,ExpVsn,ExpAppsVsns0) -> Node = node_name(?testnode), + ExpAppsVsns = [{A,V} || {A,V,_D} <- ExpAppsVsns0], wait_node_up(Node,ExpStatus,ExpVsn,lists:keysort(1,ExpAppsVsns),60). wait_node_up(Node,ExpStatus,ExpVsn,ExpAppsVsns,0) -> @@ -893,7 +893,7 @@ wait_node_up(Node,ExpStatus,ExpVsn,ExpAppsVsns,N) -> rpc:call(Node, application, which_applications, [])} of {[{_,ExpVsn,_,_}],Apps} when is_list(Apps) -> case [{A,V} || {A,_,V} <- lists:keysort(1,Apps), - lists:keymember(A,1,ExpAppsVsns)] of + lists:keymember(A,1,ExpAppsVsns)] of ExpAppsVsns -> {ok,Node}; _ -> diff --git a/lib/common_test/src/test_server.erl b/lib/common_test/src/test_server.erl index 756cd4d692..588396f101 100644 --- a/lib/common_test/src/test_server.erl +++ b/lib/common_test/src/test_server.erl @@ -1364,23 +1364,29 @@ do_end_tc_call(Mod, IPTC={init_per_testcase,Func}, Res, Return) -> {NOk,_} when NOk == auto_skip; NOk == fail; NOk == skip ; NOk == skipped -> {_,Args} = Res, - IPTCEndRes = + {NewConfig,IPTCEndRes} = case do_end_tc_call1(Mod, IPTC, Res, Return) of IPTCEndConfig when is_list(IPTCEndConfig) -> - IPTCEndConfig; + {IPTCEndConfig,IPTCEndConfig}; + {failed,RetReason} when Return=:={fail,RetReason} -> + %% Fail reason not changed by framework or hook + {Args,Return}; + {SF,_} = IPTCEndResult when SF=:=skip; SF=:=skipped; + SF=:=fail; SF=:=failed -> + {Args,IPTCEndResult}; _ -> - Args + {Args,Return} end, EPTCInitRes = case do_init_tc_call(Mod,{end_per_testcase_not_run,Func}, - IPTCEndRes,Return) of + NewConfig,IPTCEndRes) of {ok,EPTCInitConfig} when is_list(EPTCInitConfig) -> - {Return,EPTCInitConfig}; + {IPTCEndRes,EPTCInitConfig}; _ -> - {Return,IPTCEndRes} + {IPTCEndRes,NewConfig} end, do_end_tc_call1(Mod, {end_per_testcase_not_run,Func}, - EPTCInitRes, Return); + EPTCInitRes, IPTCEndRes); _Ok -> do_end_tc_call1(Mod, IPTC, Res, Return) end; diff --git a/lib/common_test/test/ct_hooks_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE.erl index 340b8f3d52..b87464f5e4 100644 --- a/lib/common_test/test/ct_hooks_SUITE.erl +++ b/lib/common_test/test/ct_hooks_SUITE.erl @@ -86,7 +86,7 @@ all(suite) -> scope_suite_state_cth, fail_pre_suite_cth, double_fail_pre_suite_cth, fail_post_suite_cth, skip_pre_suite_cth, skip_pre_end_cth, - skip_pre_init_tc_cth, + skip_pre_init_tc_cth, fail_post_init_tc_cth, skip_post_suite_cth, recover_post_suite_cth, update_config_cth, state_update_cth, update_result_cth, options_cth, same_id_cth, fail_n_skip_with_minimal_cth, prio_cth, no_config, @@ -206,6 +206,10 @@ skip_pre_init_tc_cth(Config) -> do_test(skip_pre_init_tc_cth, "ct_cth_empty_SUITE.erl", [skip_pre_init_tc_cth],Config). +fail_post_init_tc_cth(Config) -> + do_test(fail_post_init_tc_cth, "ct_fail_init_tc_SUITE.erl", + [fail_post_init_tc_cth],Config). + recover_post_suite_cth(Config) when is_list(Config) -> do_test(recover_post_suite_cth, "ct_cth_fail_per_suite_SUITE.erl", [recover_post_suite_cth],Config). @@ -671,9 +675,15 @@ test_events(scope_suite_cth) -> {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, %% check that post_groups and post_all comes before init when hook %% is installed in suite/0 + %% And there should be no terminate after these, since init is + %% not yet called. {?eh,cth,{'_',post_groups,['_',[]]}}, - {?eh,cth,{'_',post_all,['_','_',[]]}}, - {?eh,tc_start,{ct_scope_suite_cth_SUITE,init_per_suite}}, + {negative, + {?eh,cth,{'_',terminate,['_']}}, + {?eh,cth,{'_',post_all,['_','_',[]]}}}, + {negative, + {?eh,cth,{'_',terminate,['_']}}, + {?eh,tc_start,{ct_scope_suite_cth_SUITE,init_per_suite}}}, {?eh,cth,{'_',id,[[]]}}, {?eh,cth,{'_',init,['_',[]]}}, {?eh,cth,{'_',pre_init_per_suite,[ct_scope_suite_cth_SUITE,'$proplist',[]]}}, @@ -1036,6 +1046,44 @@ test_events(skip_pre_init_tc_cth) -> {?eh,stop_logging,[]} ]; +test_events(fail_post_init_tc_cth) -> + [ + {?eh,start_logging,{'DEF','RUNDIR'}}, + {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + {?eh,cth,{empty_cth,init,['_',[]]}}, + {?eh,start_info,{1,1,1}}, + {?eh,tc_start,{ct_fail_init_tc_SUITE,init_per_suite}}, + {?eh,cth,{empty_cth,pre_init_per_suite,[ct_fail_init_tc_SUITE,'$proplist',[]]}}, + {?eh,cth,{empty_cth,post_init_per_suite, + [ct_fail_init_tc_SUITE,'$proplist','$proplist',[]]}}, + {?eh,tc_done,{ct_fail_init_tc_SUITE,init_per_suite,ok}}, + {?eh,tc_start,{ct_fail_init_tc_SUITE,test_case}}, + {?eh,cth,{empty_cth,pre_init_per_testcase, + [ct_fail_init_tc_SUITE,test_case,'$proplist',[]]}}, + {?eh,cth,{empty_cth,post_init_per_testcase, + [ct_fail_init_tc_SUITE,test_case,'$proplist', + {skip, + {failed, + {ct_fail_init_tc_SUITE,init_per_testcase, + {{test_case_failed,"Failed in init_per_testcase"},'_'}}}}, + []]}}, + {?eh,tc_done,{ct_fail_init_tc_SUITE,test_case, + {failed,"Changed skip to fail in post_init_per_testcase"}}}, + {?eh,cth,{empty_cth,on_tc_fail, + [ct_fail_init_tc_SUITE,test_case, + "Changed skip to fail in post_init_per_testcase", + []]}}, + {?eh,test_stats,{0,1,{0,0}}}, + {?eh,tc_start,{ct_fail_init_tc_SUITE,end_per_suite}}, + {?eh,cth,{empty_cth,pre_end_per_suite,[ct_fail_init_tc_SUITE,'$proplist',[]]}}, + {?eh,cth,{empty_cth,post_end_per_suite, + [ct_fail_init_tc_SUITE,'$proplist',ok,[]]}}, + {?eh,tc_done,{ct_fail_init_tc_SUITE,end_per_suite,ok}}, + {?eh,test_done,{'DEF','STOP_TIME'}}, + {?eh,cth,{empty_cth,terminate,[[]]}}, + {?eh,stop_logging,[]} + ]; + test_events(recover_post_suite_cth) -> Suite = ct_cth_fail_per_suite_SUITE, [ diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_fail_init_tc_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_fail_init_tc_SUITE.erl new file mode 100644 index 0000000000..96ddfc5782 --- /dev/null +++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_fail_init_tc_SUITE.erl @@ -0,0 +1,49 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2010-2016. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +-module(ct_fail_init_tc_SUITE). + +-suite_defaults([{timetrap, {minutes, 10}}]). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +-include("ct.hrl"). + +%% Test server callback functions +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_testcase(TestCase, _Config) -> + ct:fail("Failed in init_per_testcase"). + +end_per_testcase(_TestCase, _Config) -> + ok. + +all() -> + [test_case]. + +%% Test cases starts here. +test_case(Config) when is_list(Config) -> + ok. + diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/fail_post_init_tc_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/fail_post_init_tc_cth.erl new file mode 100644 index 0000000000..ca9f05c40f --- /dev/null +++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/fail_post_init_tc_cth.erl @@ -0,0 +1,81 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2010-2017. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + + +-module(fail_post_init_tc_cth). + + +-include_lib("common_test/src/ct_util.hrl"). +-include_lib("common_test/include/ct_event.hrl"). + + +%% CT Hooks +-compile(export_all). + +init(Id, Opts) -> + empty_cth:init(Id, Opts). + +pre_init_per_suite(Suite, Config, State) -> + empty_cth:pre_init_per_suite(Suite,Config,State). + +post_init_per_suite(Suite,Config,Return,State) -> + empty_cth:post_init_per_suite(Suite,Config,Return,State). + +pre_end_per_suite(Suite,Config,State) -> + empty_cth:pre_end_per_suite(Suite,Config,State). + +post_end_per_suite(Suite,Config,Return,State) -> + empty_cth:post_end_per_suite(Suite,Config,Return,State). + +pre_init_per_group(Suite,Group,Config,State) -> + empty_cth:pre_init_per_group(Suite,Group,Config,State). + +post_init_per_group(Suite,Group,Config,Return,State) -> + empty_cth:post_init_per_group(Suite,Group,Config,Return,State). + +pre_end_per_group(Suite,Group,Config,State) -> + empty_cth:pre_end_per_group(Suite,Group,Config,State). + +post_end_per_group(Suite,Group,Config,Return,State) -> + empty_cth:post_end_per_group(Suite,Group,Config,Return,State). + +pre_init_per_testcase(Suite,TC,Config,State) -> + empty_cth:pre_init_per_testcase(Suite,TC,Config,State). + +post_init_per_testcase(Suite,TC,Config,{skip,_}=Return,State) -> + empty_cth:post_init_per_testcase(Suite,TC,Config,Return,State), + {{fail,"Changed skip to fail in post_init_per_testcase"},State}; +post_init_per_testcase(Suite,TC,Config,Return,State) -> + empty_cth:post_init_per_testcase(Suite,TC,Config,Return,State). + +pre_end_per_testcase(Suite,TC,Config,State) -> + empty_cth:pre_end_per_testcase(Suite,TC,Config,State). + +post_end_per_testcase(Suite,TC,Config,Return,State) -> + empty_cth:post_end_per_testcase(Suite,TC,Config,Return,State). + +on_tc_fail(Suite,TC, Reason, State) -> + empty_cth:on_tc_fail(Suite,TC,Reason,State). + +on_tc_skip(Suite,TC, Reason, State) -> + empty_cth:on_tc_skip(Suite,TC,Reason,State). + +terminate(State) -> + empty_cth:terminate(State). diff --git a/lib/common_test/test/ct_release_test_SUITE_data/release_test_SUITE.erl b/lib/common_test/test/ct_release_test_SUITE_data/release_test_SUITE.erl index 7f0ba65791..fe69ad0748 100644 --- a/lib/common_test/test/ct_release_test_SUITE_data/release_test_SUITE.erl +++ b/lib/common_test/test/ct_release_test_SUITE_data/release_test_SUITE.erl @@ -47,7 +47,9 @@ end_per_suite(_Config) -> init_per_testcase(major_fail_no_init, Config) -> Config; init_per_testcase(_Case, Config) -> - ct_release_test:init(Config). + Config1 = ct_release_test:init(Config), + ct:log("ct_release_test:init/1 returned:~n~p",[Config1]), + Config1. end_per_testcase(_Case, Config) -> ct_release_test:cleanup(Config). diff --git a/lib/compiler/doc/src/notes.xml b/lib/compiler/doc/src/notes.xml index 275c6268fa..f0d869381b 100644 --- a/lib/compiler/doc/src/notes.xml +++ b/lib/compiler/doc/src/notes.xml @@ -32,6 +32,83 @@ <p>This document describes the changes made to the Compiler application.</p> +<section><title>Compiler 7.4.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>Fixed an incorrect type determination for constructed + binaries, which could cause <c>is_binary</c> checks to + succeed when they shouldn't have.</p> + <p> + Own Id: OTP-15872</p> + </item> + </list> + </section> + +</section> + +<section><title>Compiler 7.4.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>The type optimization pass of the compiler could hang + or loop for a long time when analyzing a + <c>setelement/3</c> call with a varible position.</p> + <p> + Own Id: OTP-15828 Aux Id: ERL-948 </p> + </item> + <item> + <p>Certain complex receive statements would result in an + internal compiler failure.</p> + <p> + Own Id: OTP-15832 Aux Id: ERL-950 </p> + </item> + <item> + <p>Fixed an unsafe type optimization.</p> + <p> + Own Id: OTP-15838</p> + </item> + <item> + <p>Fixed a crash when optimizing compiler-generated + exceptions (like badmatch) whose offending term was a + constructed binary.</p> + <p> + Own Id: OTP-15839 Aux Id: ERL-954 </p> + </item> + <item> + <p>Fixed a bad optimization related to the <c>++/2</c> + operator, where the compiler assumed that it always + produced a list (<c>[] ++ RHS</c> returns <c>RHS</c> + verbatim, even if it's not a list).</p> + <p> + Own Id: OTP-15841</p> + </item> + <item> + <p>An <c>is_binary/1</c> test followed by + <c>is_bitstring/1</c> (or vice versa) could fail because + of an usafe optimization.</p> + <p> + Own Id: OTP-15845</p> + </item> + <item> + <p>A Core Erlang module where the last clause in a + <c>case</c> matched a map would fail to load.</p> + <p> + Own Id: OTP-15846 Aux Id: ERL-955 </p> + </item> + <item> + <p>Fixed a bug that could cause the compiler to crash + when compiling complex nested case expressions.</p> + <p> + Own Id: OTP-15848 Aux Id: ERL-956 </p> + </item> + </list> + </section> + +</section> + <section><title>Compiler 7.4</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/compiler/src/beam_except.erl b/lib/compiler/src/beam_except.erl index 28c89782c9..2305502800 100644 --- a/lib/compiler/src/beam_except.erl +++ b/lib/compiler/src/beam_except.erl @@ -140,8 +140,11 @@ fix_block_1([{set,[],[],{alloc,Live,{F1,F2,Needed0,F3}}}|Is], Words) -> [{set,[],[],{alloc,Live,{F1,F2,Needed,F3}}}|Is] end; fix_block_1([I|Is], Words) -> - [I|fix_block_1(Is, Words)]. - + [I|fix_block_1(Is, Words)]; +fix_block_1([], _Words) -> + %% Rare. The heap allocation was probably done by a binary + %% construction instruction. + []. dig_out_fc(Arity, Is0) -> Regs0 = maps:from_list([{{x,X},{arg,X}} || X <- seq(0, Arity-1)]), diff --git a/lib/compiler/src/beam_ssa.erl b/lib/compiler/src/beam_ssa.erl index a9977b0b1d..6492d1e1bf 100644 --- a/lib/compiler/src/beam_ssa.erl +++ b/lib/compiler/src/beam_ssa.erl @@ -96,7 +96,8 @@ %% To avoid the collapsing, change the value of SET_LIMIT to 50 in the %% file erl_types.erl in the hipe application. --type prim_op() :: 'bs_add' | 'bs_extract' | 'bs_init' | 'bs_init_writable' | +-type prim_op() :: 'bs_add' | 'bs_extract' | 'bs_get_tail' | + 'bs_init' | 'bs_init_writable' | 'bs_match' | 'bs_put' | 'bs_start_match' | 'bs_test_tail' | 'bs_utf16_size' | 'bs_utf8_size' | 'build_stacktrace' | 'call' | 'catch_end' | @@ -117,11 +118,12 @@ '+' | '-' | '*' | '/'. %% Primops only used internally during code generation. --type cg_prim_op() :: 'bs_get' | 'bs_match_string' | 'bs_restore' | 'bs_skip' | +-type cg_prim_op() :: 'bs_get' | 'bs_get_position' | 'bs_match_string' | + 'bs_restore' | 'bs_save' | 'bs_set_position' | 'bs_skip' | 'copy' | 'put_tuple_arity' | 'put_tuple_element' | - 'set_tuple_element'. + 'put_tuple_elements' | 'set_tuple_element'. --import(lists, [foldl/3,keyfind/3,mapfoldl/3,member/2,reverse/1]). +-import(lists, [foldl/3,keyfind/3,mapfoldl/3,member/2,reverse/1,umerge/1]). -spec add_anno(Key, Value, Construct) -> Construct when Key :: atom(), @@ -649,12 +651,18 @@ is_commutative('=/=') -> true; is_commutative('/=') -> true; is_commutative(_) -> false. -def_used_1([#b_blk{is=Is,last=Last}|Bs], Preds, Def0, Used0) -> - {Def,Used1} = def_used_is(Is, Preds, Def0, Used0), - Used = ordsets:union(used(Last), Used1), - def_used_1(Bs, Preds, Def, Used); -def_used_1([], _Preds, Def, Used) -> - {ordsets:from_list(Def),Used}. +def_used_1([#b_blk{is=Is,last=Last}|Bs], Preds, Def0, UsedAcc) -> + {Def,Used} = def_used_is(Is, Preds, Def0, used(Last)), + case Used of + [] -> + def_used_1(Bs, Preds, Def, UsedAcc); + [_|_] -> + def_used_1(Bs, Preds, Def, [Used|UsedAcc]) + end; +def_used_1([], _Preds, Def0, UsedAcc) -> + Def = ordsets:from_list(Def0), + Used = umerge(UsedAcc), + {Def,Used}. def_used_is([#b_set{op=phi,dst=Dst,args=Args}|Is], Preds, Def0, Used0) -> diff --git a/lib/compiler/src/beam_ssa_bsm.erl b/lib/compiler/src/beam_ssa_bsm.erl index 382e6f635e..1ac9e6a3bb 100644 --- a/lib/compiler/src/beam_ssa_bsm.erl +++ b/lib/compiler/src/beam_ssa_bsm.erl @@ -683,8 +683,12 @@ aca_copy_successors(Lbl0, Blocks0, Counter0) -> Lbl = maps:get(Lbl0, BRs), {Lbl, Blocks, Counter}. +aca_cs_build_brs([?BADARG_BLOCK=Lbl | Path], Counter, Acc) -> + %% ?BADARG_BLOCK is a marker and not an actual block, so renaming it will + %% break exception handling. + aca_cs_build_brs(Path, Counter, Acc#{ Lbl => Lbl }); aca_cs_build_brs([Lbl | Path], Counter0, Acc) -> - aca_cs_build_brs(Path, Counter0 + 1, maps:put(Lbl, Counter0, Acc)); + aca_cs_build_brs(Path, Counter0 + 1, Acc#{ Lbl => Counter0 }); aca_cs_build_brs([], Counter, Acc) -> {Acc, Counter}. diff --git a/lib/compiler/src/beam_ssa_codegen.erl b/lib/compiler/src/beam_ssa_codegen.erl index c2d5035b19..07f4c8b461 100644 --- a/lib/compiler/src/beam_ssa_codegen.erl +++ b/lib/compiler/src/beam_ssa_codegen.erl @@ -1016,6 +1016,14 @@ bif_fail({catch_tag,_}) -> {f,0}. next_block([]) -> none; next_block([{Next,_}|_]) -> Next. +%% Certain instructions (such as get_map_element or is_nonempty_list) +%% are only used in guards and **must** have a non-zero label; +%% otherwise, the loader will refuse to load the +%% module. ensure_label/2 replaces a zero label with the "ultimate +%% failure" label to make the module loadable. The instruction that +%% have had the zero label replaced is **not** supposed to ever fail +%% and actually jump to the label. + ensure_label(Fail0, #cg{ultimate_fail=Lbl}) -> case bif_fail(Fail0) of {f,0} -> {f,Lbl}; @@ -1160,6 +1168,11 @@ cg_block([#cg_set{op=call}=I, #cg_set{op=succeeded,dst=Bool}], {Bool,_Fail}, St) -> %% A call in try/catch block. cg_block([I], none, St); +cg_block([#cg_set{op=get_map_element,dst=Dst0,args=Args0}, + #cg_set{op=succeeded,dst=Bool}], {Bool,Fail0}, St) -> + [Dst,Map,Key] = beam_args([Dst0|Args0], St), + Fail = ensure_label(Fail0, St), + {[{get_map_elements,Fail,Map,{list,[Key,Dst]}}],St}; cg_block([#cg_set{op=Op,dst=Dst0,args=Args0}=I, #cg_set{op=succeeded,dst=Bool}], {Bool,Fail}, St) -> [Dst|Args] = beam_args([Dst0|Args0], St), @@ -1606,8 +1619,6 @@ cg_test({float,Op0}, Fail, Args, Dst, #cg_set{anno=Anno}) -> '/' -> fdiv end, [line(Anno),{bif,Op,Fail,Args,Dst}]; -cg_test(get_map_element, Fail, [Map,Key], Dst, _I) -> - [{get_map_elements,Fail,Map,{list,[Key,Dst]}}]; cg_test(peek_message, Fail, [], Dst, _I) -> [{loop_rec,Fail,{x,0}}|copy({x,0}, Dst)]; cg_test(put_map, Fail, [{atom,exact},SrcMap|Ss], Dst, Set) -> diff --git a/lib/compiler/src/beam_ssa_dead.erl b/lib/compiler/src/beam_ssa_dead.erl index bb43a550ae..64b9b3e222 100644 --- a/lib/compiler/src/beam_ssa_dead.erl +++ b/lib/compiler/src/beam_ssa_dead.erl @@ -436,8 +436,22 @@ get_phi_arg([{Val,From}|_], From) -> Val; get_phi_arg([_|As], From) -> get_phi_arg(As, From). eval_terminator(#b_br{bool=#b_var{}=Bool}=Br, Bs, _St) -> - Val = get_value(Bool, Bs), - beam_ssa:normalize(Br#b_br{bool=Val}); + case get_value(Bool, Bs) of + #b_literal{val=Val}=Lit -> + case is_boolean(Val) of + true -> + beam_ssa:normalize(Br#b_br{bool=Lit}); + false -> + %% Non-boolean literal. This means that this `br` + %% terminator will never actually be reached with + %% these bindings. (There must be a previous two-way + %% branch that branches the other way when Bool + %% is bound to a non-boolean literal.) + none + end; + #b_var{}=Var -> + beam_ssa:normalize(Br#b_br{bool=Var}) + end; eval_terminator(#b_br{bool=#b_literal{}}=Br, _Bs, _St) -> beam_ssa:normalize(Br); eval_terminator(#b_switch{arg=Arg,fail=Fail,list=List}=Sw, Bs, St) -> @@ -680,11 +694,8 @@ will_succeed_test(is_list, is_nonempty_list) -> maybe; will_succeed_test(is_nonempty_list, is_list) -> yes; -will_succeed_test(T1, T2) -> - case is_numeric_test(T1) andalso is_numeric_test(T2) of - true -> maybe; - false -> no - end. +will_succeed_test(_T1, _T2) -> + maybe. will_succeed_1('=:=', A, '<', B) -> if @@ -769,11 +780,6 @@ will_succeed_vars('==', Val1, '/=', Val2) when Val1 == Val2 -> no; will_succeed_vars(_, _, _, _) -> maybe. -is_numeric_test(is_float) -> true; -is_numeric_test(is_integer) -> true; -is_numeric_test(is_number) -> true; -is_numeric_test(_) -> false. - eval_type_test(Test, Arg) -> case eval_type_test_1(Test, Arg) of true -> yes; diff --git a/lib/compiler/src/beam_ssa_opt.erl b/lib/compiler/src/beam_ssa_opt.erl index 90c0d3cf16..229edc6a1d 100644 --- a/lib/compiler/src/beam_ssa_opt.erl +++ b/lib/compiler/src/beam_ssa_opt.erl @@ -904,8 +904,7 @@ cse_suitable(#b_set{}) -> false. }). ssa_opt_float({#st{ssa=Linear0,cnt=Count0}=St, FuncDb}) -> - NonGuards0 = float_non_guards(Linear0), - NonGuards = gb_sets:from_list(NonGuards0), + NonGuards = non_guards(Linear0), Blocks = maps:from_list(Linear0), Fs = #fs{non_guards=NonGuards,bs=Blocks}, {Linear,Count} = float_opt(Linear0, Count0, Fs), @@ -916,15 +915,6 @@ float_blk_is_in_guard(#b_blk{last=#b_br{fail=F}}, #fs{non_guards=NonGuards}) -> float_blk_is_in_guard(#b_blk{}, #fs{}) -> false. -float_non_guards([{L,#b_blk{is=Is}}|Bs]) -> - case Is of - [#b_set{op=landingpad}|_] -> - [L|float_non_guards(Bs)]; - _ -> - float_non_guards(Bs) - end; -float_non_guards([]) -> [?BADARG_BLOCK]. - float_opt([{L,Blk}|Bs0], Count0, Fs) -> case float_blk_is_in_guard(Blk, Fs) of true -> @@ -1774,35 +1764,44 @@ opt_bs_put_split_int_1(Int, L, R) -> %%% ssa_opt_tuple_size({#st{ssa=Linear0,cnt=Count0}=St, FuncDb}) -> - {Linear,Count} = opt_tup_size(Linear0, Count0, []), + %% This optimization is only safe in guards, as prefixing tuple_size with + %% an is_tuple check prevents it from throwing an exception. + NonGuards = non_guards(Linear0), + {Linear,Count} = opt_tup_size(Linear0, NonGuards, Count0, []), {St#st{ssa=Linear,cnt=Count}, FuncDb}. -opt_tup_size([{L,#b_blk{is=Is,last=Last}=Blk}|Bs], Count0, Acc0) -> +opt_tup_size([{L,#b_blk{is=Is,last=Last}=Blk}|Bs], NonGuards, Count0, Acc0) -> case {Is,Last} of {[#b_set{op={bif,'=:='},dst=Bool,args=[#b_var{}=Tup,#b_literal{val=Arity}]}], #b_br{bool=Bool}} when is_integer(Arity), Arity >= 0 -> - {Acc,Count} = opt_tup_size_1(Tup, L, Count0, Acc0), - opt_tup_size(Bs, Count, [{L,Blk}|Acc]); + {Acc,Count} = opt_tup_size_1(Tup, L, NonGuards, Count0, Acc0), + opt_tup_size(Bs, NonGuards, Count, [{L,Blk}|Acc]); {_,_} -> - opt_tup_size(Bs, Count0, [{L,Blk}|Acc0]) + opt_tup_size(Bs, NonGuards, Count0, [{L,Blk}|Acc0]) end; -opt_tup_size([], Count, Acc) -> +opt_tup_size([], _NonGuards, Count, Acc) -> {reverse(Acc),Count}. -opt_tup_size_1(Size, EqL, Count0, [{L,Blk0}|Acc]) -> - case Blk0 of - #b_blk{is=Is0,last=#b_br{bool=Bool,succ=EqL,fail=Fail}} -> - case opt_tup_size_is(Is0, Bool, Size, []) of - none -> +opt_tup_size_1(Size, EqL, NonGuards, Count0, [{L,Blk0}|Acc]) -> + #b_blk{is=Is0,last=Last} = Blk0, + case Last of + #b_br{bool=Bool,succ=EqL,fail=Fail} -> + case gb_sets:is_member(Fail, NonGuards) of + true -> {[{L,Blk0}|Acc],Count0}; - {PreIs,TupleSizeIs,Tuple} -> - opt_tup_size_2(PreIs, TupleSizeIs, L, EqL, - Tuple, Fail, Count0, Acc) + false -> + case opt_tup_size_is(Is0, Bool, Size, []) of + none -> + {[{L,Blk0}|Acc],Count0}; + {PreIs,TupleSizeIs,Tuple} -> + opt_tup_size_2(PreIs, TupleSizeIs, L, EqL, + Tuple, Fail, Count0, Acc) + end end; - #b_blk{} -> + _ -> {[{L,Blk0}|Acc],Count0} end; -opt_tup_size_1(_, _, Count, Acc) -> +opt_tup_size_1(_, _, _, Count, Acc) -> {Acc,Count}. opt_tup_size_2(PreIs, TupleSizeIs, PreL, EqL, Tuple, Fail, Count0, Acc) -> @@ -2241,6 +2240,19 @@ gcd(A, B) -> X -> gcd(B, X) end. +non_guards(Linear) -> + gb_sets:from_list(non_guards_1(Linear)). + +non_guards_1([{L,#b_blk{is=Is}}|Bs]) -> + case Is of + [#b_set{op=landingpad}|_] -> + [L | non_guards_1(Bs)]; + _ -> + non_guards_1(Bs) + end; +non_guards_1([]) -> + [?BADARG_BLOCK]. + rel2fam(S0) -> S1 = sofs:relation(S0), S = sofs:rel2fam(S1), diff --git a/lib/compiler/src/beam_ssa_pre_codegen.erl b/lib/compiler/src/beam_ssa_pre_codegen.erl index bf99e8fc26..9af72afca7 100644 --- a/lib/compiler/src/beam_ssa_pre_codegen.erl +++ b/lib/compiler/src/beam_ssa_pre_codegen.erl @@ -1415,12 +1415,15 @@ fix_receive([], _Defs, Blocks, Count) -> find_loop_exit([L1,L2|_Ls], Blocks) -> Path1 = beam_ssa:rpo([L1], Blocks), Path2 = beam_ssa:rpo([L2], Blocks), - find_loop_exit_1(reverse(Path1), reverse(Path2), none); + find_loop_exit_1(Path1, cerl_sets:from_list(Path2)); find_loop_exit(_, _) -> none. -find_loop_exit_1([H|T1], [H|T2], _) -> - find_loop_exit_1(T1, T2, H); -find_loop_exit_1(_, _, Exit) -> Exit. +find_loop_exit_1([H|T], OtherPath) -> + case cerl_sets:is_element(H, OtherPath) of + true -> H; + false -> find_loop_exit_1(T, OtherPath) + end; +find_loop_exit_1([], _) -> none. %% find_rm_blocks(StartLabel, Blocks) -> [Label]. %% Find all blocks that start with remove_message within the receive diff --git a/lib/compiler/src/beam_ssa_type.erl b/lib/compiler/src/beam_ssa_type.erl index 06b42f1928..68920e7dd3 100644 --- a/lib/compiler/src/beam_ssa_type.erl +++ b/lib/compiler/src/beam_ssa_type.erl @@ -23,8 +23,8 @@ -include("beam_ssa_opt.hrl"). -import(lists, [all/2,any/2,droplast/1,foldl/3,last/1,member/2, - keyfind/3,partition/2,reverse/1,reverse/2, - seq/2,sort/1,split/2]). + keyfind/3,reverse/1,reverse/2, + sort/1,split/2]). -define(UNICODE_INT, #t_integer{elements={0,16#10FFFF}}). @@ -840,15 +840,8 @@ type({bif,Bif}, Args, Ts, _Ds) -> Type -> Type end; -type(bs_init, [#b_literal{val=Type}|Args], _Ts, _Ds) -> - case {Type,Args} of - {new,[_,#b_literal{val=Unit}]} -> - {binary,Unit}; - {append,[_,_,#b_literal{val=Unit}]} -> - {binary,Unit}; - {private_append,[_,_,#b_literal{val=Unit}]} -> - {binary,Unit} - end; +type(bs_init, _Args, _Ts, _Ds) -> + {binary, 1}; type(bs_extract, [Ctx], Ts, _Ds) -> #t_bs_match{type=Type} = get_type(Ctx, Ts), Type; @@ -874,11 +867,11 @@ type(call, [#b_remote{mod=#b_literal{val=Mod}, true -> none end; - {#t_integer{elements={Min,Max}}, + {#t_integer{elements={Min,_}}=IntType, #t_tuple{elements=Es0,size=Size}=T} -> - %% We know this will land between Min and Max, so kill the - %% types for those indexes. - Es = maps:without(seq(Min, Max), Es0), + %% Remove type information for all indices that + %% falls into the range of the integer. + Es = remove_element_info(IntType, Es0), case T#t_tuple.exact of false -> T#t_tuple{elements=Es,size=max(Min, Size)}; @@ -896,11 +889,15 @@ type(call, [#b_remote{mod=#b_literal{val=Mod}, {_,_} -> #t_tuple{} end; - {erlang,'++',[List1,List2]} -> - case get_type(List1, Ts) =:= cons orelse - get_type(List2, Ts) =:= cons of - true -> cons; - false -> list + {erlang,'++',[LHS,RHS]} -> + LType = get_type(LHS, Ts), + RType = get_type(RHS, Ts), + case LType =:= cons orelse RType =:= cons of + true -> + cons; + false -> + %% `[] ++ RHS` yields RHS, even if RHS is not a list. + join(list, RType) end; {erlang,'--',[_,_]} -> list; @@ -1388,24 +1385,11 @@ get_type(#b_literal{val=Val}, _Ts) -> %% type for L. For example, if L was known to be 'list', subtracting %% 'cons' would give 'nil' as the only possible type. The result of the %% subtraction for L will be added to FailTypes. -%% -%% Here is another example, asking about the variable Bool: -%% -%% Head = bif:hd L -%% Bool = succeeded Head -%% -%% 'succeeded Head' will evaluate to 'true' if the instrution that -%% defined Head succeeded. In this case, it is the 'bif:hd L' -%% instruction, which will succeed if L is 'cons'. Thus, the meet of -%% the previous type for L and 'cons' will be added to SuccTypes. -%% -%% If 'succeeded Head' evaluates to 'false', it means that 'bif:hd L' -%% failed and that L is not 'cons'. 'cons' can be subtracted from the -%% previously known type for L and the result put in FailTypes. infer_types_br(#b_var{}=V, Ts, #d{ds=Ds}) -> #{V:=#b_set{op=Op,args=Args}} = Ds, - Types0 = infer_type(Op, Args, Ds), + PosTypes0 = infer_type(Op, Args, Ds), + NegTypes0 = infer_type_negative(Op, Args, Ds), %% We must be careful with types inferred from '=:='. %% @@ -1416,13 +1400,17 @@ infer_types_br(#b_var{}=V, Ts, #d{ds=Ds}) -> %% %% However, it is safe to subtract a type inferred from '=:=' if %% it is single-valued, e.g. if it is [] or the atom 'true'. - EqTypes0 = infer_eq_type(Op, Args, Ts, Ds), - {Types1,EqTypes} = partition(fun({_,T}) -> - is_singleton_type(T) - end, EqTypes0), - Types = Types1 ++ Types0, - {meet_types(EqTypes++Types, Ts),subtract_types(Types, Ts)}. + EqTypes = infer_eq_type(Op, Args, Ts, Ds), + NegTypes1 = [P || {_,T}=P <- EqTypes, is_singleton_type(T)], + + PosTypes = EqTypes ++ PosTypes0, + SuccTs = meet_types(PosTypes, Ts), + + NegTypes = NegTypes0 ++ NegTypes1, + FailTs = subtract_types(NegTypes, Ts), + + {SuccTs,FailTs}. infer_types_switch(V, Lit, Ts, #d{ds=Ds}) -> Types = infer_eq_type({bif,'=:='}, [V, Lit], Ts, Ds), @@ -1457,6 +1445,19 @@ infer_eq_lit(#b_set{op=get_tuple_element, [{Tuple,#t_tuple{size=Index,elements=Es}}]; infer_eq_lit(_, _) -> []. +infer_type_negative(Op, Args, Ds) -> + case is_negative_inference_safe(Op, Args) of + true -> + infer_type(Op, Args, Ds); + false -> + [] + end. + +%% Conservative list of instructions for which negative +%% inference is safe. +is_negative_inference_safe(is_nonempty_list, _Args) -> true; +is_negative_inference_safe(_, _) -> false. + infer_type({bif,element}, [#b_literal{val=Pos},#b_var{}=Tuple], _Ds) -> if is_integer(Pos), 1 =< Pos -> @@ -1649,6 +1650,12 @@ get_literal_from_type(nil) -> #b_literal{val=[]}; get_literal_from_type(_) -> none. +remove_element_info(#t_integer{elements={Min,Max}}, Es) -> + foldl(fun(El, Acc) when Min =< El, El =< Max -> + maps:remove(El, Acc); + (_El, Acc) -> Acc + end, Es, maps:keys(Es)). + t_atom() -> #t_atom{elements=any}. diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl index 09a5a6c104..ebe9631e09 100644 --- a/lib/compiler/src/beam_validator.erl +++ b/lib/compiler/src/beam_validator.erl @@ -2844,10 +2844,14 @@ call_return_type_1(erlang, setelement, 3, Vst) -> setelement(3, TupleType, #{}) end; call_return_type_1(erlang, '++', 2, Vst) -> - case get_term_type({x,0}, Vst) =:= cons orelse - get_term_type({x,1}, Vst) =:= cons of - true -> cons; - false -> list + LType = get_term_type({x,0}, Vst), + RType = get_term_type({x,1}, Vst), + case LType =:= cons orelse RType =:= cons of + true -> + cons; + false -> + %% `[] ++ RHS` yields RHS, even if RHS is not a list + join(list, RType) end; call_return_type_1(erlang, '--', 2, _Vst) -> list; diff --git a/lib/compiler/src/cerl.erl b/lib/compiler/src/cerl.erl index 62cd5b5120..bc28f58712 100644 --- a/lib/compiler/src/cerl.erl +++ b/lib/compiler/src/cerl.erl @@ -263,7 +263,7 @@ %% @see subtrees/1 %% @see meta/1 --type ctype() :: 'alias' | 'apply' | 'binary' | 'bitrst' | 'call' | 'case' +-type ctype() :: 'alias' | 'apply' | 'binary' | 'bitstr' | 'call' | 'case' | 'catch' | 'clause' | 'cons' | 'fun' | 'let' | 'letrec' | 'literal' | 'map' | 'map_pair' | 'module' | 'primop' | 'receive' | 'seq' | 'try' | 'tuple' | 'values' | 'var'. diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl index 3699c9d22e..007a0247f4 100644 --- a/lib/compiler/src/v3_core.erl +++ b/lib/compiler/src/v3_core.erl @@ -1811,7 +1811,8 @@ force_safe(Ce, St0) -> is_safe(#c_cons{}) -> true; is_safe(#c_tuple{}) -> true; -is_safe(#c_var{}) -> true; +is_safe(#c_var{name={_,_}}) -> false; %Fun. Not safe. +is_safe(#c_var{name=_}) -> true; %Ordinary variable. is_safe(#c_literal{}) -> true; is_safe(_) -> false. diff --git a/lib/compiler/test/beam_except_SUITE.erl b/lib/compiler/test/beam_except_SUITE.erl index 8e3b373d29..67947dc292 100644 --- a/lib/compiler/test/beam_except_SUITE.erl +++ b/lib/compiler/test/beam_except_SUITE.erl @@ -21,7 +21,8 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, - multiple_allocs/1,bs_get_tail/1,coverage/1]). + multiple_allocs/1,bs_get_tail/1,coverage/1, + binary_construction_allocation/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -32,7 +33,8 @@ groups() -> [{p,[parallel], [multiple_allocs, bs_get_tail, - coverage]}]. + coverage, + binary_construction_allocation]}]. init_per_suite(Config) -> test_lib:recompile(?MODULE), @@ -118,6 +120,20 @@ coverage(_) -> fake_function_clause(A) -> error(function_clause, [A,42.0]). + +binary_construction_allocation(_Config) -> + ok = do_binary_construction_allocation("PUT"), + ok. + +do_binary_construction_allocation(Req) -> + %% Allocation for building the error term was done by the + %% bs_init2 instruction. beam_except crashed because it expected + %% an explicit allocation instruction. + ok = case Req of + "POST" -> {error, <<"BAD METHOD ", Req/binary>>, Req}; + _ -> ok + end. + id(I) -> I. -file("fake.erl", 1). diff --git a/lib/compiler/test/beam_ssa_SUITE.erl b/lib/compiler/test/beam_ssa_SUITE.erl index 15cf9bcbf3..a741ebbdf9 100644 --- a/lib/compiler/test/beam_ssa_SUITE.erl +++ b/lib/compiler/test/beam_ssa_SUITE.erl @@ -22,7 +22,8 @@ -export([all/0,suite/0,groups/0,init_per_suite/1,end_per_suite/1, init_per_group/2,end_per_group/2, calls/1,tuple_matching/1,recv/1,maps/1, - cover_ssa_dead/1,combine_sw/1,share_opt/1]). + cover_ssa_dead/1,combine_sw/1,share_opt/1, + beam_ssa_dead_crash/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -37,7 +38,8 @@ groups() -> maps, cover_ssa_dead, combine_sw, - share_opt + share_opt, + beam_ssa_dead_crash ]}]. init_per_suite(Config) -> @@ -492,6 +494,60 @@ do_share_opt(A) -> end, receive after 1 -> ok end. +beam_ssa_dead_crash(_Config) -> + not_A_B = do_beam_ssa_dead_crash(id(false), id(true)), + not_A_not_B = do_beam_ssa_dead_crash(false, false), + neither = do_beam_ssa_dead_crash(true, false), + neither = do_beam_ssa_dead_crash(true, true), + ok. + +do_beam_ssa_dead_crash(A, B) -> + %% beam_ssa_dead attempts to shortcut branches that branch other + %% branches. When a two-way branch is encountered, beam_ssa_dead + %% will simulate execution along both paths, in the hope that both + %% paths happens to end up in the same place. + %% + %% During the simulated execution of this function, the boolean + %% varible for a `br` instruction would be replaced with the + %% literal atom `nil`, which is not allowed, and would crash the + %% compiler. In practice, during the actual execution, control + %% would never be transferred to that `br` instruction when the + %% variable in question had the value `nil`. + %% + %% beam_ssa_dead has been updated to immediately abort the search + %% along the current path if there is an attempt to substitute a + %% non-boolean value into a `br` instruction. + + case + case not A of + false -> + false; + true -> + B + end + of + V + when + V /= nil + andalso + V /= false -> + not_A_B; + _ -> + case + case not A of + false -> + false; + true -> + not B + end + of + true -> + not_A_not_B; + false -> + neither + end + end. + %% The identity function. id(I) -> I. diff --git a/lib/compiler/test/beam_type_SUITE.erl b/lib/compiler/test/beam_type_SUITE.erl index 882e281a44..076a604aa4 100644 --- a/lib/compiler/test/beam_type_SUITE.erl +++ b/lib/compiler/test/beam_type_SUITE.erl @@ -24,7 +24,7 @@ integers/1,numbers/1,coverage/1,booleans/1,setelement/1, cons/1,tuple/1,record_float/1,binary_float/1,float_compare/1, arity_checks/1,elixir_binaries/1,find_best/1, - test_size/1,cover_lists_functions/1]). + test_size/1,cover_lists_functions/1,list_append/1,bad_binary_unit/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -47,7 +47,9 @@ groups() -> elixir_binaries, find_best, test_size, - cover_lists_functions + cover_lists_functions, + list_append, + bad_binary_unit ]}]. init_per_suite(Config) -> @@ -271,8 +273,22 @@ setelement(_Config) -> T0 = id({a,42}), {a,_} = T0, {b,_} = setelement(1, T0, b), + {z,b} = do_setelement_1(<<(id(1)):32>>, {a,b}, z), + {new,two} = do_setelement_2(<<(id(1)):1>>, {one,two}, new), ok. +do_setelement_1(<<N:32>>, Tuple, NewValue) -> + _ = element(N, Tuple), + %% While updating the type for Tuple, beam_ssa_type would do: + %% maps:without(lists:seq(0, 4294967295), Elements) + setelement(N, Tuple, NewValue). + +do_setelement_2(<<N:1>>, Tuple, NewValue) -> + %% Cover the second clause in remove_element_info/2. The + %% type for the second element will be kept. + two = element(2, Tuple), + setelement(N, Tuple, NewValue). + cons(_Config) -> [did] = cons(assigned, did), @@ -487,5 +503,20 @@ cover_lists_functions(Config) -> true = is_list(Zipped), ok. +list_append(_Config) -> + %% '++'/2 has a quirk where it returns the right-hand argument as-is when + %% the left-hand is []. + hello = id([]) ++ id(hello), + ok. + +%% OTP-15872: The compiler would treat the "Unit" of bs_init instructions as +%% the unit of the result instead of the required unit of the input, causing +%% is_binary checks to be wrongly optimized away. +bad_binary_unit(_Config) -> + Bin = id(<<1,2,3>>), + Bitstring = <<Bin/binary,1:1>>, + false = is_binary(Bitstring), + ok. + id(I) -> I. diff --git a/lib/compiler/test/bs_match_SUITE.erl b/lib/compiler/test/bs_match_SUITE.erl index d97f49c56e..145a50f4ad 100644 --- a/lib/compiler/test/bs_match_SUITE.erl +++ b/lib/compiler/test/bs_match_SUITE.erl @@ -44,7 +44,8 @@ beam_bsm/1,guard/1,is_ascii/1,non_opt_eq/1, expression_before_match/1,erl_689/1,restore_on_call/1, restore_after_catch/1,matches_on_parameter/1,big_positions/1, - matching_meets_apply/1,bs_start_match2_defs/1]). + matching_meets_apply/1,bs_start_match2_defs/1, + exceptions_after_match_failure/1]). -export([coverage_id/1,coverage_external_ignore/2]). @@ -80,7 +81,8 @@ groups() -> beam_bsm,guard,is_ascii,non_opt_eq, expression_before_match,erl_689,restore_on_call, matches_on_parameter,big_positions, - matching_meets_apply,bs_start_match2_defs]}]. + matching_meets_apply,bs_start_match2_defs, + exceptions_after_match_failure]}]. init_per_suite(Config) -> @@ -2005,4 +2007,17 @@ do_matching_meets_apply(_Bin, {Handler, State}) -> %% Another case of the above. Handler:abs(State). +%% Exception handling was broken on the failure path of bs_start_match as +%% beam_ssa_bsm accidentally cloned and renamed the ?BADARG_BLOCK. +exceptions_after_match_failure(_Config) -> + {'EXIT', {badarith, _}} = (catch do_exceptions_after_match_failure(atom)), + ok = do_exceptions_after_match_failure(<<0, 1, "gurka">>), + ok = do_exceptions_after_match_failure(2.0). + +do_exceptions_after_match_failure(<<_A, _B, "gurka">>) -> + ok; +do_exceptions_after_match_failure(Other) -> + Other / 2.0, + ok. + id(I) -> I. diff --git a/lib/compiler/test/core_SUITE.erl b/lib/compiler/test/core_SUITE.erl index e5611e99d1..72016c6d76 100644 --- a/lib/compiler/test/core_SUITE.erl +++ b/lib/compiler/test/core_SUITE.erl @@ -29,7 +29,8 @@ bs_shadowed_size_var/1, cover_v3_kernel_1/1,cover_v3_kernel_2/1,cover_v3_kernel_3/1, cover_v3_kernel_4/1,cover_v3_kernel_5/1, - non_variable_apply/1,name_capture/1,fun_letrec_effect/1]). + non_variable_apply/1,name_capture/1,fun_letrec_effect/1, + get_map_element/1]). -include_lib("common_test/include/ct.hrl"). @@ -57,7 +58,8 @@ groups() -> bs_shadowed_size_var, cover_v3_kernel_1,cover_v3_kernel_2,cover_v3_kernel_3, cover_v3_kernel_4,cover_v3_kernel_5, - non_variable_apply,name_capture,fun_letrec_effect + non_variable_apply,name_capture,fun_letrec_effect, + get_map_element ]}]. @@ -95,6 +97,7 @@ end_per_group(_GroupName, Config) -> ?comp(non_variable_apply). ?comp(name_capture). ?comp(fun_letrec_effect). +?comp(get_map_element). try_it(Mod, Conf) -> Src = filename:join(proplists:get_value(data_dir, Conf), diff --git a/lib/compiler/test/core_SUITE_data/get_map_element.core b/lib/compiler/test/core_SUITE_data/get_map_element.core new file mode 100644 index 0000000000..092b5e71eb --- /dev/null +++ b/lib/compiler/test/core_SUITE_data/get_map_element.core @@ -0,0 +1,18 @@ +module 'get_map_element' ['get_map_element'/0] +attributes [] + +'get_map_element'/0 = + fun () -> + apply 'match_map'/1(~{'foo'=>'bar'}~) + +'match_map'/1 = + fun (_0) -> + case _0 of + <~{'foo':='bar'}~> when 'true' -> + 'ok' + %% It will be undefined behaviour at runtime if no + %% clause of the case can be selected. That can't + %% happen for this module, because match_map/1 is + %% always called with a matching map argument. + end +end diff --git a/lib/compiler/test/fun_SUITE.erl b/lib/compiler/test/fun_SUITE.erl index 1df0a05275..7fc6195e31 100644 --- a/lib/compiler/test/fun_SUITE.erl +++ b/lib/compiler/test/fun_SUITE.erl @@ -22,7 +22,8 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, test1/1,overwritten_fun/1,otp_7202/1,bif_fun/1, - external/1,eep37/1,eep37_dup/1,badarity/1,badfun/1]). + external/1,eep37/1,eep37_dup/1,badarity/1,badfun/1, + duplicated_fun/1]). %% Internal exports. -export([call_me/1,dup1/0,dup2/0]). @@ -37,7 +38,7 @@ all() -> groups() -> [{p,[parallel], [test1,overwritten_fun,otp_7202,bif_fun,external,eep37, - eep37_dup,badarity,badfun]}]. + eep37_dup,badarity,badfun,duplicated_fun]}]. init_per_suite(Config) -> test_lib:recompile(?MODULE), @@ -261,5 +262,20 @@ badfun(_Config) -> expect_badfun(Term, Exit) -> {'EXIT',{{badfun,Term},_}} = Exit. +duplicated_fun(_Config) -> + try + %% The following code used to crash the compiler before + %% v3_core:is_safe/1 was corrected to consider fun variables + %% unsafe. + id([print_result_paths_fun = fun duplicated_fun_helper/1]), + ct:error(should_fail) + catch + error:{badmatch,F} when is_function(F, 1) -> + ok + end. + +duplicated_fun_helper(_) -> + ok. + id(I) -> I. diff --git a/lib/compiler/test/guard_SUITE.erl b/lib/compiler/test/guard_SUITE.erl index ed0a56f064..cea7a374cd 100644 --- a/lib/compiler/test/guard_SUITE.erl +++ b/lib/compiler/test/guard_SUITE.erl @@ -35,7 +35,8 @@ basic_andalso_orelse/1,traverse_dcd/1, check_qlc_hrl/1,andalso_semi/1,t_tuple_size/1,binary_part/1, bad_constants/1,bad_guards/1, - guard_in_catch/1,beam_bool_SUITE/1]). + guard_in_catch/1,beam_bool_SUITE/1, + repeated_type_tests/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -53,7 +54,8 @@ groups() -> rel_ops,rel_op_combinations, literal_type_tests,basic_andalso_orelse,traverse_dcd, check_qlc_hrl,andalso_semi,t_tuple_size,binary_part, - bad_constants,bad_guards,guard_in_catch,beam_bool_SUITE]}]. + bad_constants,bad_guards,guard_in_catch,beam_bool_SUITE, + repeated_type_tests]}]. init_per_suite(Config) -> test_lib:recompile(?MODULE), @@ -2261,6 +2263,25 @@ maps() -> evidence(#{0 := Charge}) when 0; #{[] => Charge} == #{[] => 42} -> ok. +repeated_type_tests(_Config) -> + binary = repeated_type_test(<<42>>), + bitstring = repeated_type_test(<<1:1>>), + other = repeated_type_test(atom), + ok. + +repeated_type_test(T) -> + %% Test for a bug in beam_ssa_dead. + if is_bitstring(T) -> + if is_binary(T) -> %This test would be optimized away. + binary; + true -> + bitstring + end; + true -> + other + end. + + %% Call this function to turn off constant propagation. id(I) -> I. diff --git a/lib/compiler/test/match_SUITE.erl b/lib/compiler/test/match_SUITE.erl index 94bfbb0efe..bc74ec4984 100644 --- a/lib/compiler/test/match_SUITE.erl +++ b/lib/compiler/test/match_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2018. All Rights Reserved. +%% Copyright Ericsson AB 2004-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -25,7 +25,8 @@ match_in_call/1,untuplify/1,shortcut_boolean/1,letify_guard/1, selectify/1,deselectify/1,underscore/1,match_map/1,map_vars_used/1, coverage/1,grab_bag/1,literal_binary/1, - unary_op/1,eq_types/1,match_after_return/1]). + unary_op/1,eq_types/1,match_after_return/1,match_right_tuple/1, + tuple_size_in_try/1]). -include_lib("common_test/include/ct.hrl"). @@ -41,7 +42,8 @@ groups() -> shortcut_boolean,letify_guard,selectify,deselectify, underscore,match_map,map_vars_used,coverage, grab_bag,literal_binary,unary_op,eq_types, - match_after_return]}]. + match_after_return,match_right_tuple, + tuple_size_in_try]}]. init_per_suite(Config) -> @@ -902,4 +904,39 @@ match_after_return(Config) when is_list(Config) -> mar_test_tuple(I) -> {gurka, I}. +match_right_tuple(Config) when is_list(Config) -> + %% The loader wrongly coalesced certain get_tuple_element sequences, fusing + %% the code below into a single i_get_tuple_element2 operating on {x,0} + %% even though the first one overwrites it. + %% + %% {get_tuple_element,{x,0},0,{x,0}}. + %% {get_tuple_element,{x,0},1,{x,1}}. + + Inner = {id(wrong_element), id(ok)}, + Outer = {Inner, id(wrong_tuple)}, + ok = match_right_tuple_1(Outer). + +match_right_tuple_1(T) -> + {A, _} = T, + {_, B} = A, + %% The call ensures that A is in {x,0} and B is in {x,1} + id(force_succ_regs(A, B)). + +force_succ_regs(_A, B) -> B. + +tuple_size_in_try(Config) when is_list(Config) -> + %% The tuple_size optimization was applied outside of guards, causing + %% either the emulator or compiler to crash. + ok = tsit(gurka), + ok = tsit(gaffel). + +tsit(A) -> + try + id(ignored), + 1 = tuple_size(A), + error + catch + _:_ -> ok + end. + id(I) -> I. diff --git a/lib/compiler/test/receive_SUITE.erl b/lib/compiler/test/receive_SUITE.erl index 0038eb1a4b..752491f0f8 100644 --- a/lib/compiler/test/receive_SUITE.erl +++ b/lib/compiler/test/receive_SUITE.erl @@ -26,7 +26,7 @@ init_per_testcase/2,end_per_testcase/2, export/1,recv/1,coverage/1,otp_7980/1,ref_opt/1, wait/1,recv_in_try/1,double_recv/1,receive_var_zero/1, - match_built_terms/1]). + match_built_terms/1,elusive_common_exit/1]). -include_lib("common_test/include/ct.hrl"). @@ -47,7 +47,7 @@ groups() -> [{p,test_lib:parallel(), [recv,coverage,otp_7980,ref_opt,export,wait, recv_in_try,double_recv,receive_var_zero, - match_built_terms]}]. + match_built_terms,elusive_common_exit]}]. init_per_suite(Config) -> @@ -427,4 +427,26 @@ match_built_terms(Config) when is_list(Config) -> ?MATCH_BUILT_TERM(Ref, <<A, B>>), ?MATCH_BUILT_TERM(Ref, #{ 1 => A, 2 => B}). +elusive_common_exit(_Config) -> + self() ! {1, a}, + self() ! {2, b}, + {[z], [{2,b},{1,a}]} = elusive_loop([x,y,z], 2, []), + ok. + +elusive_loop(List, 0, Results) -> + {List, Results}; +elusive_loop(List, ToReceive, Results) -> + {Result, RemList} = + receive + {_Pos, _R} = Res when List =/= [] -> + [_H|T] = List, + {Res, T}; + {_Pos, _R} = Res when List =:= [] -> + {Res, []} + end, + %% beam_ssa_pre_codegen:fix_receives() would fail to find + %% the common exit block for this receive. That would mean + %% that it would not insert all necessary copy instructions. + elusive_loop(RemList, ToReceive-1, [Result | Results]). + id(I) -> I. diff --git a/lib/compiler/vsn.mk b/lib/compiler/vsn.mk index 494de072ff..508bbc902c 100644 --- a/lib/compiler/vsn.mk +++ b/lib/compiler/vsn.mk @@ -1 +1 @@ -COMPILER_VSN = 7.4 +COMPILER_VSN = 7.4.2 diff --git a/lib/crypto/c_src/Makefile.in b/lib/crypto/c_src/Makefile.in index b6a65d7488..f922c3fb9b 100644 --- a/lib/crypto/c_src/Makefile.in +++ b/lib/crypto/c_src/Makefile.in @@ -92,16 +92,15 @@ CRYPTO_OBJS = $(OBJDIR)/crypto$(TYPEMARKER).o \ $(OBJDIR)/hash$(TYPEMARKER).o \ $(OBJDIR)/hmac$(TYPEMARKER).o \ $(OBJDIR)/info$(TYPEMARKER).o \ + $(OBJDIR)/mac$(TYPEMARKER).o \ $(OBJDIR)/math$(TYPEMARKER).o \ $(OBJDIR)/pkey$(TYPEMARKER).o \ - $(OBJDIR)/poly1305$(TYPEMARKER).o \ $(OBJDIR)/rand$(TYPEMARKER).o \ $(OBJDIR)/rsa$(TYPEMARKER).o \ $(OBJDIR)/srp$(TYPEMARKER).o CALLBACK_OBJS = $(OBJDIR)/crypto_callback$(TYPEMARKER).o NIF_MAKEFILE = $(PRIVDIR)/Makefile -CRYPTO_STATIC_OBJS = $(OBJDIR)/crypto_static$(TYPEMARKER).o\ - $(OBJDIR)/crypto_callback_static$(TYPEMARKER).o +CRYPTO_STATIC_OBJS = $(patsubst $(OBJDIR)/%$(TYPEMARKER).o,$(OBJDIR)/%_static$(TYPEMARKER).o,$(CRYPTO_OBJS) $(CALLBACK_OBJS)) NIF_ARCHIVE = $(LIBDIR)/crypto$(TYPEMARKER).a diff --git a/lib/crypto/c_src/algorithms.c b/lib/crypto/c_src/algorithms.c index 75cddeb1e9..53b8b7eaa9 100644 --- a/lib/crypto/c_src/algorithms.c +++ b/lib/crypto/c_src/algorithms.c @@ -20,13 +20,12 @@ #include "algorithms.h" #include "cipher.h" +#include "mac.h" static unsigned int algo_hash_cnt, algo_hash_fips_cnt; static ERL_NIF_TERM algo_hash[14]; /* increase when extending the list */ static unsigned int algo_pubkey_cnt, algo_pubkey_fips_cnt; static ERL_NIF_TERM algo_pubkey[12]; /* increase when extending the list */ -static unsigned int algo_mac_cnt, algo_mac_fips_cnt; -static ERL_NIF_TERM algo_mac[3]; /* increase when extending the list */ static unsigned int algo_curve_cnt, algo_curve_fips_cnt; static ERL_NIF_TERM algo_curve[89]; /* increase when extending the list */ static unsigned int algo_rsa_opts_cnt, algo_rsa_opts_fips_cnt; @@ -101,19 +100,6 @@ void init_algorithms_types(ErlNifEnv* env) #endif algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "srp"); - - // Validated algorithms first - algo_mac_cnt = 0; - algo_mac[algo_mac_cnt++] = enif_make_atom(env,"hmac"); -#ifdef HAVE_CMAC - algo_mac[algo_mac_cnt++] = enif_make_atom(env,"cmac"); -#endif -#ifdef HAVE_POLY1305 - algo_mac[algo_mac_cnt++] = enif_make_atom(env,"poly1305"); -#endif - // Non-validated algorithms follow - algo_mac_fips_cnt = algo_mac_cnt; - // Validated algorithms first algo_curve_cnt = 0; #if defined(HAVE_EC) @@ -250,7 +236,6 @@ void init_algorithms_types(ErlNifEnv* env) // Check that the max number of algos is updated ASSERT(algo_hash_cnt <= sizeof(algo_hash)/sizeof(ERL_NIF_TERM)); ASSERT(algo_pubkey_cnt <= sizeof(algo_pubkey)/sizeof(ERL_NIF_TERM)); - ASSERT(algo_mac_cnt <= sizeof(algo_mac)/sizeof(ERL_NIF_TERM)); ASSERT(algo_curve_cnt <= sizeof(algo_curve)/sizeof(ERL_NIF_TERM)); ASSERT(algo_rsa_opts_cnt <= sizeof(algo_rsa_opts)/sizeof(ERL_NIF_TERM)); } @@ -284,18 +269,12 @@ ERL_NIF_TERM cipher_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv return cipher_types_as_list(env); /* Exclude old api ciphers */ } + ERL_NIF_TERM mac_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - unsigned int cnt = -#ifdef FIPS_SUPPORT - FIPS_mode() ? algo_mac_fips_cnt : -#endif - algo_mac_cnt; - - return enif_make_list_from_array(env, algo_mac, cnt); + return mac_types_as_list(env); } - ERL_NIF_TERM curve_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { unsigned int cnt = diff --git a/lib/crypto/c_src/api_ng.c b/lib/crypto/c_src/api_ng.c index 3408ba1b88..941e03cc98 100644 --- a/lib/crypto/c_src/api_ng.c +++ b/lib/crypto/c_src/api_ng.c @@ -100,7 +100,7 @@ static int get_init_args(ErlNifEnv* env, } - if (FORBIDDEN_IN_FIPS(*cipherp)) + if (CIPHER_FORBIDDEN_IN_FIPS(*cipherp)) { *return_term = EXCP_NOTSUP(env, "Forbidden in FIPS"); goto err; @@ -334,12 +334,11 @@ ERL_NIF_TERM ng_crypto_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg if ((ctx_res = enif_alloc_resource(evp_cipher_ctx_rtype, sizeof(struct evp_cipher_ctx))) == NULL) return EXCP_ERROR(env, "Can't allocate resource"); - if (!get_init_args(env, ctx_res, argv[0], argv[1], argv[2], argv[argc-1], + if (get_init_args(env, ctx_res, argv[0], argv[1], argv[2], argv[argc-1], &cipherp, &ret)) - /* Error msg in &ret */ - goto ret; + ret = enif_make_resource(env, ctx_res); + /* else error msg in ret */ - ret = enif_make_resource(env, ctx_res); if(ctx_res) enif_release_resource(ctx_res); } else if (enif_get_resource(env, argv[0], (ErlNifResourceType*)evp_cipher_ctx_rtype, (void**)&ctx_res)) { diff --git a/lib/crypto/c_src/atoms.c b/lib/crypto/c_src/atoms.c index 059c14690f..bbeb329fa2 100644 --- a/lib/crypto/c_src/atoms.c +++ b/lib/crypto/c_src/atoms.c @@ -30,6 +30,10 @@ ERL_NIF_TERM atom_rsa_no_padding; ERL_NIF_TERM atom_signature_md; ERL_NIF_TERM atom_undefined; +ERL_NIF_TERM atom_hmac; +ERL_NIF_TERM atom_cmac; +ERL_NIF_TERM atom_poly1305; + ERL_NIF_TERM atom_ok; ERL_NIF_TERM atom_none; ERL_NIF_TERM atom_notsup; @@ -155,6 +159,11 @@ int init_atoms(ErlNifEnv *env, const ERL_NIF_TERM fips_mode, const ERL_NIF_TERM atom_rsa_no_padding = enif_make_atom(env,"rsa_no_padding"); atom_signature_md = enif_make_atom(env,"signature_md"); atom_undefined = enif_make_atom(env,"undefined"); + + atom_hmac = enif_make_atom(env,"hmac"); + atom_cmac = enif_make_atom(env,"cmac"); + atom_poly1305 = enif_make_atom(env,"poly1305"); + atom_ok = enif_make_atom(env,"ok"); atom_none = enif_make_atom(env,"none"); atom_notsup = enif_make_atom(env,"notsup"); diff --git a/lib/crypto/c_src/atoms.h b/lib/crypto/c_src/atoms.h index f5913de96f..0e2f1a0022 100644 --- a/lib/crypto/c_src/atoms.h +++ b/lib/crypto/c_src/atoms.h @@ -34,6 +34,10 @@ extern ERL_NIF_TERM atom_rsa_no_padding; extern ERL_NIF_TERM atom_signature_md; extern ERL_NIF_TERM atom_undefined; +extern ERL_NIF_TERM atom_hmac; +extern ERL_NIF_TERM atom_cmac; +extern ERL_NIF_TERM atom_poly1305; + extern ERL_NIF_TERM atom_ok; extern ERL_NIF_TERM atom_none; extern ERL_NIF_TERM atom_notsup; diff --git a/lib/crypto/c_src/bn.c b/lib/crypto/c_src/bn.c index 34ed4f7ebc..6021d56db6 100644 --- a/lib/crypto/c_src/bn.c +++ b/lib/crypto/c_src/bn.c @@ -32,8 +32,6 @@ int get_bn_from_mpint(ErlNifEnv* env, ERL_NIF_TERM term, BIGNUM** bnp) if (bin.size > INT_MAX - 4) goto err; - ERL_VALGRIND_ASSERT_MEM_DEFINED(bin.data, bin.size); - if (bin.size < 4) goto err; sz = (int)bin.size - 4; @@ -60,8 +58,6 @@ int get_bn_from_bin(ErlNifEnv* env, ERL_NIF_TERM term, BIGNUM** bnp) if (bin.size > INT_MAX) goto err; - ERL_VALGRIND_ASSERT_MEM_DEFINED(bin.data, bin.size); - if ((ret = BN_bin2bn(bin.data, (int)bin.size, NULL)) == NULL) goto err; @@ -103,8 +99,6 @@ ERL_NIF_TERM mod_exp_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) unsigned extra_byte; ERL_NIF_TERM ret; - ASSERT(argc == 4); - if (!get_bn_from_bin(env, argv[0], &bn_base)) goto bad_arg; if (!get_bn_from_bin(env, argv[1], &bn_exponent)) @@ -177,7 +171,6 @@ ERL_NIF_TERM bn2term(ErlNifEnv* env, const BIGNUM *bn) BN_bn2bin(bn, ptr); - ERL_VALGRIND_MAKE_MEM_DEFINED(ptr, dlen); return ret; err: diff --git a/lib/crypto/c_src/cipher.c b/lib/crypto/c_src/cipher.c index 00072af632..e144a891a6 100644 --- a/lib/crypto/c_src/cipher.c +++ b/lib/crypto/c_src/cipher.c @@ -214,7 +214,7 @@ ERL_NIF_TERM cipher_info_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[] if ((cipherp = get_cipher_type_no_key(argv[0])) == NULL) return enif_make_badarg(env); - if (FORBIDDEN_IN_FIPS(cipherp)) + if (CIPHER_FORBIDDEN_IN_FIPS(cipherp)) return enif_raise_exception(env, atom_notsup); if ((cipher = cipherp->cipher.p) == NULL) return enif_raise_exception(env, atom_notsup); @@ -330,10 +330,11 @@ ERL_NIF_TERM cipher_types_as_list(ErlNifEnv* env) for (p = cipher_types; (p->type.atom & (p->type.atom != atom_false)); p++) { if ((prev == p->type.atom) || - FORBIDDEN_IN_FIPS(p) ) + CIPHER_FORBIDDEN_IN_FIPS(p) ) continue; if ((p->cipher.p != NULL) || + (p->flags & AES_CTR_COMPAT) || (p->type.atom == atom_aes_ige256)) /* Special handling. Bad indeed... */ { hd = enif_make_list_cell(env, p->type.atom, hd); diff --git a/lib/crypto/c_src/cipher.h b/lib/crypto/c_src/cipher.h index 0e51c410eb..c23e128824 100644 --- a/lib/crypto/c_src/cipher.h +++ b/lib/crypto/c_src/cipher.h @@ -52,10 +52,10 @@ struct cipher_type_t { #ifdef FIPS_SUPPORT /* May have FIPS support, must check dynamically if it is enabled */ -# define FORBIDDEN_IN_FIPS(P) (((P)->flags & NO_FIPS_CIPHER) && FIPS_mode()) +# define CIPHER_FORBIDDEN_IN_FIPS(P) (((P)->flags & NO_FIPS_CIPHER) && FIPS_mode()) #else /* No FIPS support since the symbol FIPS_SUPPORT is undefined */ -# define FORBIDDEN_IN_FIPS(P) 0 +# define CIPHER_FORBIDDEN_IN_FIPS(P) 0 #endif extern ErlNifResourceType* evp_cipher_ctx_rtype; diff --git a/lib/crypto/c_src/cmac.c b/lib/crypto/c_src/cmac.c index 49e67ccf29..a1564f6661 100644 --- a/lib/crypto/c_src/cmac.c +++ b/lib/crypto/c_src/cmac.c @@ -18,71 +18,56 @@ * %CopyrightEnd% */ -#include "cmac.h" -#include "cipher.h" +#include "common.h" -ERL_NIF_TERM cmac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Type, Key, Data) */ -#if defined(HAVE_CMAC) - const struct cipher_type_t *cipherp; - const EVP_CIPHER *cipher; - CMAC_CTX *ctx = NULL; - ErlNifBinary key; - ErlNifBinary data; - ERL_NIF_TERM ret; - size_t ret_size; - unsigned char *outp; - int cipher_len; +/***************************************************************** + * + * This file has functions for compatibility with cryptolibs + * lacking the EVP_Digest API. + * + * See mac.c for the implementation using the EVP interface. + * + ****************************************************************/ - ASSERT(argc == 3); +#if defined(HAVE_CMAC) && !defined(HAVE_EVP_PKEY_new_CMAC_key) - if (!enif_inspect_iolist_as_binary(env, argv[1], &key)) - goto bad_arg; - if ((cipherp = get_cipher_type(argv[0], key.size)) == NULL) - goto bad_arg; - if (cipherp->flags & (NON_EVP_CIPHER | AEAD_CIPHER)) - goto bad_arg; - if (!enif_inspect_iolist_as_binary(env, argv[2], &data)) - goto bad_arg; +#include "cmac.h" - if (FORBIDDEN_IN_FIPS(cipherp)) - return enif_raise_exception(env, atom_notsup); - if ((cipher = cipherp->cipher.p) == NULL) - return enif_raise_exception(env, atom_notsup); +int cmac_low_level(ErlNifEnv* env, + ErlNifBinary key_bin, const EVP_CIPHER* cipher, ErlNifBinary text, + ErlNifBinary *ret_bin, int *ret_bin_alloc, ERL_NIF_TERM *return_term) +{ + CMAC_CTX *ctx = NULL; + size_t size; if ((ctx = CMAC_CTX_new()) == NULL) - goto err; - if (!CMAC_Init(ctx, key.data, key.size, cipher, NULL)) - goto err; - if (!CMAC_Update(ctx, data.data, data.size)) - goto err; - if ((cipher_len = EVP_CIPHER_block_size(cipher)) < 0) - goto err; - if ((outp = enif_make_new_binary(env, (size_t)cipher_len, &ret)) == NULL) - goto err; - if (!CMAC_Final(ctx, outp, &ret_size)) - goto err; + goto local_err; - ASSERT(ret_size == (unsigned)EVP_CIPHER_block_size(cipher)); - CONSUME_REDS(env, data); - goto done; + if (!CMAC_Init(ctx, key_bin.data, key_bin.size, cipher, NULL)) + goto local_err; - bad_arg: - return enif_make_badarg(env); + if (!CMAC_Update(ctx, text.data, text.size)) + goto local_err; - err: - ret = atom_notsup; + if ((size = (size_t)EVP_CIPHER_block_size(cipher)) < 0) + goto local_err; - done: + if (!enif_alloc_binary(size, ret_bin)) + goto local_err; + *ret_bin_alloc = 1; + + if (!CMAC_Final(ctx, ret_bin->data, &ret_bin->size)) + goto local_err; + + CMAC_CTX_free(ctx); + return 1; + + local_err: if (ctx) CMAC_CTX_free(ctx); - return ret; -#else - /* The CMAC functionality was introduced in OpenSSL 1.0.1 - * Although OTP requires at least version 0.9.8, the versions 0.9.8 and 1.0.0 are - * no longer maintained. */ - return atom_notsup; -#endif + *return_term = EXCP_ERROR(env,"Compat cmac"); + return 0; } +#endif diff --git a/lib/crypto/c_src/cmac.h b/lib/crypto/c_src/cmac.h index 14488def58..04c742b2dc 100644 --- a/lib/crypto/c_src/cmac.h +++ b/lib/crypto/c_src/cmac.h @@ -23,6 +23,12 @@ #include "common.h" -ERL_NIF_TERM cmac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +#if defined(HAVE_CMAC) && !defined(HAVE_EVP_PKEY_new_CMAC_key) + +int cmac_low_level(ErlNifEnv* env, + ErlNifBinary key_bin, const EVP_CIPHER* cipher, ErlNifBinary text, + ErlNifBinary *ret_bin, int *ret_bin_alloc, ERL_NIF_TERM *return_term); + +#endif #endif /* E_CMAC_H__ */ diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c index d533cba140..802818541b 100644 --- a/lib/crypto/c_src/crypto.c +++ b/lib/crypto/c_src/crypto.c @@ -31,7 +31,7 @@ #include "api_ng.h" #include "bn.h" #include "cipher.h" -#include "cmac.h" +#include "mac.h" #include "dh.h" #include "digest.h" #include "dss.h" @@ -46,7 +46,6 @@ #include "info.h" #include "math.h" #include "pkey.h" -#include "poly1305.h" #include "rand.h" #include "rsa.h" #include "srp.h" @@ -74,13 +73,10 @@ static ErlNifFunc nif_funcs[] = { {"hash_init_nif", 1, hash_init_nif, 0}, {"hash_update_nif", 2, hash_update_nif, 0}, {"hash_final_nif", 1, hash_final_nif, 0}, - {"hmac_nif", 3, hmac_nif, 0}, - {"hmac_nif", 4, hmac_nif, 0}, - {"hmac_init_nif", 2, hmac_init_nif, 0}, - {"hmac_update_nif", 2, hmac_update_nif, 0}, - {"hmac_final_nif", 1, hmac_final_nif, 0}, - {"hmac_final_nif", 2, hmac_final_nif, 0}, - {"cmac_nif", 3, cmac_nif, 0}, + {"mac_nif", 4, mac_nif, 0}, + {"mac_init_nif", 3, mac_init_nif, 0}, + {"mac_update_nif", 2, mac_update_nif, 0}, + {"mac_final_nif", 1, mac_final_nif, 0}, {"cipher_info_nif", 1, cipher_info_nif, 0}, {"aes_ige_crypt_nif", 4, aes_ige_crypt_nif, 0}, {"ng_crypto_init_nif", 4, ng_crypto_init_nif, 0}, @@ -112,8 +108,6 @@ static ErlNifFunc nif_funcs[] = { {"aead_cipher", 7, aead_cipher, 0}, - {"poly1305_nif", 2, poly1305_nif, 0}, - {"engine_by_id_nif", 1, engine_by_id_nif, 0}, {"engine_init_nif", 1, engine_init_nif, 0}, {"engine_finish_nif", 1, engine_finish_nif, 0}, @@ -181,9 +175,15 @@ static int initialize(ErlNifEnv* env, ERL_NIF_TERM load_info) if (!enif_inspect_binary(env, tpl_array[1], &lib_bin)) return __LINE__; +#ifdef HAS_EVP_PKEY_CTX + if (!init_mac_ctx(env)) { + return __LINE__; + } +#else if (!init_hmac_ctx(env)) { return __LINE__; } +#endif if (!init_hash_ctx(env)) { return __LINE__; } @@ -248,6 +248,7 @@ static int initialize(ErlNifEnv* env, ERL_NIF_TERM load_info) #endif /* OPENSSL_THREADS */ init_digest_types(env); + init_mac_types(env); init_cipher_types(env); init_algorithms_types(env); diff --git a/lib/crypto/c_src/digest.c b/lib/crypto/c_src/digest.c index c987a664d5..0f887ab765 100644 --- a/lib/crypto/c_src/digest.c +++ b/lib/crypto/c_src/digest.c @@ -22,7 +22,7 @@ static struct digest_type_t digest_types[] = { - {{"md4"}, + {{"md4"}, NO_FIPS_DIGEST, #ifdef HAVE_MD4 {&EVP_md4} #else @@ -30,7 +30,7 @@ static struct digest_type_t digest_types[] = #endif }, - {{"md5"}, + {{"md5"}, NO_FIPS_DIGEST, #ifdef HAVE_MD5 {&EVP_md5} #else @@ -38,7 +38,7 @@ static struct digest_type_t digest_types[] = #endif }, - {{"ripemd160"}, + {{"ripemd160"}, NO_FIPS_DIGEST, #ifdef HAVE_RIPEMD160 {&EVP_ripemd160} #else @@ -46,9 +46,9 @@ static struct digest_type_t digest_types[] = #endif }, - {{"sha"}, {&EVP_sha1}}, + {{"sha"}, 0, {&EVP_sha1}}, - {{"sha224"}, + {{"sha224"}, 0, #ifdef HAVE_SHA224 {&EVP_sha224} #else @@ -56,7 +56,7 @@ static struct digest_type_t digest_types[] = #endif }, - {{"sha256"}, + {{"sha256"}, 0, #ifdef HAVE_SHA256 {&EVP_sha256} #else @@ -64,7 +64,7 @@ static struct digest_type_t digest_types[] = #endif }, - {{"sha384"}, + {{"sha384"}, 0, #ifdef HAVE_SHA384 {&EVP_sha384} #else @@ -72,7 +72,7 @@ static struct digest_type_t digest_types[] = #endif }, - {{"sha512"}, + {{"sha512"}, 0, #ifdef HAVE_SHA512 {&EVP_sha512} #else @@ -80,7 +80,7 @@ static struct digest_type_t digest_types[] = #endif }, - {{"sha3_224"}, + {{"sha3_224"}, 0, #ifdef HAVE_SHA3_224 {&EVP_sha3_224} #else @@ -88,7 +88,7 @@ static struct digest_type_t digest_types[] = #endif }, - {{"sha3_256"}, + {{"sha3_256"}, 0, #ifdef HAVE_SHA3_256 {&EVP_sha3_256} #else @@ -96,7 +96,7 @@ static struct digest_type_t digest_types[] = #endif }, - {{"sha3_384"}, + {{"sha3_384"}, 0, #ifdef HAVE_SHA3_384 {&EVP_sha3_384} #else @@ -104,7 +104,7 @@ static struct digest_type_t digest_types[] = #endif }, - {{"sha3_512"}, + {{"sha3_512"}, 0, #ifdef HAVE_SHA3_512 {&EVP_sha3_512} #else @@ -112,7 +112,7 @@ static struct digest_type_t digest_types[] = #endif }, - {{"blake2b"}, + {{"blake2b"}, 0, #ifdef HAVE_BLAKE2 {&EVP_blake2b512} #else @@ -120,7 +120,7 @@ static struct digest_type_t digest_types[] = #endif }, - {{"blake2s"}, + {{"blake2s"}, 0, #ifdef HAVE_BLAKE2 {&EVP_blake2s256} #else @@ -128,7 +128,8 @@ static struct digest_type_t digest_types[] = #endif }, - {{NULL}, {NULL}} + /*==== End of list ==== */ + {{NULL}, 0, {NULL}} }; void init_digest_types(ErlNifEnv* env) diff --git a/lib/crypto/c_src/digest.h b/lib/crypto/c_src/digest.h index 06852416cf..b1f8128a1f 100644 --- a/lib/crypto/c_src/digest.h +++ b/lib/crypto/c_src/digest.h @@ -28,12 +28,25 @@ struct digest_type_t { const char* str; /* before init, NULL for end-of-table */ ERL_NIF_TERM atom; /* after init, 'false' for end-of-table */ }type; + unsigned flags; union { const EVP_MD* (*funcp)(void); /* before init, NULL if notsup */ const EVP_MD* p; /* after init, NULL if notsup */ }md; }; +/* masks in the flags field if digest_type_t */ +#define NO_FIPS_DIGEST 1 + +#ifdef FIPS_SUPPORT +/* May have FIPS support, must check dynamically if it is enabled */ +# define DIGEST_FORBIDDEN_IN_FIPS(P) (((P)->flags & NO_FIPS_DIGEST) && FIPS_mode()) +#else +/* No FIPS support since the symbol FIPS_SUPPORT is undefined */ +# define DIGEST_FORBIDDEN_IN_FIPS(P) 0 +#endif + + void init_digest_types(ErlNifEnv* env); struct digest_type_t* get_digest_type(ERL_NIF_TERM type); diff --git a/lib/crypto/c_src/hmac.c b/lib/crypto/c_src/hmac.c index ff7005d75e..5e2c68bfee 100644 --- a/lib/crypto/c_src/hmac.c +++ b/lib/crypto/c_src/hmac.c @@ -18,6 +18,18 @@ * %CopyrightEnd% */ + +/***************************************************************** + * + * This file has functions for compatibility with cryptolibs + * lacking the EVP_Digest API. + * + * See mac.c for the implementation using the EVP interface. + * + ****************************************************************/ + +#ifndef HAS_EVP_PKEY_CTX + #include "hmac.h" #include "digest.h" @@ -47,61 +59,6 @@ int init_hmac_ctx(ErlNifEnv *env) { return 0; } -ERL_NIF_TERM hmac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Type, Key, Data) or (Type, Key, Data, MacSize) */ - struct digest_type_t *digp = NULL; - ErlNifBinary key, data; - unsigned char buff[EVP_MAX_MD_SIZE]; - unsigned size = 0, req_size = 0; - ERL_NIF_TERM ret; - unsigned char *outp; - - ASSERT(argc == 3 || argc == 4); - - if ((digp = get_digest_type(argv[0])) == NULL) - goto bad_arg; - if (!enif_inspect_iolist_as_binary(env, argv[1], &key)) - goto bad_arg; - if (key.size > INT_MAX) - goto bad_arg; - if (!enif_inspect_iolist_as_binary(env, argv[2], &data)) - goto bad_arg; - if (argc == 4) { - if (!enif_get_uint(env, argv[3], &req_size)) - goto bad_arg; - } - - if (digp->md.p == NULL) - goto err; - if (HMAC(digp->md.p, - key.data, (int)key.size, - data.data, data.size, - buff, &size) == NULL) - goto err; - - ASSERT(0 < size && size <= EVP_MAX_MD_SIZE); - CONSUME_REDS(env, data); - - if (argc == 4) { - if (req_size > size) - goto bad_arg; - - size = req_size; - } - - if ((outp = enif_make_new_binary(env, size, &ret)) == NULL) - goto err; - - memcpy(outp, buff, size); - return ret; - - bad_arg: - return enif_make_badarg(env); - - err: - return atom_notsup; -} - static void hmac_context_dtor(ErlNifEnv* env, struct hmac_context *obj) { if (obj == NULL) @@ -118,17 +75,17 @@ static void hmac_context_dtor(ErlNifEnv* env, struct hmac_context *obj) } ERL_NIF_TERM hmac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Type, Key) */ +{/* (hmac, Type, Key) */ struct digest_type_t *digp = NULL; ErlNifBinary key; ERL_NIF_TERM ret; struct hmac_context *obj = NULL; - ASSERT(argc == 2); + ASSERT(argc == 3); - if ((digp = get_digest_type(argv[0])) == NULL) + if ((digp = get_digest_type(argv[1])) == NULL) goto bad_arg; - if (!enif_inspect_iolist_as_binary(env, argv[1], &key)) + if (!enif_inspect_iolist_as_binary(env, argv[2], &key)) goto bad_arg; if (key.size > INT_MAX) goto bad_arg; @@ -268,3 +225,44 @@ ERL_NIF_TERM hmac_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) return ret; } + + +int hmac_low_level(ErlNifEnv* env, const EVP_MD *md, + ErlNifBinary key_bin, ErlNifBinary text, + ErlNifBinary *ret_bin, int *ret_bin_alloc, ERL_NIF_TERM *return_term) +{ + unsigned int size_int; + size_t size; + + /* Find the needed space */ + if (HMAC(md, + key_bin.data, (int)key_bin.size, + text.data, text.size, + NULL, &size_int) == NULL) + { + *return_term = EXCP_ERROR(env, "Get HMAC size failed"); + return 0; + } + + size = (size_t)size_int; /* Otherwise "size" is unused in 0.9.8.... */ + if (!enif_alloc_binary(size, ret_bin)) + { + *return_term = EXCP_ERROR(env, "Alloc binary"); + return 0; + } + *ret_bin_alloc = 1; + + /* And do the real HMAC calc */ + if (HMAC(md, + key_bin.data, (int)key_bin.size, + text.data, text.size, + ret_bin->data, &size_int) == NULL) + { + *return_term = EXCP_ERROR(env, "HMAC sign failed"); + return 0; + } + + return 1; +} + +#endif diff --git a/lib/crypto/c_src/hmac.h b/lib/crypto/c_src/hmac.h index 1f0e0ca632..f5805e13e5 100644 --- a/lib/crypto/c_src/hmac.h +++ b/lib/crypto/c_src/hmac.h @@ -21,13 +21,19 @@ #ifndef E_HMAC_H__ #define E_HMAC_H__ 1 +#ifndef HAS_EVP_PKEY_CTX + #include "common.h" int init_hmac_ctx(ErlNifEnv *env); -ERL_NIF_TERM hmac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); ERL_NIF_TERM hmac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); ERL_NIF_TERM hmac_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); ERL_NIF_TERM hmac_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +int hmac_low_level(ErlNifEnv* env, const EVP_MD *md, + ErlNifBinary key_bin, ErlNifBinary text, + ErlNifBinary *ret_bin, int *ret_bin_alloc, ERL_NIF_TERM *return_term); +#endif + #endif /* E_HMAC_H__ */ diff --git a/lib/crypto/c_src/mac.c b/lib/crypto/c_src/mac.c new file mode 100644 index 0000000000..149975ba9d --- /dev/null +++ b/lib/crypto/c_src/mac.c @@ -0,0 +1,751 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2019. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +#include "common.h" +#include "cipher.h" +#include "digest.h" +#include "cmac.h" +#include "hmac.h" +#include "mac.h" + +/*************************** + MAC type declaration +***************************/ + +struct mac_type_t { + union { + const char* str; /* before init, NULL for end-of-table */ + ERL_NIF_TERM atom; /* after init, 'false' for end-of-table */ + }name; + unsigned flags; + union { + const int pkey_type; + }alg; + int type; + size_t key_len; /* != 0 to also match on key_len */ +}; + +/* masks in the flags field if mac_type_t */ +#define NO_FIPS_MAC 1 + +#define NO_mac 0 +#define HMAC_mac 1 +#define CMAC_mac 2 +#define POLY1305_mac 3 + +static struct mac_type_t mac_types[] = +{ + {{"poly1305"}, NO_FIPS_MAC, +#ifdef HAVE_POLY1305 + /* If we have POLY then we have EVP_PKEY */ + {EVP_PKEY_POLY1305}, POLY1305_mac, 32 +#else + {EVP_PKEY_NONE}, NO_mac, 0 +#endif + }, + + {{"hmac"}, 0, +#ifdef HAS_EVP_PKEY_CTX + {EVP_PKEY_HMAC}, HMAC_mac, 0 +#else + /* HMAC is always supported, but possibly with low-level routines */ + {EVP_PKEY_NONE}, HMAC_mac, 0 +#endif + }, + + {{"cmac"}, 0, +#ifdef HAVE_CMAC + /* If we have CMAC then we have EVP_PKEY */ + {EVP_PKEY_CMAC}, CMAC_mac, 0 +#else + {EVP_PKEY_NONE}, NO_mac, 0 +#endif + }, + + /*==== End of list ==== */ + {{NULL}, 0, + {0}, NO_mac, 0 + } +}; + + +#ifdef FIPS_SUPPORT +/* May have FIPS support, must check dynamically if it is enabled */ +# define MAC_FORBIDDEN_IN_FIPS(P) (((P)->flags & NO_FIPS_MAC) && FIPS_mode()) +#else +/* No FIPS support since the symbol FIPS_SUPPORT is undefined */ +# define MAC_FORBIDDEN_IN_FIPS(P) 0 +#endif + + +/*************************** + Mandatory prototypes +***************************/ + +struct mac_type_t* get_mac_type(ERL_NIF_TERM type, size_t key_len); +struct mac_type_t* get_mac_type_no_key(ERL_NIF_TERM type); + +ERL_NIF_TERM mac_one_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +ERL_NIF_TERM mac_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + + +/******************************** + Support functions for type array +*********************************/ + +void init_mac_types(ErlNifEnv* env) +{ + struct mac_type_t* p = mac_types; + + for (p = mac_types; p->name.str; p++) { + p->name.atom = enif_make_atom(env, p->name.str); + } + p->name.atom = atom_false; /* end marker */ +} + + +ERL_NIF_TERM mac_types_as_list(ErlNifEnv* env) +{ + struct mac_type_t* p; + ERL_NIF_TERM prev, hd; + + hd = enif_make_list(env, 0); + prev = atom_undefined; + + for (p = mac_types; (p->name.atom & (p->name.atom != atom_false)); p++) { + if (prev == p->name.atom) + continue; + + if (p->type != NO_mac) + { + hd = enif_make_list_cell(env, p->name.atom, hd); + } + } + + return hd; +} + +struct mac_type_t* get_mac_type(ERL_NIF_TERM type, size_t key_len) +{ + struct mac_type_t* p = NULL; + for (p = mac_types; p->name.atom != atom_false; p++) { + if (type == p->name.atom) { + if ((p->key_len == 0) || (p->key_len == key_len)) + return p; + } + } + return NULL; +} + +struct mac_type_t* get_mac_type_no_key(ERL_NIF_TERM type) +{ + struct mac_type_t* p = NULL; + for (p = mac_types; p->name.atom != atom_false; p++) { + if (type == p->name.atom) { + return p; + } + } + return NULL; +} + +/******************************************************************* + * + * Mac nif + * + ******************************************************************/ +ERL_NIF_TERM mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (MacType, SubType, Key, Text) */ + ErlNifBinary text; + + if (!enif_inspect_iolist_as_binary(env, argv[3], &text)) + return EXCP_BADARG(env, "Bad text"); + + if (text.size > INT_MAX) + return EXCP_BADARG(env, "Too long text"); + + /* Run long jobs on a dirty scheduler to not block the current emulator thread */ + if (text.size > MAX_BYTES_TO_NIF) { + return enif_schedule_nif(env, "mac_one_time", + ERL_NIF_DIRTY_JOB_CPU_BOUND, + mac_one_time, argc, argv); + } + + return mac_one_time(env, argc, argv); +} + + + +ERL_NIF_TERM mac_one_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (MacType, SubType, Key, Text) */ + + struct mac_type_t *macp; + ErlNifBinary key_bin, text; + int ret_bin_alloc = 0; + ERL_NIF_TERM return_term; + const EVP_MD *md = NULL; + ErlNifBinary ret_bin; +#ifdef HAS_EVP_PKEY_CTX + size_t size; + EVP_PKEY *pkey = NULL; + EVP_MD_CTX *mctx = NULL; +#endif + + /*--------------------------------- + Get common indata and validate it + */ + if (!enif_inspect_iolist_as_binary(env, argv[2], &key_bin)) + { + return_term = EXCP_BADARG(env, "Bad key"); + goto err; + } + + if (!enif_inspect_iolist_as_binary(env, argv[3], &text)) + { + return_term = EXCP_BADARG(env, "Bad text"); + goto err; + } + + if (!(macp = get_mac_type(argv[0], key_bin.size))) + { + if (!get_mac_type_no_key(argv[0])) + return_term = EXCP_BADARG(env, "Unknown mac algorithm"); + else + return_term = EXCP_BADARG(env, "Bad key length"); + goto err; + } + + if (MAC_FORBIDDEN_IN_FIPS(macp)) + { + return_term = EXCP_NOTSUP(env, "MAC algorithm forbidden in FIPS"); + goto err; + } + + /*-------------------------------------------------- + Algorithm dependent indata checking and computation. + If EVP_PKEY is available, only set the pkey variable + and do the computation after the switch statement. + If not available, do the low-level calls in the + corresponding case part + */ + switch (macp->type) { + + /******** + * HMAC * + ********/ + case HMAC_mac: + { + struct digest_type_t *digp; + + if ((digp = get_digest_type(argv[1])) == NULL) + { + return_term = EXCP_BADARG(env, "Bad digest algorithm for HMAC"); + goto err; + } + if (digp->md.p == NULL) + { + return_term = EXCP_NOTSUP(env, "Unsupported digest algorithm"); + goto err; + } + if (DIGEST_FORBIDDEN_IN_FIPS(digp)) + { + return_term = EXCP_NOTSUP(env, "Digest algorithm for HMAC forbidden in FIPS"); + goto err; + } + md = digp->md.p; + +#ifdef HAS_EVP_PKEY_CTX +# ifdef HAVE_PKEY_new_raw_private_key + /* Prefered for new applications according to EVP_PKEY_new_mac_key(3) */ + pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_HMAC, /*engine*/ NULL, key_bin.data, key_bin.size); +# else + /* Available in older versions */ + pkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, /*engine*/ NULL, key_bin.data, key_bin.size); +# endif + +#else + if (!hmac_low_level(env, md, key_bin, text, &ret_bin, &ret_bin_alloc, &return_term)) + goto err; + else + goto success; +#endif + } + break; + + + /******** + * CMAC * + ********/ +#ifdef HAVE_CMAC + case CMAC_mac: + { + const struct cipher_type_t *cipherp; + if (!(cipherp = get_cipher_type(argv[1], key_bin.size))) + { /* Something went wrong. Find out what by retrying in another way. */ + if (!get_cipher_type_no_key(argv[1])) + return_term = EXCP_BADARG(env, "Unknown cipher"); + else + /* Cipher exists, so it must be the key size that is wrong */ + return_term = EXCP_BADARG(env, "Bad key size"); + goto err; + } + + if (CIPHER_FORBIDDEN_IN_FIPS(cipherp)) + { + return_term = EXCP_NOTSUP(env, "Cipher algorithm not supported in FIPS"); + goto err; + } + + if (cipherp->cipher.p == NULL) + { + return_term = EXCP_NOTSUP(env, "Unsupported cipher algorithm"); + goto err; + } + +# ifdef HAVE_EVP_PKEY_new_CMAC_key + pkey = EVP_PKEY_new_CMAC_key(/*engine*/ NULL, key_bin.data, key_bin.size, cipherp->cipher.p); +# else + if (!cmac_low_level(env, key_bin, cipherp->cipher.p, text, &ret_bin, &ret_bin_alloc, &return_term)) + goto err; + else + goto success; +# endif + } + break; +#endif /* HAVE_CMAC */ + + + /************ + * POLY1305 * + ************/ +#ifdef HAVE_POLY1305 + case POLY1305_mac: + /* poly1305 implies that EVP_PKEY_new_raw_private_key exists */ + pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_POLY1305, /*engine*/ NULL, key_bin.data, key_bin.size); + break; +#endif + + + /*************** + * Unknown MAC * + ***************/ + case NO_mac: + default: + /* We know that this mac is supported with some version(s) of cryptolib */ + return_term = EXCP_NOTSUP(env, "Unsupported mac algorithm"); + goto err; + } + + /*----------------------------------------- + Common computations when we have EVP_PKEY + */ +#ifdef HAS_EVP_PKEY_CTX + if (!pkey) + { + return_term = EXCP_ERROR(env, "EVP_PKEY_key creation"); + goto err; + } + + if ((mctx = EVP_MD_CTX_new()) == NULL) + { + return_term = EXCP_ERROR(env, "EVP_MD_CTX_new"); + goto err; + } + + if (EVP_DigestSignInit(mctx, /*&pctx*/ NULL, md, /*engine*/ NULL, pkey) != 1) + { + return_term = EXCP_ERROR(env, "EVP_DigestSign"); + goto err; + } + +# ifdef HAVE_DigestSign_as_single_op + if (EVP_DigestSign(mctx, NULL, &size, text.data, text.size) != 1) + { + return_term = EXCP_ERROR(env, "Can't get sign size"); + goto err; + } +# else + if (EVP_DigestSignUpdate(mctx, text.data, text.size) != 1) + { + return_term = EXCP_ERROR(env, "EVP_DigestSignUpdate"); + goto err; + } + + if (EVP_DigestSignFinal(mctx, NULL, &size) != 1) + { + return_term = EXCP_ERROR(env, "Can't get sign size"); + goto err; + } +# endif + + if (!enif_alloc_binary(size, &ret_bin)) + { + return_term = EXCP_ERROR(env, "Alloc binary"); + goto err; + } + ret_bin_alloc = 1; + +# ifdef HAVE_DigestSign_as_single_op + if (EVP_DigestSign(mctx, ret_bin.data, &size, text.data, text.size) != 1) +# else + if (EVP_DigestSignFinal(mctx, ret_bin.data, &size) != 1) +# endif + { + return_term = EXCP_ERROR(env, "Signing"); + goto err; + } + + goto success; /* The label "success:" could be left without any "goto success" + in some combination of flags. This prevents a compiler warning + */ +#endif /* ifdef HAS_EVP_PKEY_CTX */ + + + /**************************** + Exit when we got a signature + *****************************/ + success: + CONSUME_REDS(env, text); + + return_term = enif_make_binary(env, &ret_bin); + ret_bin_alloc = 0; + + err: + +#ifdef HAS_EVP_PKEY_CTX + if (pkey) + EVP_PKEY_free(pkey); + if (mctx) + EVP_MD_CTX_free(mctx); +#endif + + if (ret_bin_alloc) + enif_release_binary(&ret_bin); + + return return_term; +} + + +/******************************************************************* + * + * Mac ctx + * + ******************************************************************/ + +int init_mac_ctx(ErlNifEnv *env); + +struct mac_context +{ + EVP_MD_CTX *ctx; +}; + +static ErlNifResourceType* mac_context_rtype; + +static void mac_context_dtor(ErlNifEnv* env, struct mac_context*); + +int init_mac_ctx(ErlNifEnv *env) { + mac_context_rtype = enif_open_resource_type(env, NULL, "mac_context", + (ErlNifResourceDtor*) mac_context_dtor, + ERL_NIF_RT_CREATE|ERL_NIF_RT_TAKEOVER, + NULL); + if (mac_context_rtype == NULL) + goto err; + + return 1; + + err: + PRINTF_ERR0("CRYPTO: Could not open resource type 'mac_context'"); + return 0; +} + + +static void mac_context_dtor(ErlNifEnv* env, struct mac_context *obj) +{ + if (obj == NULL) + return; + + if (obj->ctx) + EVP_MD_CTX_free(obj->ctx); +} + +/******************************************************************* + * + * mac_init, mac_update, mac_final nifs + * + ******************************************************************/ + +ERL_NIF_TERM mac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (MacType, SubType, Key) */ +#ifdef HAS_EVP_PKEY_CTX + struct mac_context *obj = NULL; + struct mac_type_t *macp; + ErlNifBinary key_bin; + ERL_NIF_TERM return_term; + const EVP_MD *md = NULL; + EVP_PKEY *pkey = NULL; + + /*--------------------------------- + Get common indata and validate it + */ + if (!enif_inspect_iolist_as_binary(env, argv[2], &key_bin)) + { + return_term = EXCP_BADARG(env, "Bad key"); + goto err; + } + + if (!(macp = get_mac_type(argv[0], key_bin.size))) + { + if (!get_mac_type_no_key(argv[0])) + return_term = EXCP_BADARG(env, "Unknown mac algorithm"); + else + return_term = EXCP_BADARG(env, "Bad key length"); + goto err; + } + + if (MAC_FORBIDDEN_IN_FIPS(macp)) + { + return_term = EXCP_NOTSUP(env, "MAC algorithm forbidden in FIPS"); + goto err; + } + + /*-------------------------------------------------- + Algorithm dependent indata checking and computation. + If EVP_PKEY is available, only set the pkey variable + and do the computation after the switch statement. + If not available, do the low-level calls in the + corresponding case part + */ + switch (macp->type) { + + /******** + * HMAC * + ********/ + case HMAC_mac: + { + struct digest_type_t *digp; + + if ((digp = get_digest_type(argv[1])) == NULL) + { + return_term = EXCP_BADARG(env, "Bad digest algorithm for HMAC"); + goto err; + } + if (digp->md.p == NULL) + { + return_term = EXCP_NOTSUP(env, "Unsupported digest algorithm"); + goto err; + } + if (DIGEST_FORBIDDEN_IN_FIPS(digp)) + { + return_term = EXCP_NOTSUP(env, "Digest algorithm for HMAC forbidden in FIPS"); + goto err; + } + md = digp->md.p; + +# ifdef HAVE_PKEY_new_raw_private_key + /* Prefered for new applications according to EVP_PKEY_new_mac_key(3) */ + pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_HMAC, /*engine*/ NULL, key_bin.data, key_bin.size); +# else + /* Available in older versions */ + pkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, /*engine*/ NULL, key_bin.data, key_bin.size); +# endif + } + break; + + + /******** + * CMAC * + ********/ +#if defined(HAVE_CMAC) && defined(HAVE_EVP_PKEY_new_CMAC_key) + case CMAC_mac: + { + const struct cipher_type_t *cipherp; + if (!(cipherp = get_cipher_type(argv[1], key_bin.size))) + { /* Something went wrong. Find out what by retrying in another way. */ + if (!get_cipher_type_no_key(argv[1])) + return_term = EXCP_BADARG(env, "Unknown cipher"); + else + /* Cipher exists, so it must be the key size that is wrong */ + return_term = EXCP_BADARG(env, "Bad key size"); + goto err; + } + + if (CIPHER_FORBIDDEN_IN_FIPS(cipherp)) + { + return_term = EXCP_NOTSUP(env, "Cipher algorithm not supported in FIPS"); + goto err; + } + + if (cipherp->cipher.p == NULL) + { + return_term = EXCP_NOTSUP(env, "Unsupported cipher algorithm"); + goto err; + } + + pkey = EVP_PKEY_new_CMAC_key(/*engine*/ NULL, key_bin.data, key_bin.size, cipherp->cipher.p); + } + break; +#endif /* HAVE_CMAC && HAVE_EVP_PKEY_new_CMAC_key */ + + + /************ + * POLY1305 * + ************/ +#ifdef HAVE_POLY1305 + case POLY1305_mac: + /* poly1305 implies that EVP_PKEY_new_raw_private_key exists */ + pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_POLY1305, /*engine*/ NULL, key_bin.data, key_bin.size); + break; +#endif + + + /*************** + * Unknown MAC * + ***************/ + case NO_mac: + default: + /* We know that this mac is supported with some version(s) of cryptolib */ + return_term = EXCP_NOTSUP(env, "Unsupported mac algorithm"); + goto err; + } + + /*----------------------------------------- + Common computations + */ + if (!pkey) + { + return_term = EXCP_ERROR(env, "EVP_PKEY_key creation"); + goto err; + } + + if ((obj = enif_alloc_resource(mac_context_rtype, sizeof(struct mac_context))) == NULL) + { + return_term = EXCP_ERROR(env, "Can't allocate mac_context_rtype"); + goto err; + } + + if ((obj->ctx = EVP_MD_CTX_new()) == NULL) + { + return_term = EXCP_ERROR(env, "EVP_MD_CTX_new"); + goto err; + } + + if (EVP_DigestSignInit(obj->ctx, /*&pctx*/ NULL, md, /*engine*/ NULL, pkey) != 1) + { + return_term = EXCP_ERROR(env, "EVP_DigestSign"); + goto err; + } + + return_term = enif_make_resource(env, obj); + + err: + + if (obj) + enif_release_resource(obj); + + if (pkey) + EVP_PKEY_free(pkey); + + return return_term; + +#else + if (argv[0] != atom_hmac) + return EXCP_NOTSUP(env, "Unsupported mac algorithm"); + + return hmac_init_nif(env, argc, argv); +#endif +} + + + +ERL_NIF_TERM mac_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Ref, Text) */ + ErlNifBinary text; + + if (!enif_inspect_iolist_as_binary(env, argv[1], &text)) + return EXCP_BADARG(env, "Bad text"); + + if (text.size > INT_MAX) + return EXCP_BADARG(env, "Too long text"); + + /* Run long jobs on a dirty scheduler to not block the current emulator thread */ + if (text.size > MAX_BYTES_TO_NIF) { + return enif_schedule_nif(env, "mac_update", + ERL_NIF_DIRTY_JOB_CPU_BOUND, + mac_update, argc, argv); + } + + return mac_update(env, argc, argv); +} + + +ERL_NIF_TERM mac_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Ref, Text) */ +#ifdef HAS_EVP_PKEY_CTX + struct mac_context *obj = NULL; + ErlNifBinary text; + + if (!enif_get_resource(env, argv[0], (ErlNifResourceType*)mac_context_rtype, (void**)&obj)) + return EXCP_BADARG(env, "Bad ref"); + + if (!enif_inspect_iolist_as_binary(env, argv[1], &text)) + return EXCP_BADARG(env, "Bad text"); + + if (EVP_DigestSignUpdate(obj->ctx, text.data, text.size) != 1) + return EXCP_ERROR(env, "EVP_DigestSignUpdate"); + + CONSUME_REDS(env, text); + return argv[0]; + +#else + return hmac_update_nif(env, argc, argv); +#endif +} + + + +ERL_NIF_TERM mac_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Ref) */ +#ifdef HAS_EVP_PKEY_CTX + struct mac_context *obj; + size_t size; + ErlNifBinary ret_bin; + + if (!enif_get_resource(env, argv[0], (ErlNifResourceType*)mac_context_rtype, (void**)&obj)) + return EXCP_BADARG(env, "Bad ref"); + + if (EVP_DigestSignFinal(obj->ctx, NULL, &size) != 1) + return EXCP_ERROR(env, "Can't get sign size"); + + if (!enif_alloc_binary(size, &ret_bin)) + return EXCP_ERROR(env, "Alloc binary"); + + if (EVP_DigestSignFinal(obj->ctx, ret_bin.data, &size) != 1) + { + enif_release_binary(&ret_bin); + return EXCP_ERROR(env, "Signing"); + } + + return enif_make_binary(env, &ret_bin); + +#else + return hmac_final_nif(env, argc, argv); +#endif +} + diff --git a/lib/crypto/c_src/poly1305.h b/lib/crypto/c_src/mac.h index 4bf45e6218..053a331324 100644 --- a/lib/crypto/c_src/poly1305.h +++ b/lib/crypto/c_src/mac.h @@ -18,11 +18,21 @@ * %CopyrightEnd% */ -#ifndef E_POLY1305_H__ -#define E_POLY1305_H__ 1 +#ifndef E_MAC_H__ +#define E_MAC_H__ 1 #include "common.h" -ERL_NIF_TERM poly1305_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +int init_mac_ctx(ErlNifEnv *env); -#endif /* E_POLY1305_H__ */ +void init_mac_types(ErlNifEnv* env); + +ERL_NIF_TERM mac_types_as_list(ErlNifEnv* env); + +ERL_NIF_TERM mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +ERL_NIF_TERM mac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM mac_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM mac_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_MAC_H__ */ diff --git a/lib/crypto/c_src/openssl_config.h b/lib/crypto/c_src/openssl_config.h index 339eb5b8f4..32a0830717 100644 --- a/lib/crypto/c_src/openssl_config.h +++ b/lib/crypto/c_src/openssl_config.h @@ -110,6 +110,12 @@ # define HAS_EVP_PKEY_CTX # define HAVE_EVP_CIPHER_CTX_COPY # endif + +# if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,1,1) +# define HAVE_PKEY_new_raw_private_key +# define HAVE_EVP_PKEY_new_CMAC_key +# define HAVE_DigestSign_as_single_op +# endif #endif diff --git a/lib/crypto/c_src/pkey.c b/lib/crypto/c_src/pkey.c index a1e2677b34..d53d91c25b 100644 --- a/lib/crypto/c_src/pkey.c +++ b/lib/crypto/c_src/pkey.c @@ -59,8 +59,9 @@ static int get_pkey_public_key(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_T EVP_PKEY **pkey); static int get_pkey_crypt_options(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_TERM options, PKeyCryptOptions *opt); +#ifdef HAVE_RSA_SSLV23_PADDING static size_t size_of_RSA(EVP_PKEY *pkey); - +#endif static int get_pkey_digest_type(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_TERM type, const EVP_MD **md) @@ -1031,6 +1032,7 @@ static int get_pkey_crypt_options(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NI return PKEY_BADARG; } +#ifdef HAVE_RSA_SSLV23_PADDING static size_t size_of_RSA(EVP_PKEY *pkey) { int ret = 0; RSA *rsa = NULL; @@ -1045,6 +1047,7 @@ static size_t size_of_RSA(EVP_PKEY *pkey) { return (ret < 0) ? 0 : (size_t)ret; } +#endif ERL_NIF_TERM pkey_crypt_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {/* (Algorithm, Data, PublKey=[E,N]|[E,N,D]|[E,N,D,P1,P2,E1,E2,C], Options, IsPrivate, IsEncrypt) */ diff --git a/lib/crypto/c_src/poly1305.c b/lib/crypto/c_src/poly1305.c deleted file mode 100644 index 76579c0a29..0000000000 --- a/lib/crypto/c_src/poly1305.c +++ /dev/null @@ -1,90 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 2010-2018. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * %CopyrightEnd% - */ - -#include "poly1305.h" - -/* For OpenSSL >= 1.1.1 the hmac_nif and cmac_nif could be integrated into poly1305 (with 'type' as parameter) */ -ERL_NIF_TERM poly1305_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Key, Text) */ -#ifdef HAVE_POLY1305 - ErlNifBinary key_bin, text, ret_bin; - ERL_NIF_TERM ret; - EVP_PKEY *key = NULL; - EVP_MD_CTX *mctx = NULL; - EVP_PKEY_CTX *pctx = NULL; - const EVP_MD *md = NULL; - size_t size; - int ret_bin_alloc = 0; - - ASSERT(argc == 2); - - if (!enif_inspect_binary(env, argv[0], &key_bin)) - goto bad_arg; - if (key_bin.size != 32) - goto bad_arg; - if (!enif_inspect_binary(env, argv[1], &text)) - goto bad_arg; - - if ((key = EVP_PKEY_new_raw_private_key(EVP_PKEY_POLY1305, /*engine*/ NULL, key_bin.data, key_bin.size)) == NULL) - goto err; - - if ((mctx = EVP_MD_CTX_new()) == NULL) - goto err; - if (EVP_DigestSignInit(mctx, &pctx, md, /*engine*/ NULL, key) != 1) - goto err; - if (EVP_DigestSignUpdate(mctx, text.data, text.size) != 1) - goto err; - - if (EVP_DigestSignFinal(mctx, NULL, &size) != 1) - goto err; - if (!enif_alloc_binary(size, &ret_bin)) - goto err; - ret_bin_alloc = 1; - if (EVP_DigestSignFinal(mctx, ret_bin.data, &size) != 1) - goto err; - - if (size != ret_bin.size) { - if (!enif_realloc_binary(&ret_bin, size)) - goto err; - } - - ret = enif_make_binary(env, &ret_bin); - ret_bin_alloc = 0; - goto done; - - bad_arg: - return enif_make_badarg(env); - - err: - if (ret_bin_alloc) - enif_release_binary(&ret_bin); - ret = atom_error; - - done: - if (mctx) - EVP_MD_CTX_free(mctx); - if (key) - EVP_PKEY_free(key); - return ret; - -#else - return enif_raise_exception(env, atom_notsup); -#endif -} diff --git a/lib/crypto/doc/src/algorithm_details.xml b/lib/crypto/doc/src/algorithm_details.xml index 854bfbb4b1..71014764c8 100644 --- a/lib/crypto/doc/src/algorithm_details.xml +++ b/lib/crypto/doc/src/algorithm_details.xml @@ -37,122 +37,163 @@ <section> <title>Ciphers</title> + <p>A <seealso marker="crypto#type-cipher">cipher</seealso> in the + <seealso marker="crypto:new_api#the-new-api">new api</seealso> + is categorized as either + <seealso marker="crypto#type-cipher_no_iv">cipher_no_iv()</seealso>, + <seealso marker="crypto#type-cipher_iv">cipher_iv()</seealso> or + <seealso marker="crypto#type-cipher_aead">cipher_aead()</seealso>. + The letters IV are short for <i>Initialization Vector</i> and + AEAD is an abreviation of <i>Authenticated Encryption with Associated Data</i>. + </p> + <p>Due to irregular naming conventions, some cipher names in the old api are + substitued by new names in the new api. For a list of retired names, see + <seealso marker="crypto:new_api#retired-cipher-names">Retired cipher names</seealso>. + </p> + <p>To dynamically check availability, check that the name in the <i>Cipher and Mode</i> column is present in the + list returned by <seealso marker="crypto#supports-1">crypto:supports(ciphers)</seealso>. + </p> + <section> - <title>Block Ciphers</title> - <p>To be used in - <seealso marker="crypto#block_encrypt-3">block_encrypt/3</seealso>, - <seealso marker="crypto#block_encrypt-4">block_encrypt/4</seealso>, - <seealso marker="crypto#block_decrypt-3">block_decrypt/3</seealso> and - <seealso marker="crypto#block_decrypt-4">block_decrypt/4</seealso>. - </p> - <p>Available in all OpenSSL compatible with Erlang CRYPTO if not disabled by configuration. + <title>Ciphers without an IV - cipher_no_iv()</title> + <p>To be used with: </p> - <p>To dynamically check availability, check that the name in the <i>Cipher and Mode</i> column is present in the - list with the <c>cipher</c> tag in the return value of - <seealso marker="crypto#supports-0">crypto:supports()</seealso>. + <list> + <item><seealso marker="crypto#crypto_one_time/4">crypto_one_time/4</seealso></item> + <item><seealso marker="crypto#crypto_init/3">crypto_init/3</seealso></item> + </list> + <p>The ciphers are: </p> <table> - <row><cell><strong>Cipher and Mode</strong></cell><cell><strong>Key length</strong><br/><strong>[bytes]</strong></cell><cell><strong>IV length</strong><br/><strong>[bytes]</strong></cell><cell><strong>Block size</strong><br/><strong>[bytes]</strong></cell></row> - <row><cell><c>aes_cbc</c></cell> <cell>16, 24, 32</cell><cell>16</cell><cell>16</cell></row> - <row><cell><c>aes_cbc128</c></cell><cell>16</cell><cell>16</cell><cell>16</cell></row> - <row><cell><c>aes_cbc256</c></cell><cell>32</cell><cell>16</cell><cell>16</cell></row> - - <row><cell><c>aes_cfb8</c></cell> <cell>16, 24, 32</cell><cell>16</cell><cell>any</cell></row> - - <row><cell><c>aes_ecb</c></cell><cell>16, 24, 32</cell><cell> </cell><cell>16</cell></row> - - <row><cell><c>aes_ige256</c></cell><cell>16</cell><cell>32</cell><cell>16</cell></row> - <row><cell><c>blowfish_cbc</c></cell> <cell>4-56</cell> <cell>8</cell> <cell>8</cell></row> - <row><cell><c>blowfish_cfb64</c></cell> <cell>≥1</cell> <cell>8</cell> <cell>any</cell></row> - <row><cell><c>blowfish_ecb</c></cell><cell>≥1</cell><cell> </cell><cell>8</cell></row> - <row><cell><c>blowfish_ofb64</c></cell><cell>≥1</cell><cell>8</cell><cell>any</cell></row> - - <row><cell><c>des3_cbc</c><br/><i>(=DES EDE3 CBC)</i></cell><cell>[8,8,8]</cell><cell>8</cell><cell>8</cell></row> - <row><cell><c>des3_cfb</c><br/><i>(=DES EDE3 CFB)</i></cell><cell>[8,8,8]</cell><cell>8</cell><cell>any</cell></row> - - <row><cell><c>des_cbc</c></cell><cell>8</cell><cell>8</cell> <cell>8</cell></row> - <row><cell><c>des_cfb</c></cell><cell>8</cell><cell>8</cell><cell>any</cell></row> - <row><cell><c>des_ecb</c></cell><cell>8</cell><cell> </cell><cell>8</cell></row> - <row><cell><c>des_ede3</c><br/><i>(=DES EDE3 CBC)</i></cell><cell>[8,8,8]</cell><cell>8</cell><cell>8</cell></row> - <row><cell><c>rc2_cbc</c></cell><cell>≥1</cell><cell>8</cell><cell>8</cell></row> - <tcaption>Block cipher key lengths</tcaption> + <row> + <cell><strong>Cipher and Mode</strong></cell> + <cell><strong>Key length</strong><br/><strong>[bytes]</strong></cell> + <cell><strong>Block size</strong><br/><strong>[bytes]</strong></cell> + </row> + <row><cell><c>aes_128_ecb</c></cell> <cell>16</cell> <cell>16</cell></row> + <row><cell><c>aes_192_ecb</c></cell> <cell>24</cell> <cell>16</cell></row> + <row><cell><c>aes_256_ecb</c></cell> <cell>32</cell> <cell>16</cell></row> + <row><cell><c>blowfish_ecb</c></cell> <cell>16</cell> <cell> 8</cell></row> + <row><cell><c>des_ecb</c></cell> <cell> 8</cell> <cell> 8</cell></row> + <row><cell><c>rc4</c></cell> <cell>16</cell> <cell> 1</cell></row> + <tcaption>Ciphers without IV</tcaption> </table> </section> <section> - <title>AEAD Ciphers</title> - <p>To be used in <seealso marker="crypto#block_encrypt-4">block_encrypt/4</seealso> and - <seealso marker="crypto#block_decrypt-4">block_decrypt/4</seealso>. + <title>Ciphers with an IV - cipher_iv()</title> + <p>To be used with: </p> - <p>To dynamically check availability, check that the name in the <i>Cipher and Mode</i> column is present in the - list with the <c>cipher</c> tag in the return value of - <seealso marker="crypto#supports-0">crypto:supports()</seealso>. + <list> + <item><seealso marker="crypto#crypto_one_time/5">crypto_one_time/5</seealso></item> + <item><seealso marker="crypto#crypto_init/4">crypto_init/4</seealso></item> + <item><seealso marker="crypto#crypto_dyn_iv_init/3">crypto_dyn_iv_init/3</seealso></item> + </list> + <p>The ciphers are: </p> <table> - <row><cell><strong>Cipher and Mode</strong></cell><cell><strong>Key length</strong><br/><strong>[bytes]</strong></cell><cell><strong>IV length</strong><br/><strong>[bytes]</strong></cell><cell><strong>AAD length</strong><br/><strong>[bytes]</strong></cell><cell><strong>Tag length</strong><br/><strong>[bytes]</strong></cell><cell><strong>Block size</strong><br/><strong>[bytes]</strong></cell><cell><strong>Supported with</strong><br/><strong>OpenSSL versions</strong></cell></row> - <row><cell><c>aes_ccm</c></cell> <cell>16,24,32</cell> <cell>7-13</cell> <cell>any</cell> <cell>even 4-16<br/>default: 12</cell> <cell>any</cell><cell>≥1.1.0</cell></row> - <row><cell><c>aes_gcm</c></cell> <cell>16,24,32</cell> <cell>≥1</cell> <cell>any</cell> <cell>1-16<br/>default: 16</cell> <cell>any</cell><cell>≥1.1.0</cell></row> - <row><cell><c>chacha20_poly1305</c></cell><cell>32</cell> <cell>1-16</cell> <cell>any</cell> <cell>16</cell> <cell>any</cell><cell>≥1.1.0</cell></row> - <tcaption>AEAD cipher key lengths</tcaption> + <row> + <cell><strong>Cipher and Mode</strong></cell> + <cell><strong>Key length</strong><br/><strong>[bytes]</strong></cell> + <cell><strong>IV length</strong><br/><strong>[bytes]</strong></cell> + <cell><strong>Block size</strong><br/><strong>[bytes]</strong></cell> + <cell><strong>Limited to</strong><br/><strong>OpenSSL versions</strong></cell> + </row> + <row><cell><c>aes_128_cbc</c></cell> <cell>16</cell> <cell>16</cell> <cell>16</cell> <cell></cell></row> + <row><cell><c>aes_192_cbc</c></cell> <cell>24</cell> <cell>16</cell> <cell>16</cell> <cell></cell></row> + <row><cell><c>aes_256_cbc</c></cell> <cell>32</cell> <cell>16</cell> <cell>16</cell> <cell></cell></row> + <row><cell><c>aes_128_cfb8</c></cell> <cell>16</cell> <cell>16</cell> <cell> 1</cell> <cell></cell></row> + <row><cell><c>aes_192_cfb8</c></cell> <cell>24</cell> <cell>16</cell> <cell> 1</cell> <cell></cell></row> + <row><cell><c>aes_256_cfb8</c></cell> <cell>32</cell> <cell>16</cell> <cell> 1</cell> <cell></cell></row> + <row><cell><c>aes_128_cfb128</c></cell><cell>16</cell> <cell>16</cell> <cell> 1</cell> <cell></cell></row> + <row><cell><c>aes_192_cfb128</c></cell><cell>24</cell> <cell>16</cell> <cell> 1</cell> <cell></cell></row> + <row><cell><c>aes_256_cfb128</c></cell><cell>32</cell> <cell>16</cell> <cell> 1</cell> <cell></cell></row> + <row><cell><c>aes_128_ctr</c></cell> <cell>16</cell> <cell>16</cell> <cell> 1</cell> <cell></cell></row> + <row><cell><c>aes_192_ctr</c></cell> <cell>24</cell> <cell>16</cell> <cell> 1</cell> <cell></cell></row> + <row><cell><c>aes_256_ctr</c></cell> <cell>32</cell> <cell>16</cell> <cell> 1</cell> <cell></cell></row> + <row><cell><c>aes_ige256</c></cell> <cell>16</cell> <cell>32</cell> <cell>16</cell> <cell></cell></row> + <row><cell><c>blowfish_cbc</c></cell> <cell>16</cell> <cell> 8</cell> <cell> 8</cell> <cell></cell></row> + <row><cell><c>blowfish_cfb64</c></cell><cell>16</cell> <cell> 8</cell> <cell> 1</cell> <cell></cell></row> + <row><cell><c>blowfish_ofb64</c></cell><cell>16</cell> <cell> 8</cell> <cell> 1</cell> <cell></cell></row> + <row><cell><c>chacha20</c></cell> <cell>32</cell> <cell>16</cell> <cell> 1</cell> <cell>≥1.1.0d</cell></row> + <row><cell><c>des_cbc</c></cell> <cell> 8</cell> <cell> 8</cell> <cell> 8</cell> <cell></cell></row> + <row><cell><c>des_ede3_cbc</c></cell> <cell>24</cell> <cell> 8</cell> <cell> 8</cell> <cell></cell></row> + <row><cell><c>des_cfb</c></cell> <cell> 8</cell> <cell> 8</cell> <cell> 1</cell> <cell></cell></row> + <row><cell><c>des_ede3_cfb</c></cell> <cell>24</cell> <cell> 8</cell> <cell> 1</cell> <cell></cell></row> + <row><cell><c>rc2_cbc</c></cell> <cell>16</cell> <cell> 8</cell> <cell> 8</cell> <cell></cell></row> + <tcaption>Ciphers with IV</tcaption> </table> </section> <section> - <title>Stream Ciphers</title> - <p>To be used in <seealso marker="crypto#stream_init-2">stream_init/2</seealso> and - <seealso marker="crypto#stream_init/3">stream_init/3</seealso>. + <title>Ciphers with AEAD - cipher_aead()</title> + <p>To be used with: </p> - <p>To dynamically check availability, check that the name in the <i>Cipher and Mode</i> column is present in the - list with the <c>cipher</c> tag in the return value of - <seealso marker="crypto#supports-0">crypto:supports()</seealso>. + <list> + <item><seealso marker="crypto#crypto_one_time_aead/6">crypto_one_time_aead/6</seealso></item> + <item><seealso marker="crypto#crypto_one_time_aead/7">crypto_one_time_aead/7</seealso></item> + </list> + <p>The ciphers are: </p> <table> - <row><cell><strong>Cipher and Mode</strong></cell><cell><strong>Key length</strong><br/><strong>[bytes]</strong></cell><cell><strong>IV length</strong><br/><strong>[bytes]</strong></cell><cell><strong>Supported with</strong><br/><strong>OpenSSL versions</strong></cell></row> - <row><cell><c>aes_ctr</c></cell><cell>16, 24, 32</cell><cell>16</cell><cell>≥1.0.1</cell></row> - <row><cell><c>rc4</c></cell><cell>≥1</cell><cell> </cell> <cell>all</cell></row> - <tcaption>Stream cipher key lengths</tcaption> + <row> + <cell><strong>Cipher and Mode</strong></cell> + <cell><strong>Key length</strong><br/><strong>[bytes]</strong></cell> + <cell><strong>IV length</strong><br/><strong>[bytes]</strong></cell> + <cell><strong>AAD length</strong><br/><strong>[bytes]</strong></cell> + <cell><strong>Tag length</strong><br/><strong>[bytes]</strong></cell> + <cell><strong>Block size</strong><br/><strong>[bytes]</strong></cell> + <cell><strong>Limited to</strong><br/><strong>OpenSSL versions</strong></cell> + </row> + <row><cell><c>aes_128_ccm</c></cell> <cell>16</cell> <cell>7-13</cell> <cell>any</cell> <cell>even 4-16<br/>default: 12</cell> <cell>any</cell><cell>≥1.0.1</cell></row> + <row><cell><c>aes_192_ccm</c></cell> <cell>24</cell> <cell>7-13</cell> <cell>any</cell> <cell>even 4-16<br/>default: 12</cell> <cell>any</cell><cell>≥1.0.1</cell></row> + <row><cell><c>aes_256_ccm</c></cell> <cell>32</cell> <cell>7-13</cell> <cell>any</cell> <cell>even 4-16<br/>default: 12</cell> <cell>any</cell><cell>≥1.0.1</cell></row> + + <row><cell><c>aes_128_gcm</c></cell> <cell>16</cell> <cell>≥1</cell> <cell>any</cell> <cell>1-16<br/>default: 16</cell> <cell>any</cell><cell>≥1.0.1</cell></row> + <row><cell><c>aes_192_gcm</c></cell> <cell>24</cell> <cell>≥1</cell> <cell>any</cell> <cell>1-16<br/>default: 16</cell> <cell>any</cell><cell>≥1.0.1</cell></row> + <row><cell><c>aes_256_gcm</c></cell> <cell>32</cell> <cell>≥1</cell> <cell>any</cell> <cell>1-16<br/>default: 16</cell> <cell>any</cell><cell>≥1.0.1</cell></row> + + <row><cell><c>chacha20_poly1305</c></cell><cell>32</cell> <cell>1-16</cell> <cell>any</cell> <cell>16</cell> <cell>any</cell><cell>≥1.1.0</cell></row> + <tcaption>AEAD ciphers</tcaption> </table> </section> </section> + <section> <title>Message Authentication Codes (MACs)</title> + <p>To be used in <seealso marker="crypto#mac-4">mac/4</seealso> and + <seealso marker="crypto:new_api#macs--message-authentication-codes-">related functions</seealso>. + </p> <section> <title>CMAC</title> - <p>To be used in <seealso marker="crypto#cmac-3">cmac/3</seealso> and - <seealso marker="crypto#cmac-3">cmac/4</seealso>. - </p> <p>CMAC with the following ciphers are available with OpenSSL 1.0.1 or later if not disabled by configuration. </p> <p>To dynamically check availability, check that the name <c>cmac</c> is present in the - list with the <c>macs</c> tag in the return value of - <seealso marker="crypto#supports-0">crypto:supports()</seealso>. + list returned by <seealso marker="crypto#supports-1">crypto:supports(macs)</seealso>. Also check that the name in the <i>Cipher and Mode</i> column is present in the - list with the <c>cipher</c> tag in the return value. + list returned by <seealso marker="crypto#supports-1">crypto:supports(ciphers)</seealso>. </p> <table> - <row><cell><strong>Cipher and Mode</strong></cell><cell><strong>Key length</strong><br/><strong>[bytes]</strong></cell><cell><strong>Max Mac Length</strong><br/><strong>[bytes]</strong></cell></row> - <row><cell><c>aes_cbc</c></cell> <cell>16, 24, 32</cell><cell>16</cell></row> - <row><cell><c>aes_cbc128</c></cell><cell>16</cell><cell>16</cell></row> - <row><cell><c>aes_cbc256</c></cell><cell>32</cell><cell>16</cell></row> - - <row><cell><c>aes_cfb8</c></cell> <cell>16</cell><cell>1</cell></row> - - <row><cell><c>blowfish_cbc</c></cell> <cell>4-56</cell> <cell>8</cell></row> - <row><cell><c>blowfish_cfb64</c></cell> <cell>≥1</cell> <cell>1</cell></row> - <row><cell><c>blowfish_ecb</c></cell><cell>≥1</cell> <cell>8</cell></row> - <row><cell><c>blowfish_ofb64</c></cell><cell>≥1</cell> <cell>1</cell></row> - - <row><cell><c>des3_cbc</c><br/><i>(=DES EDE3 CBC)</i></cell><cell>[8,8,8]</cell><cell>8</cell></row> - <row><cell><c>des3_cfb</c><br/><i>(=DES EDE3 CFB)</i></cell><cell>[8,8,8]</cell><cell>1</cell></row> - - <row><cell><c>des_cbc</c></cell><cell>8</cell><cell>8</cell></row> - - <row><cell><c>des_cfb</c></cell><cell>8</cell><cell>1</cell></row> - <row><cell><c>des_ecb</c></cell><cell>8</cell><cell>1</cell></row> - <row><cell><c>rc2_cbc</c></cell><cell>≥1</cell><cell>8</cell></row> + <row> + <cell><strong>Cipher and Mode</strong></cell> + <cell><strong>Key length</strong><br/><strong>[bytes]</strong></cell> + <cell><strong>Max Mac Length</strong><br/><strong>(= default length)</strong><br/><strong>[bytes]</strong></cell> + </row> + <row><cell><c>aes_128_cbc</c></cell> <cell>16</cell> <cell>16</cell></row> + <row><cell><c>aes_192_cbc</c></cell> <cell>24</cell> <cell>16</cell></row> + <row><cell><c>aes_256_cbc</c></cell> <cell>32</cell> <cell>16</cell></row> + <row><cell><c>aes_128_ecb</c></cell> <cell>16</cell> <cell>16</cell></row> + <row><cell><c>aes_192_ecb</c></cell> <cell>24</cell> <cell>16</cell></row> + <row><cell><c>aes_256_ecb</c></cell> <cell>32</cell> <cell>16</cell></row> + <row><cell><c>blowfish_cbc</c></cell> <cell>16</cell> <cell> 8</cell></row> + <row><cell><c>blowfish_ecb</c></cell> <cell>16</cell> <cell> 8</cell></row> + <row><cell><c>des_cbc</c></cell> <cell> 8</cell> <cell> 8</cell></row> + <row><cell><c>des_ecb</c></cell> <cell> 8</cell> <cell> 8</cell></row> + <row><cell><c>des_ede3_cbc</c></cell> <cell>24</cell> <cell> 8</cell></row> + <row><cell><c>rc2_cbc</c></cell> <cell>16</cell> <cell> 8</cell></row> <tcaption>CMAC cipher key lengths</tcaption> </table> </section> @@ -162,9 +203,34 @@ <p>Available in all OpenSSL compatible with Erlang CRYPTO if not disabled by configuration. </p> <p>To dynamically check availability, check that the name <c>hmac</c> is present in the - list with the <c>macs</c> tag in the return value of - <seealso marker="crypto#supports-0">crypto:supports()</seealso>. + list returned by <seealso marker="crypto#supports-1">crypto:supports(macs)</seealso> and + that the hash name is present in the + list returned by <seealso marker="crypto#supports-1">crypto:supports(hashs)</seealso>. </p> + + <table> + <row> + <cell><strong>Hash</strong></cell> + <cell><strong>Max Mac Length</strong><br/><strong>(= default length)</strong><br/><strong>[bytes]</strong></cell> + </row> + <row><cell><c>sha</c></cell> <cell>20</cell></row> + <row><cell><c>sha224</c></cell> <cell>28</cell></row> + <row><cell><c>sha256</c></cell> <cell>32</cell></row> + <row><cell><c>sha384</c></cell> <cell>48</cell></row> + <row><cell><c>sha512</c></cell> <cell>64</cell></row> + <row><cell><c>sha3_224</c></cell> <cell>28</cell></row> + <row><cell><c>sha3_256</c></cell> <cell>32</cell></row> + <row><cell><c>sha3_384</c></cell> <cell>48</cell></row> + <row><cell><c>sha3_512</c></cell> <cell>64</cell></row> + <row><cell><c>blake2b</c></cell> <cell>64</cell></row> + <row><cell><c>blake2s</c></cell> <cell>32</cell></row> + <row><cell><c>md4</c></cell> <cell>16</cell></row> + <row><cell><c>md5</c></cell> <cell>16</cell></row> + <row><cell><c>ripemd160</c></cell> <cell>20</cell></row> + <tcaption>HMAC output sizes</tcaption> + </table> + + </section> <section> @@ -172,8 +238,9 @@ <p>POLY1305 is available with OpenSSL 1.1.1 or later if not disabled by configuration. </p> <p>To dynamically check availability, check that the name <c>poly1305</c> is present in the - list with the <c>macs</c> tag in the return value of - <seealso marker="crypto#supports-0">crypto:supports()</seealso>. + list returned by <seealso marker="crypto#supports-1">crypto:supports(macs)</seealso>. + </p> + <p>The poly1305 mac wants an 32 bytes key and produces a 16 byte MAC by default. </p> </section> @@ -183,22 +250,20 @@ <title>Hash</title> <p>To dynamically check availability, check that the wanted name in the <i>Names</i> column is present in the - list with the <c>hashs</c> tag in the return value of - <seealso marker="crypto#supports-0">crypto:supports()</seealso>. + list returned by <seealso marker="crypto#supports-1">crypto:supports(hashs)</seealso>. </p> - <table> <row><cell><strong>Type</strong></cell> <cell><strong>Names</strong></cell> - <cell><strong>Supported with</strong><br/><strong>OpenSSL versions</strong></cell> + <cell><strong>Limitated to</strong><br/><strong>OpenSSL versions</strong></cell> </row> - <row><cell>SHA1</cell><cell>sha</cell><cell>all</cell></row> - <row><cell>SHA2</cell><cell>sha224, sha256, sha384, sha512</cell><cell>all</cell></row> + <row><cell>SHA1</cell><cell>sha</cell><cell></cell></row> + <row><cell>SHA2</cell><cell>sha224, sha256, sha384, sha512</cell><cell></cell></row> <row><cell>SHA3</cell><cell>sha3_224, sha3_256, sha3_384, sha3_512</cell><cell>≥1.1.1</cell></row> - <row><cell>MD4</cell><cell>md4</cell><cell>all</cell></row> - <row><cell>MD5</cell><cell>md5</cell><cell>all</cell></row> - <row><cell>RIPEMD</cell><cell>ripemd160</cell><cell>all</cell></row> + <row><cell>MD4</cell><cell>md4</cell><cell></cell></row> + <row><cell>MD5</cell><cell>md5</cell><cell></cell></row> + <row><cell>RIPEMD</cell><cell>ripemd160</cell><cell></cell></row> <tcaption></tcaption> </table> </section> @@ -210,8 +275,7 @@ <title>RSA</title> <p>RSA is available with all OpenSSL versions compatible with Erlang CRYPTO if not disabled by configuration. To dynamically check availability, check that the atom <c>rsa</c> is present in the - list with the <c>public_keys</c> tag in the return value of - <seealso marker="crypto#supports-0">crypto:supports()</seealso>. + list returned by <seealso marker="crypto#supports-1">crypto:supports(public_keys)</seealso>. </p> <warning> <!-- In RefMan rsa_opt(), rsa_sign_verify_opt() and User's man RSA --> @@ -283,8 +347,7 @@ <title>DSS</title> <p>DSS is available with OpenSSL versions compatible with Erlang CRYPTO if not disabled by configuration. To dynamically check availability, check that the atom <c>dss</c> is present in the - list with the <c>public_keys</c> tag in the return value of - <seealso marker="crypto#supports-0">crypto:supports()</seealso>. + list returned by <seealso marker="crypto#supports-1">crypto:supports(public_keys)</seealso>. </p> </section> @@ -292,13 +355,11 @@ <title>ECDSA</title> <p>ECDSA is available with OpenSSL 0.9.8o or later if not disabled by configuration. To dynamically check availability, check that the atom <c>ecdsa</c> is present in the - list with the <c>public_keys</c> tag in the return value of - <seealso marker="crypto#supports-0">crypto:supports()</seealso>. - If the atom <c>ec_gf2m</c> characteristic two field curves are available. + list returned by <seealso marker="crypto#supports-1">crypto:supports(public_keys)</seealso>. + If the atom <c>ec_gf2m</c> also is present, the characteristic two field curves are available. </p> - <p>The actual supported named curves could be checked by examining the list with the - <c>curves</c> tag in the return value of - <seealso marker="crypto#supports-0">crypto:supports()</seealso>. + <p>The actual supported named curves could be checked by examining the + list returned by <seealso marker="crypto#supports-1">crypto:supports(curves)</seealso>. </p> </section> @@ -306,13 +367,11 @@ <title>EdDSA</title> <p>EdDSA is available with OpenSSL 1.1.1 or later if not disabled by configuration. To dynamically check availability, check that the atom <c>eddsa</c> is present in the - list with the <c>public_keys</c> tag in the return value of - <seealso marker="crypto#supports-0">crypto:supports()</seealso>. + list returned by <seealso marker="crypto#supports-1">crypto:supports(public_keys)</seealso>. </p> <p>Support for the curves ed25519 and ed448 is implemented. The actual supported named curves could be checked by examining the list with the - <c>curves</c> tag in the return value of - <seealso marker="crypto#supports-0">crypto:supports()</seealso>. + list returned by <seealso marker="crypto#supports-1">crypto:supports(curves)</seealso>. </p> </section> @@ -321,8 +380,7 @@ <p>Diffie-Hellman computations are available with OpenSSL versions compatible with Erlang CRYPTO if not disabled by configuration. To dynamically check availability, check that the atom <c>dh</c> is present in the - list with the <c>public_keys</c> tag in the return value of - <seealso marker="crypto#supports-0">crypto:supports()</seealso>. + list returned by <seealso marker="crypto#supports-1">crypto:supports(public_keys)</seealso>. </p> </section> @@ -330,17 +388,15 @@ <title>Elliptic Curve Diffie-Hellman</title> <p>Elliptic Curve Diffie-Hellman is available with OpenSSL 0.9.8o or later if not disabled by configuration. To dynamically check availability, check that the atom <c>ecdh</c> is present in the - list with the <c>public_keys</c> tag in the return value of - <seealso marker="crypto#supports-0">crypto:supports()</seealso>. + list returned by <seealso marker="crypto#supports-1">crypto:supports(public_keys)</seealso>. </p> <p>The Edward curves <c>x25519</c> and <c>x448</c> are supported with OpenSSL 1.1.1 or later if not disabled by configuration. </p> - <p>The actual supported named curves could be checked by examining the list with the - <c>curves</c> tag in the return value of - <seealso marker="crypto#supports-0">crypto:supports()</seealso>. + <p>The actual supported named curves could be checked by examining the + list returned by <seealso marker="crypto#supports-1">crypto:supports(curves)</seealso>. </p> </section> diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml index d1d1252f29..3973cf3f9f 100644 --- a/lib/crypto/doc/src/crypto.xml +++ b/lib/crypto/doc/src/crypto.xml @@ -302,6 +302,12 @@ </datatype> <datatype> + <name name="cmac_cipher_algorithm"/> + <desc> + </desc> + </datatype> + + <datatype> <name name="rsa_digest_type"/> <desc> </desc> @@ -324,6 +330,11 @@ <name name="sha2"/> <name name="sha3"/> <name name="blake2"/> + <desc> + </desc> + </datatype> + + <datatype> <name name="compatibility_only_hash"/> <desc> <p>The <c>compatibility_only_hash()</c> algorithms are recommended only for compatibility with existing applications.</p> @@ -575,10 +586,11 @@ <datatype_title>Internal data types</datatype_title> <datatype> - <name name="stream_state"/> - <name name="hmac_state"/> - <name name="hash_state"/> <name name="crypto_state"/> + <name name="hash_state"/> + <name name="hmac_state"/> + <name name="mac_state"/> + <name name="stream_state"/> <desc> <p>Contexts with an internal state that should not be manipulated but passed between function calls. </p> @@ -783,6 +795,187 @@ </desc> </func> + <func> + <name name="mac" arity="3" since="OTP @OTP-13872@"/> + <fsummary></fsummary> + <desc> + <p>Short for <seealso marker="#mac-4">mac(Type, undefined, Key, Data)</seealso>. + </p> + </desc> + </func> + + <func> + <name name="mac" arity="4" since="OTP @OTP-13872@"/> + <fsummary></fsummary> + <desc> + <p>Computes a MAC (Message Authentication Code) of type <c>Type</c> from <c>Data</c>. + </p> + + <p><c>SubType</c> depends on the MAC <c>Type</c>: + </p> + <list> + <item>For <c>hmac</c> it is a hash algorithm, see + <seealso marker="algorithm_details#hmac">Algorithm Details</seealso> in the User's Guide. + </item> + <item>For <c>cmac</c> it is a cipher suitable for cmac, see + <seealso marker="algorithm_details#cmac">Algorithm Details</seealso> in the User's Guide. + </item> + <item>For <c>poly1305</c> it should be set to <c>undefined</c> or the + <seealso marker="#mac_init-2">mac/2</seealso> function could be used instead, see + <seealso marker="algorithm_details#poly1305">Algorithm Details</seealso> in the User's Guide. + </item> + </list> + + <p><c>Key</c> is the authentication key with a length according to the + <c>Type</c> and <c>SubType</c>. + The key length could be found with the + <seealso marker="#hash_info-1">hash_info/1</seealso> (<c>hmac</c>) for and + <seealso marker="#cipher_info-1">cipher_info/1</seealso> (<c>cmac</c>) + functions. For <c>poly1305</c> the key length is 32 bytes. Note that + the cryptographic quality of the key is not checked. + </p> + + <p>The <c>Mac</c> result will have a default length depending on the <c>Type</c> and <c>SubType</c>. + To set a shorter length, use <seealso marker="#macN-4">macN/4</seealso> or + <seealso marker="#macN-5">macN/5</seealso> instead. + The default length is documented in + <seealso marker="algorithm_details#message-authentication-codes--macs-">Algorithm Details</seealso> + in the User's Guide. + </p> + </desc> + </func> + + <func> + <name name="macN" arity="4" since="OTP @OTP-13872@"/> + <fsummary></fsummary> + <desc> + <p>Short for <seealso marker="#macN-5">macN(Type, undefined, Key, Data, MacLength)</seealso>. + </p> + </desc> + </func> + + <func> + <name name="macN" arity="5" since="OTP @OTP-13872@"/> + <fsummary></fsummary> + <desc> + <p>Computes a MAC (Message Authentication Code) + as <seealso marker="#mac-3">mac/3</seealso> and <seealso marker="#mac-4">mac/4</seealso> but + <c>MacLength</c> will limit the size of the resultant <c>Mac</c> to + at most <c>MacLength</c> bytes. + Note that if <c>MacLength</c> is greater than the actual number of + bytes returned from the underlying hash, the returned hash will have + that shorter length instead. + </p> + <p>The max <c>MacLength</c> is documented in + <seealso marker="algorithm_details#message-authentication-codes--macs-">Algorithm Details</seealso> + in the User's Guide. + </p> + </desc> + </func> + + <func> + <name name="mac_init" arity="2" since="OTP @OTP-13872@"/> + <fsummary></fsummary> + <desc> + <p>Short for <seealso marker="#mac_init-3">mac_init(Type, undefined, Key)</seealso>. + </p> + </desc> + </func> + + <func> + <name name="mac_init" arity="3" since="OTP @OTP-13872@"/> + <fsummary></fsummary> + <desc> + <p>Initializes the context for streaming MAC operations. + </p> + <p><c>Type</c> determines which mac algorithm to use in the MAC operation. + </p> + + <p><c>SubType</c> depends on the MAC <c>Type</c>: + </p> + <list> + <item>For <c>hmac</c> it is a hash algorithm, see + <seealso marker="algorithm_details#hmac">Algorithm Details</seealso> in the User's Guide. + </item> + <item>For <c>cmac</c> it is a cipher suitable for cmac, see + <seealso marker="algorithm_details#cmac">Algorithm Details</seealso> in the User's Guide. + </item> + <item>For <c>poly1305</c> it should be set to <c>undefined</c> or the + <seealso marker="#mac_init-2">mac/2</seealso> function could be used instead, see + <seealso marker="algorithm_details#poly1305">Algorithm Details</seealso> in the User's Guide. + </item> + </list> + + <p><c>Key</c> is the authentication key with a length according to the + <c>Type</c> and <c>SubType</c>. + The key length could be found with the + <seealso marker="#hash_info-1">hash_info/1</seealso> (<c>hmac</c>) for and + <seealso marker="#cipher_info-1">cipher_info/1</seealso> (<c>cmac</c>) + functions. For <c>poly1305</c> the key length is 32 bytes. Note that + the cryptographic quality of the key is not checked. + </p> + + <p>The returned <c>State</c> should be used in one or more subsequent calls to + <seealso marker="#mac_update-2">mac_update/2</seealso>. + The MAC value is finally returned by calling + <seealso marker="#mac_final-1">mac_final/1</seealso> or + <seealso marker="#mac_finalN-2">mac_finalN/2</seealso>. + </p> + + <p>See <seealso marker="crypto:new_api#example-of-mac_init-mac_update-and-mac_final"> + examples in the User's Guide.</seealso> + </p> + </desc> + </func> + + <func> + <name name="mac_update" arity="2" since="OTP @OTP-13872@"/> + <fsummary></fsummary> + <desc> + <p>Updates the MAC represented by <c>State0</c> using the given <c>Data</c> which + could be of any length. + </p> + <p>The <c>State0</c> is the State value originally from a MAC init function, that is + <seealso marker="#mac_init-2">mac_init/2</seealso>, + <seealso marker="#mac_init-3">mac_init/3</seealso> or + a previous call of <c>mac_update/2</c>. + The value <c>State0</c> is returned unchanged by the function as <c>State</c>. + </p> + </desc> + </func> + + <func> + <name name="mac_final" arity="1" since="OTP @OTP-13872@"/> + <fsummary></fsummary> + <desc> + <p>Finalizes the MAC operation referenced by <c>State</c>. The <c>Mac</c> result will have + a default length depending on the <c>Type</c> and <c>SubType</c> in the + <seealso marker="#mac_init-3">mac_init/2,3</seealso> call. + To set a shorter length, use <seealso marker="#mac_finalN-2">mac_finalN/2</seealso> instead. + The default length is documented in + <seealso marker="algorithm_details#message-authentication-codes--macs-">Algorithm Details</seealso> + in the User's Guide. + </p> + </desc> + </func> + + <func> + <name name="mac_finalN" arity="2" since="OTP @OTP-13872@"/> + <fsummary></fsummary> + <desc> + <p>Finalizes the MAC operation referenced by <c>State</c>. + </p> + <p><c>Mac</c> will be a binary with at most <c>MacLength</c> bytes. + Note that if <c>MacLength</c> is greater than the actual number of + bytes returned from the underlying hash, the returned hash will have + that shorter length instead. + </p> + <p>The max <c>MacLength</c> is documented in + <seealso marker="algorithm_details#message-authentication-codes--macs-">Algorithm Details</seealso> + in the User's Guide. + </p> + </desc> + </func> </funcs> <section> @@ -886,75 +1079,6 @@ </func> <func> - <name name="hmac" arity="3" since="OTP R16B"/> - <name name="hmac" arity="4" since="OTP R16B"/> - <fsummary></fsummary> - <desc> - <p>Computes a HMAC of type <c>Type</c> from <c>Data</c> using - <c>Key</c> as the authentication key.</p> <p><c>MacLength</c> - will limit the size of the resultant <c>Mac</c>.</p> - </desc> - </func> - - <func> - <name name="hmac_init" arity="2" since="OTP R14B03"/> - <fsummary></fsummary> - <desc> - <p>Initializes the context for streaming HMAC operations. <c>Type</c> determines - which hash function to use in the HMAC operation. <c>Key</c> is the authentication - key. The key can be any length.</p> - </desc> - </func> - - <func> - <name name="hmac_update" arity="2" since="OTP R14B03"/> - <fsummary></fsummary> - <desc> - <p>Updates the HMAC represented by <c>Context</c> using the given <c>Data</c>. <c>Context</c> - must have been generated using an HMAC init function (such as - <seealso marker="#hmac_init-2">hmac_init</seealso>). <c>Data</c> can be any length. <c>NewContext</c> - must be passed into the next call to <c>hmac_update</c> - or to one of the functions <seealso marker="#hmac_final-1">hmac_final</seealso> and - <seealso marker="#hmac_final_n-2">hmac_final_n</seealso> - </p> - <warning><p>Do not use a <c>Context</c> as argument in more than one - call to hmac_update or hmac_final. The semantics of reusing old contexts - in any way is undefined and could even crash the VM in earlier releases. - The reason for this limitation is a lack of support in the underlying - libcrypto API.</p></warning> - </desc> - </func> - - <func> - <name name="hmac_final" arity="1" since="OTP R14B03"/> - <fsummary></fsummary> - <desc> - <p>Finalizes the HMAC operation referenced by <c>Context</c>. The size of the resultant MAC is - determined by the type of hash function used to generate it.</p> - </desc> - </func> - - <func> - <name name="hmac_final_n" arity="2" since="OTP R14B03"/> - <fsummary></fsummary> - <desc> - <p>Finalizes the HMAC operation referenced by <c>Context</c>. <c>HashLen</c> must be greater than - zero. <c>Mac</c> will be a binary with at most <c>HashLen</c> bytes. Note that if HashLen is greater than the actual number of bytes returned from the underlying hash, the returned hash will have fewer than <c>HashLen</c> bytes.</p> - </desc> - </func> - - <func> - <name name="cmac" arity="3" since="OTP 20.0"/> - <name name="cmac" arity="4" since="OTP 20.0"/> - <fsummary>Calculates the Cipher-based Message Authentication Code.</fsummary> - <desc> - <p>Computes a CMAC of type <c>Type</c> from <c>Data</c> using - <c>Key</c> as the authentication key.</p> <p><c>MacLength</c> - will limit the size of the resultant <c>Mac</c>.</p> - </desc> - </func> - - <func> <name name="info_fips" arity="0" since="OTP 20.0"/> <fsummary>Provides information about the FIPS operating status.</fsummary> <desc> @@ -1068,15 +1192,6 @@ </func> <func> - <name name="poly1305" arity="2" since="OTP 21.1"/> - <fsummary></fsummary> - <desc> - <p>Computes a POLY1305 message authentication code (<c>Mac</c>) from <c>Data</c> using - <c>Key</c> as the authentication key.</p> - </desc> - </func> - - <func> <name name="private_decrypt" arity="4" since="OTP R16B01"/> <fsummary>Decrypts CipherText using the private Key.</fsummary> <desc> @@ -1901,7 +2016,7 @@ FloatValue = rand:uniform(). % again <seealso marker="#stream_encrypt-2">stream_encrypt</seealso> and <seealso marker="#stream_decrypt-2">stream_decrypt</seealso></p> <p>For keylengths see the - <seealso marker="crypto:algorithm_details#stream-ciphers">User's Guide</seealso>. + <seealso marker="crypto:algorithm_details#ciphers">User's Guide</seealso>. </p> </desc> </func> @@ -1917,7 +2032,7 @@ FloatValue = rand:uniform(). % again <seealso marker="#stream_encrypt-2">stream_encrypt</seealso> and <seealso marker="#stream_decrypt-2">stream_decrypt</seealso>.</p> <p>For keylengths and iv-sizes see the - <seealso marker="crypto:algorithm_details#stream-ciphers">User's Guide</seealso>. + <seealso marker="crypto:algorithm_details#ciphers">User's Guide</seealso>. </p> </desc> </func> @@ -1961,6 +2076,115 @@ FloatValue = rand:uniform(). % again </desc> </func> + <func> + <name name="hmac" arity="3" since="OTP R16B"/> + <name name="hmac" arity="4" since="OTP R16B"/> + <fsummary></fsummary> + <desc> + <dont><p>Don't use this function for new programs! Use + <seealso marker="crypto#mac-4">mac/4</seealso> or + <seealso marker="crypto#macN-5">macN/5</seealso> in + <seealso marker="crypto:new_api">the new api</seealso>.</p> + </dont> + <p>Computes a HMAC of type <c>Type</c> from <c>Data</c> using + <c>Key</c> as the authentication key.</p> <p><c>MacLength</c> + will limit the size of the resultant <c>Mac</c>.</p> + </desc> + </func> + + <func> + <name name="hmac_init" arity="2" since="OTP R14B03"/> + <fsummary></fsummary> + <desc> + <dont><p>Don't use this function for new programs! Use + <seealso marker="crypto#mac_init-3">mac_init/3</seealso> in + <seealso marker="crypto:new_api">the new api</seealso>.</p> + </dont> + <p>Initializes the context for streaming HMAC operations. <c>Type</c> determines + which hash function to use in the HMAC operation. <c>Key</c> is the authentication + key. The key can be any length.</p> + </desc> + </func> + + <func> + <name name="hmac_update" arity="2" since="OTP R14B03"/> + <fsummary></fsummary> + <desc> + <dont><p>Don't use this function for new programs! Use + <seealso marker="crypto#mac_update-2">mac_update/2</seealso> in + <seealso marker="crypto:new_api">the new api</seealso>.</p> + </dont> + <p>Updates the HMAC represented by <c>Context</c> using the given <c>Data</c>. <c>Context</c> + must have been generated using an HMAC init function (such as + <seealso marker="#hmac_init-2">hmac_init</seealso>). <c>Data</c> can be any length. <c>NewContext</c> + must be passed into the next call to <c>hmac_update</c> + or to one of the functions <seealso marker="#hmac_final-1">hmac_final</seealso> and + <seealso marker="#hmac_final_n-2">hmac_final_n</seealso> + </p> + <warning><p>Do not use a <c>Context</c> as argument in more than one + call to hmac_update or hmac_final. The semantics of reusing old contexts + in any way is undefined and could even crash the VM in earlier releases. + The reason for this limitation is a lack of support in the underlying + libcrypto API.</p></warning> + </desc> + </func> + + <func> + <name name="hmac_final" arity="1" since="OTP R14B03"/> + <fsummary></fsummary> + <desc> + <dont><p>Don't use this function for new programs! Use + <seealso marker="crypto#mac_final-1">mac_final/1</seealso> in + <seealso marker="crypto:new_api">the new api</seealso>.</p> + </dont> + <p>Finalizes the HMAC operation referenced by <c>Context</c>. The size of the resultant MAC is + determined by the type of hash function used to generate it.</p> + </desc> + </func> + + <func> + <name name="hmac_final_n" arity="2" since="OTP R14B03"/> + <fsummary></fsummary> + <desc> + <dont><p>Don't use this function for new programs! Use + <seealso marker="crypto#mac_finalN-2">mac_finalN/2</seealso> in + <seealso marker="crypto:new_api">the new api</seealso>.</p> + </dont> + <p>Finalizes the HMAC operation referenced by <c>Context</c>. <c>HashLen</c> must be greater than + zero. <c>Mac</c> will be a binary with at most <c>HashLen</c> bytes. Note that if HashLen is greater than the actual number of bytes returned from the underlying hash, the returned hash will have fewer than <c>HashLen</c> bytes.</p> + </desc> + </func> + + <func> + <name name="cmac" arity="3" since="OTP 20.0"/> + <name name="cmac" arity="4" since="OTP 20.0"/> + <fsummary>Calculates the Cipher-based Message Authentication Code.</fsummary> + <desc> + <dont><p>Don't use this function for new programs! Use + <seealso marker="crypto#mac-4">mac/4</seealso> or + <seealso marker="crypto#macN-5">macN/5</seealso> in + <seealso marker="crypto:new_api">the new api</seealso>.</p> + </dont> + <p>Computes a CMAC of type <c>Type</c> from <c>Data</c> using + <c>Key</c> as the authentication key.</p> <p><c>MacLength</c> + will limit the size of the resultant <c>Mac</c>.</p> + </desc> + </func> + + <func> + <name name="poly1305" arity="2" since="OTP 21.1"/> + <fsummary></fsummary> + <desc> + <dont><p>Don't use this function for new programs! Use + <seealso marker="crypto#mac-3">mac/3</seealso> or + <seealso marker="crypto#macN-4">macN/4</seealso> in + <seealso marker="crypto:new_api">the new api</seealso>.</p> + </dont> + <p>Computes a POLY1305 message authentication code (<c>Mac</c>) from <c>Data</c> using + <c>Key</c> as the authentication key.</p> + </desc> + </func> + </funcs> diff --git a/lib/crypto/doc/src/new_api.xml b/lib/crypto/doc/src/new_api.xml index bd2334ac9f..aacf5e4f76 100644 --- a/lib/crypto/doc/src/new_api.xml +++ b/lib/crypto/doc/src/new_api.xml @@ -40,7 +40,7 @@ to maintain. </p> <p>It turned out that using the old api in the new way (more about that later), and still keep it - backwards compatible, was not possible. Specially as more precision in the error messages was wanted + backwards compatible, was not possible. Specially as more precision in the error messages is desired it could not be combined with the old standard. </p> <p>Therefore the old api (see next section) is kept for now but internally implemented with new primitives. @@ -49,7 +49,7 @@ <section> <title>The old API</title> - <p>The old functions - not recommended for new programs - are:</p> + <p>The old functions - not recommended for new programs - are for chipers:</p> <list> <item><seealso marker="crypto#block_encrypt-3">block_encrypt/3</seealso></item> <item><seealso marker="crypto#block_encrypt-4">block_encrypt/4</seealso></item> @@ -59,61 +59,101 @@ <item><seealso marker="crypto#stream_init-2">stream_init/3</seealso></item> <item><seealso marker="crypto#stream_encrypt-2">stream_encrypt/2</seealso></item> <item><seealso marker="crypto#stream_decrypt-2">stream_decrypt/2</seealso></item> + </list> + <p>for lists of supported algorithms:</p> + <list> <item><seealso marker="crypto#supports-0">supports/0</seealso></item> </list> + <p>and for MACs (Message Authentication Codes):</p> + <list> + <item><seealso marker="crypto#cmac-3">cmac/3</seealso></item> + <item><seealso marker="crypto#cmac-4">cmac/4</seealso></item> + <item><seealso marker="crypto#hmac-3">hmac/3</seealso></item> + <item><seealso marker="crypto#hmac-4">hmac/4</seealso></item> + <item><seealso marker="crypto#hmac_init-2">hmac_init/2</seealso></item> + <item><seealso marker="crypto#hmac_update-2">hmac_update/2</seealso></item> + <item><seealso marker="crypto#hmac_final-1">hmac_final/1</seealso></item> + <item><seealso marker="crypto#hmac_final_n-2">hmac_final_n/2</seealso></item> + <item><seealso marker="crypto#poly1305-2">poly1305/2</seealso></item> + </list> <p>They are not deprecated for now, but may be in a future release. </p> </section> <section> <title>The new API</title> - <p>The new functions for encrypting or decrypting one single binary are: - </p> - <list> - <item><seealso marker="crypto#crypto_one_time/4">crypto_one_time/4</seealso></item> - <item><seealso marker="crypto#crypto_one_time/5">crypto_one_time/5</seealso></item> - <item><seealso marker="crypto#crypto_one_time_aead/6">crypto_one_time_aead/6</seealso></item> - <item><seealso marker="crypto#crypto_one_time_aead/7">crypto_one_time_aead/7</seealso></item> - </list> - <p>In those functions the internal crypto state is first created and initialized - with the cipher type, the key and possibly other data. Then the single binary is encrypted - or decrypted, - the crypto state is de-allocated and the result of the crypto operation is returned. - </p> - <p>The <c>crypto_one_time_aead</c> functions are for the ciphers of mode <c>ccm</c> or - <c>gcm</c>, and for the cipher <c>chacha20-poly1305</c>. - </p> - <p>For repeated encryption or decryption of a text divided in parts, where the internal - crypto state is initialized once, and then many binaries are encrypted or decrypted with - the same state, the functions are: - </p> - <list> - <item><seealso marker="crypto#crypto_init/4">crypto_init/4</seealso></item> - <item><seealso marker="crypto#crypto_init/3">crypto_init/3</seealso></item> - <item><seealso marker="crypto#crypto_update/2">crypto_update/2</seealso></item> - </list> - <p>The <c>crypto_init</c> initialies an internal cipher state, and one or more calls of - <c>crypto_update</c> does the acual encryption or decryption. Note that AEAD ciphers - can't be handled this way due to their nature. - </p> - <p>For repeated encryption or decryption of a text divided in parts where the - same cipher and same key is used, but a new initialization vector (nounce) should be applied - for each part, the functions are: - </p> - <list> - <item><seealso marker="crypto#crypto_dyn_iv_init/3">crypto_dyn_iv_init/3</seealso></item> - <item><seealso marker="crypto#crypto_dyn_iv_update/3">crypto_dyn_iv_update/3</seealso></item> - </list> - <p>An example of where those functions are needed, is when handling the TLS protocol.</p> - <p>For information about available algorithms, use: - </p> - <list> - <item><seealso marker="crypto#supports-1">supports/1</seealso></item> - <item><seealso marker="crypto#hash_info-1">hash_info/1</seealso></item> - <item><seealso marker="crypto#cipher_info-1">cipher_info/1</seealso></item> - </list> + <section> + <title>Encryption and decryption</title> + <p>The new functions for encrypting or decrypting one single binary are: + </p> + <list> + <item><seealso marker="crypto#crypto_one_time/4">crypto_one_time/4</seealso></item> + <item><seealso marker="crypto#crypto_one_time/5">crypto_one_time/5</seealso></item> + <item><seealso marker="crypto#crypto_one_time_aead/6">crypto_one_time_aead/6</seealso></item> + <item><seealso marker="crypto#crypto_one_time_aead/7">crypto_one_time_aead/7</seealso></item> + </list> + <p>In those functions the internal crypto state is first created and initialized + with the cipher type, the key and possibly other data. Then the single binary is encrypted + or decrypted, + the crypto state is de-allocated and the result of the crypto operation is returned. + </p> + <p>The <c>crypto_one_time_aead</c> functions are for the ciphers of mode <c>ccm</c> or + <c>gcm</c>, and for the cipher <c>chacha20-poly1305</c>. + </p> + <p>For repeated encryption or decryption of a text divided in parts, where the internal + crypto state is initialized once, and then many binaries are encrypted or decrypted with + the same state, the functions are: + </p> + <list> + <item><seealso marker="crypto#crypto_init/4">crypto_init/4</seealso></item> + <item><seealso marker="crypto#crypto_init/3">crypto_init/3</seealso></item> + <item><seealso marker="crypto#crypto_update/2">crypto_update/2</seealso></item> + </list> + <p>The <c>crypto_init</c> initialies an internal cipher state, and one or more calls of + <c>crypto_update</c> does the acual encryption or decryption. Note that AEAD ciphers + can't be handled this way due to their nature. + </p> + <p>For repeated encryption or decryption of a text divided in parts where the + same cipher and same key is used, but a new initialization vector (nounce) should be applied + for each part, the functions are: + </p> + <list> + <item><seealso marker="crypto#crypto_dyn_iv_init/3">crypto_dyn_iv_init/3</seealso></item> + <item><seealso marker="crypto#crypto_dyn_iv_update/3">crypto_dyn_iv_update/3</seealso></item> + </list> + <p>An example of where those functions are needed, is when handling the TLS protocol.</p> + <p>For information about available algorithms, use: + </p> + <list> + <item><seealso marker="crypto#supports-1">supports/1</seealso></item> + <item><seealso marker="crypto#hash_info-1">hash_info/1</seealso></item> + <item><seealso marker="crypto#cipher_info-1">cipher_info/1</seealso></item> + </list> + </section> <section> + <title>MACs (Message Authentication Codes)</title> + <p>The new functions for calculating a MAC of a single piece of text are:</p> + <list> + <item><seealso marker="crypto#mac-3">mac/3</seealso></item> + <item><seealso marker="crypto#mac-4">mac/4</seealso></item> + <item><seealso marker="crypto#macN-4">macN/4</seealso></item> + <item><seealso marker="crypto#macN-5">macN/5</seealso></item> + </list> + <p>For calculating a MAC of a text divided in parts use:</p> + <list> + <item><seealso marker="crypto#mac_init-2">mac_init/2</seealso></item> + <item><seealso marker="crypto#mac_init-3">mac_init/3</seealso></item> + <item><seealso marker="crypto#mac_update-2">mac_update/2</seealso></item> + <item><seealso marker="crypto#mac_final-1">mac_final/1</seealso></item> + <item><seealso marker="crypto#mac_finalN-2">mac_finalN/2</seealso></item> + </list> + </section> + </section> + + <section> + <title>Examples of the new api</title> + <section> <title>Examples of crypto_init/4 and crypto_update/2</title> <p>The functions <seealso marker="crypto#crypto_init/4">crypto_init/4</seealso> and <seealso marker="crypto#crypto_update/2">crypto_update/2</seealso> are intended @@ -143,7 +183,7 @@ 8> crypto:crypto_update(StateDec, <<67,44,216,166,25,130,203>>). <<"First b">> 9> crypto:crypto_update(StateDec, <<5,66,6,162,16,79,94,115,234,197, - 94,253,16,144,151>>). + 94,253,16,144,151>>). <<"ytesSecond byte">> 10> crypto:crypto_update(StateDec, <<41>>). <<"s">> @@ -159,16 +199,16 @@ </p> <code type="erl"> encode(Crypto, Key, IV) -> - crypto_loop(crypto:crypto_init(Crypto, Key, IV, true)). + crypto_loop(crypto:crypto_init(Crypto, Key, IV, true)). crypto_loop(State) -> - receive - {Text, Requester} -> - Requester ! crypto:crypto_update(State, Text), - loop(State) - end. + receive + {Text, Requester} -> + Requester ! crypto:crypto_update(State, Text), + loop(State) + end. </code> - </section> + </section> <section> <title>Example of crypto_one_time/5</title> @@ -219,6 +259,35 @@ </p> </section> + <section> + <title>Example of mac_init mac_update and mac_final</title> + <code> + 1> Key = <<1:128>>. + <<0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1>> + 2> StateMac = crypto:mac_init(cmac, aes_128_cbc, Key). + #Ref<0.2424664121.2781478916.232610> + 3> crypto:mac_update(StateMac, <<"First bytes">>). + #Ref<0.2424664121.2781478916.232610> + 4> crypto:mac_update(StateMac, " "). + #Ref<0.2424664121.2781478916.232610> + 5> crypto:mac_update(StateMac, <<"last bytes">>). + #Ref<0.2424664121.2781478916.232610> + 6> crypto:mac_final(StateMac). + <<68,191,219,128,84,77,11,193,197,238,107,6,214,141,160, + 249>> + 7> + </code> + <p>and compare the result with a single calculation just for this example:</p> + <code> + 7> crypto:mac(cmac, aes_128_cbc, Key, "First bytes last bytes"). + <<68,191,219,128,84,77,11,193,197,238,107,6,214,141,160, + 249>> + 8> v(7) == v(6). + true + 9> + </code> + </section> + </section> <section> @@ -233,7 +302,7 @@ on the mode. An example is the ccm mode which has a variant called ccm8 where the so called tag has a length of eight bits. </p> - <p>The old names had by time lost any common naming which the new names now introduces. The new names include + <p>The old names had by time lost any common naming convention which the new names now introduces. The new names include the key length which improves the error checking in the lower levels of the crypto application. </p> diff --git a/lib/crypto/doc/src/notes.xml b/lib/crypto/doc/src/notes.xml index b69657bfa8..5f47981855 100644 --- a/lib/crypto/doc/src/notes.xml +++ b/lib/crypto/doc/src/notes.xml @@ -31,6 +31,23 @@ </header> <p>This document describes the changes made to the Crypto application.</p> +<section><title>Crypto 4.5.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + The cipher aes-ctr was disabled by misstake in + crypto:supports for cryptolibs before 1.0.1. It worked + however in the encrypt and decrypt functions.</p> + <p> + Own Id: OTP-15829</p> + </item> + </list> + </section> + +</section> + <section><title>Crypto 4.5</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl index 8ffdde2b90..965697578d 100644 --- a/lib/crypto/src/crypto.erl +++ b/lib/crypto/src/crypto.erl @@ -28,9 +28,6 @@ -export([hash/2, hash_init/1, hash_update/2, hash_final/1]). -export([sign/4, sign/5, verify/5, verify/6]). -export([generate_key/2, generate_key/3, compute_key/4]). --export([hmac/3, hmac/4, hmac_init/2, hmac_update/2, hmac_final/1, hmac_final_n/2]). --export([cmac/3, cmac/4]). --export([poly1305/2]). -export([exor/2, strong_rand_bytes/1, mod_pow/3]). -export([rand_seed/0, rand_seed_alg/1, rand_seed_alg/2]). -export([rand_seed_s/0, rand_seed_alg_s/1, rand_seed_alg_s/2]). @@ -48,6 +45,9 @@ -export([rand_seed/1]). %% Old interface. Now implemented with the New interface +-export([hmac/3, hmac/4, hmac_init/2, hmac_update/2, hmac_final/1, hmac_final_n/2]). +-export([cmac/3, cmac/4]). +-export([poly1305/2]). -export([stream_init/2, stream_init/3, stream_encrypt/2, stream_decrypt/2, @@ -62,7 +62,9 @@ crypto_one_time_aead/6, crypto_one_time_aead/7, crypto_dyn_iv_init/3, crypto_dyn_iv_update/3, - supports/1 + supports/1, + mac/3, mac/4, macN/4, macN/5, + mac_init/2, mac_init/3, mac_update/2, mac_final/1, mac_finalN/2 ]). @@ -109,9 +111,10 @@ stream_state/0, hmac_state/0, hash_state/0, - crypto_state/0 + crypto_state/0, + mac_state/0 ]). - + %% Private. For tests. -export([packed_openssl_version/4, engine_methods_convert_to_bitmask/2, get_test_engine/0]). @@ -136,7 +139,7 @@ -type rsa_private() :: [key_integer()] . % [E, N, D] | [E, N, D, P1, P2, E1, E2, C] -type rsa_params() :: {ModulusSizeInBits::integer(), PublicExponent::key_integer()} . --type dss_public() :: [key_integer()] . % [P, Q, G, Y] +-type dss_public() :: [key_integer()] . % [P, Q, G, Y] -type dss_private() :: [key_integer()] . % [P, Q, G, X] -type ecdsa_public() :: key_integer() . @@ -282,7 +285,7 @@ %%% New cipher schema %%% -type cipher() :: cipher_no_iv() - | cipher_iv() + | cipher_iv() | cipher_aead() . -type cipher_no_iv() :: aes_128_ecb @@ -326,7 +329,7 @@ -type cipher_aead() :: aes_128_ccm | aes_192_ccm | aes_256_ccm - + | aes_128_gcm | aes_192_gcm | aes_256_gcm @@ -334,23 +337,6 @@ | chacha20_poly1305 . -%% -type retired_cipher_no_iv_aliases() :: aes_ecb . - -%% -type retired_cipher_iv_aliases() :: aes_cbc -%% | aes_cbc128 % aes_128_cbc -%% | aes_cbc256 % aes_256_cbc -%% | aes_cfb128 -%% | aes_cfb8 -%% | aes_ctr -%% | des3_cbc % des_ede3_cbc -%% | des_ede3 % des_ede3_cbc -%% | des_ede3_cbf % des_ede3_cfb -%% | des3_cbf % des_ede3_cfb -%% | des3_cfb . % des_ede3_cfb - -%% -type retired_cipher_aead_aliases() :: aes_ccm -%% | aes_gcm . - %%%---------------------------------------------------------------- %%% Old cipher scheme %%% @@ -365,7 +351,7 @@ -type stream_cipher() :: ctr_cipher() | chacha20 | rc4 . - + %%%---- -type cbc_cipher() :: aes_128_cbc @@ -374,7 +360,7 @@ | blowfish_cbc | des_cbc | des_ede3_cbc - | rc2_cbc + | rc2_cbc | retired_cbc_cipher_aliases() . -type retired_cbc_cipher_aliases() :: aes_cbc % aes_*_cbc @@ -382,7 +368,7 @@ | aes_cbc256 % aes_256_cbc | des3_cbc % des_ede3_cbc | des_ede3 . % des_ede3_cbc - + %%%---- -type cfb_cipher() :: aes_128_cfb128 | aes_192_cfb128 @@ -398,7 +384,7 @@ -type retired_cfb_cipher_aliases() :: aes_cfb8 % aes_*_cfb8 | aes_cfb128 % aes_*_cfb128 | des3_cbf % des_ede3_cfb, cfb misspelled - | des3_cfb % des_ede3_cfb + | des3_cfb % des_ede3_cfb | des_ede3_cbf .% cfb misspelled @@ -457,6 +443,19 @@ %%-------------------------------------------------------------------- +%% +%% Make the new descriptive_error() look like the old run_time_error() +%% +-define(COMPAT(CALL), + try begin CALL end + catch + error:{error, {_File,_Line}, _Reason} -> + error(badarg); + error:{E, {_File,_Line}, _Reason} when E==notsup ; E==badarg -> + error(E) + end). + +%%-------------------------------------------------------------------- -compile(no_native). -on_load(on_load/0). -define(CRYPTO_NIF_VSN,302). @@ -580,7 +579,7 @@ hash(Type, Data) -> -spec hash_init(Type) -> State when Type :: hash_algorithm(), State :: hash_state(). -hash_init(Type) -> +hash_init(Type) -> notsup_to_error(hash_init_nif(Type)). -spec hash_update(State, Data) -> NewState when State :: hash_state(), @@ -599,25 +598,139 @@ hash_final(Context) -> %%%================================================================ %%% %%% MACs (Message Authentication Codes) -%%% +%%% %%%================================================================ -%%%---- HMAC - -type hmac_hash_algorithm() :: sha1() | sha2() | sha3() | compatibility_only_hash(). -%%%---- hmac/3,4 +-type cmac_cipher_algorithm() :: aes_128_cbc | aes_192_cbc | aes_256_cbc | blowfish_cbc + | des_cbc | des_ede3_cbc | rc2_cbc + | aes_128_cfb128 | aes_192_cfb128 | aes_256_cfb128 + | aes_128_cfb8 | aes_192_cfb8 | aes_256_cfb8 + . + +%%%---------------------------------------------------------------- +%%% Calculate MAC for the whole text at once + +-spec mac(Type :: poly1305, Key, Data) -> Mac | descriptive_error() + when Key :: iodata(), + Data :: iodata(), + Mac :: binary(). + +mac(poly1305, Key, Data) -> mac(poly1305, undefined, Key, Data). + + +-spec mac(Type, SubType, Key, Data) -> Mac | descriptive_error() + when Type :: hmac | cmac | poly1305, + SubType :: hmac_hash_algorithm() | cmac_cipher_algorithm() | undefined, + Key :: iodata(), + Data :: iodata(), + Mac :: binary(). + +mac(Type, SubType, Key, Data) -> mac_nif(Type, SubType, Key, Data). + + + +-spec macN(Type :: poly1305, Key, Data, MacLength) -> Mac | descriptive_error() + when Key :: iodata(), + Data :: iodata(), + Mac :: binary(), + MacLength :: pos_integer(). + +macN(Type, Key, Data, MacLength) -> + macN(Type, undefined, Key, Data, MacLength). + + +-spec macN(Type, SubType, Key, Data, MacLength) -> Mac | descriptive_error() + when Type :: hmac | cmac | poly1305, + SubType :: hmac_hash_algorithm() | cmac_cipher_algorithm() | undefined, + Key :: iodata(), + Data :: iodata(), + Mac :: binary(), + MacLength :: pos_integer(). + +macN(Type, SubType, Key, Data, MacLength) -> + erlang:binary_part(mac(Type,SubType,Key,Data), 0, MacLength). + + +%%%---------------------------------------------------------------- +%%% Calculate the MAC by uppdating by pieces of the text + +-opaque mac_state() :: reference() . + +-spec mac_init(Type :: poly1305, Key) -> State | descriptive_error() + when Key :: iodata(), + State :: mac_state() . +mac_init(poly1305, Key) -> + mac_init_nif(poly1305, undefined, Key). + + +-spec mac_init(Type, SubType, Key) -> State | descriptive_error() + when Type :: hmac | cmac | poly1305, + SubType :: hmac_hash_algorithm() | cmac_cipher_algorithm() | undefined, + Key :: iodata(), + State :: mac_state() . +mac_init(Type, SubType, Key) -> + mac_init_nif(Type, SubType, Key). + + +-spec mac_update(State0, Data) -> State | descriptive_error() + when Data :: iodata(), + State0 :: mac_state(), + State :: mac_state(). +mac_update(Ref, Data) -> + mac_update_nif(Ref, Data). + + + +-spec mac_final(State) -> Mac | descriptive_error() + when State :: mac_state(), + Mac :: binary(). +mac_final(Ref) -> + mac_final_nif(Ref). + + +-spec mac_finalN(State, MacLength) -> Mac | descriptive_error() + when State :: mac_state(), + MacLength :: pos_integer(), + Mac :: binary(). +mac_finalN(Ref, MacLength) -> + erlang:binary_part(mac_final(Ref), 0, MacLength). + --spec hmac(Type, Key, Data) -> +%%%---------------------------------------------------------------- +%%% NIFs for the functions above + +mac_nif(_Type, _SubType, _Key, _Data) -> ?nif_stub. + +mac_init_nif(_Type, _SubType, _Key) -> ?nif_stub. +mac_update_nif(_Ref, _Data) -> ?nif_stub. +mac_final_nif(_Ref) -> ?nif_stub. + +%%%================================================================ +%%% +%%% The "Old API", kept for compatibility +%%% +%%%================================================================ + +%%%---------------------------------------------------------------- +%%%---------------------------------------------------------------- +%%% Message Authentication Codes, MAC +%%% + +%%%---- HMAC + +%%%---- hmac/3,4 + +-spec hmac(Type, Key, Data) -> Mac when Type :: hmac_hash_algorithm(), Key :: iodata(), Data :: iodata(), Mac :: binary() . hmac(Type, Key, Data) -> - Data1 = iolist_to_binary(Data), - hmac(Type, Key, Data1, undefined, erlang:byte_size(Data1), max_bytes()). + ?COMPAT(mac(hmac, Type, Key, Data)). --spec hmac(Type, Key, Data, MacLength) -> +-spec hmac(Type, Key, Data, MacLength) -> Mac when Type :: hmac_hash_algorithm(), Key :: iodata(), Data :: iodata(), @@ -625,45 +738,43 @@ hmac(Type, Key, Data) -> Mac :: binary() . hmac(Type, Key, Data, MacLength) -> - Data1 = iolist_to_binary(Data), - hmac(Type, Key, Data1, MacLength, erlang:byte_size(Data1), max_bytes()). + ?COMPAT(macN(hmac, Type, Key, Data, MacLength)). %%%---- hmac_init, hamc_update, hmac_final --opaque hmac_state() :: binary(). +-opaque hmac_state() :: mac_state(). % Was: binary(). -spec hmac_init(Type, Key) -> State when Type :: hmac_hash_algorithm(), Key :: iodata(), State :: hmac_state() . hmac_init(Type, Key) -> - notsup_to_error(hmac_init_nif(Type, Key)). + ?COMPAT(mac_init(hmac, Type, Key)). %%%---- hmac_update -spec hmac_update(State, Data) -> NewState when Data :: iodata(), State :: hmac_state(), NewState :: hmac_state(). -hmac_update(State, Data0) -> - Data = iolist_to_binary(Data0), - hmac_update(State, Data, erlang:byte_size(Data), max_bytes()). +hmac_update(State, Data) -> + ?COMPAT(mac_update(State, Data)). %%%---- hmac_final -spec hmac_final(State) -> Mac when State :: hmac_state(), Mac :: binary(). hmac_final(Context) -> - notsup_to_error(hmac_final_nif(Context)). + ?COMPAT(mac_final(Context)). -spec hmac_final_n(State, HashLen) -> Mac when State :: hmac_state(), HashLen :: integer(), Mac :: binary(). hmac_final_n(Context, HashLen) -> - notsup_to_error(hmac_final_nif(Context, HashLen)). + ?COMPAT(mac_finalN(Context, HashLen)). %%%---- CMAC --define(CMAC_CIPHER_ALGORITHM, cbc_cipher() | cfb_cipher() | blowfish_cbc | des_ede3 | rc2_cbc ). +-define(CMAC_CIPHER_ALGORITHM, cbc_cipher() | cfb_cipher() | blowfish_cbc | des_ede3 | rc2_cbc ). -spec cmac(Type, Key, Data) -> Mac when Type :: ?CMAC_CIPHER_ALGORITHM, @@ -671,42 +782,31 @@ hmac_final_n(Context, HashLen) -> Data :: iodata(), Mac :: binary(). cmac(Type, Key, Data) -> - notsup_to_error(cmac_nif(alias(Type), Key, Data)). + ?COMPAT(mac(cmac, alias(Type), Key, Data)). -spec cmac(Type, Key, Data, MacLength) -> Mac when Type :: ?CMAC_CIPHER_ALGORITHM, Key :: iodata(), Data :: iodata(), - MacLength :: integer(), + MacLength :: integer(), Mac :: binary(). cmac(Type, Key, Data, MacLength) -> - erlang:binary_part(cmac(alias(Type), Key, Data), 0, MacLength). + ?COMPAT(macN(cmac, alias(Type), Key, Data, MacLength)). %%%---- POLY1305 -spec poly1305(iodata(), iodata()) -> Mac when Mac :: binary(). poly1305(Key, Data) -> - poly1305_nif(Key, Data). + ?COMPAT(mac(poly1305, Key, Data)). -%%%================================================================ -%%% -%%% Encrypt/decrypt, The "Old API" -%%% -%%%================================================================ +%%%---------------------------------------------------------------- +%%%---------------------------------------------------------------- +%%% Ciphers --define(COMPAT(CALL), - try begin CALL end - catch - error:{error, {_File,_Line}, _Reason} -> - error(badarg); - error:{E, {_File,_Line}, _Reason} when E==notsup ; E==badarg -> - error(E) - end). %%%---- Cipher info -%%%---------------------------------------------------------------- -spec cipher_info(Type) -> Result | run_time_error() when Type :: cipher(), Result :: #{key_length := integer(), @@ -845,7 +945,7 @@ block_decrypt(Type, Key0, CryptoText) -> Key :: iodata(), IVec ::binary(), State :: stream_state() . -stream_init(Type, Key0, IVec) when is_binary(IVec) -> +stream_init(Type, Key0, IVec) when is_binary(IVec) -> Key = iolist_to_binary(Key0), Ref = ?COMPAT(ng_crypto_init_nif(alias(Type,Key), Key, iolist_to_binary(IVec), @@ -933,7 +1033,7 @@ next_iv(Type, Data, _Ivec) -> %%%---------------------------------------------------------------- %%% %%% Create and initialize a new state for encryption or decryption -%%% +%%% -spec crypto_init(Cipher, Key, EncryptFlag) -> State | descriptive_error() when Cipher :: cipher_no_iv(), @@ -971,12 +1071,12 @@ crypto_dyn_iv_init(Cipher, Key, EncryptFlag) -> %%% Encrypt/decrypt a sequence of bytes. The sum of the sizes %%% of all blocks must be an integer multiple of the crypto's %%% blocksize. -%%% +%%% -spec crypto_update(State, Data) -> Result | descriptive_error() - when State :: crypto_state(), - Data :: iodata(), - Result :: binary() . + when State :: crypto_state(), + Data :: iodata(), + Result :: binary() . crypto_update(State, Data0) -> case iolist_to_binary(Data0) of <<>> -> @@ -1005,7 +1105,7 @@ crypto_dyn_iv_update(State, Data0, IV) -> %%% %%% Encrypt/decrypt one set bytes. %%% The size must be an integer multiple of the crypto's blocksize. -%%% +%%% -spec crypto_one_time(Cipher, Key, Data, EncryptFlag) -> Result | descriptive_error() @@ -1015,8 +1115,15 @@ crypto_dyn_iv_update(State, Data0, IV) -> EncryptFlag :: boolean(), Result :: binary() . -crypto_one_time(Cipher, Key, Data, EncryptFlag) -> - crypto_one_time(Cipher, Key, <<>>, Data, EncryptFlag). +crypto_one_time(Cipher, Key, Data0, EncryptFlag) -> + case iolist_to_binary(Data0) of + <<>> -> + <<>>; % Known to fail on OpenSSL 0.9.8h + Data -> + ng_crypto_one_time_nif(Cipher, + iolist_to_binary(Key), <<>>, Data, + EncryptFlag) + end. -spec crypto_one_time(Cipher, Key, IV, Data, EncryptFlag) -> Result | descriptive_error() @@ -1121,7 +1228,7 @@ ng_crypto_one_time_nif(_Cipher, _Key, _IVec, _Data, _EncryptFlg) -> ?nif_stub. false -> Ciphers end). - + prepend_old_aliases(L0) -> L1 = ?if_also(des_ede3_cbc, L0, @@ -1465,7 +1572,7 @@ rand_seed_nif(_Seed) -> ?nif_stub. %%% Sign -spec sign(Algorithm, DigestType, Msg, Key) - -> Signature + -> Signature when Algorithm :: pk_sign_verify_algs(), DigestType :: rsa_digest_type() | dss_digest_type() @@ -1483,7 +1590,7 @@ sign(Algorithm, Type, Data, Key) -> -spec sign(Algorithm, DigestType, Msg, Key, Options) - -> Signature + -> Signature when Algorithm :: pk_sign_verify_algs(), DigestType :: rsa_digest_type() | dss_digest_type() @@ -1580,7 +1687,7 @@ sign_verify_compatibility(Algorithm0, Type0, _Digest) -> | rsa_x931_padding | rsa_no_padding. --type rsa_opt() :: {rsa_padding, rsa_padding()} +-type rsa_opt() :: {rsa_padding, rsa_padding()} | {signature_md, atom()} | {rsa_mgf1_md, sha} | {rsa_oaep_label, binary()} @@ -1653,7 +1760,7 @@ pkey_crypt_nif(_Algorithm, _In, _Key, _Options, _IsPrivate, _IsEncrypt) -> ?nif_ %%%================================================================ -spec generate_key(Type, Params) - -> {PublicKey, PrivKeyOut} + -> {PublicKey, PrivKeyOut} when Type :: dh | ecdh | rsa | srp, PublicKey :: dh_public() | ecdh_public() | rsa_public() | srp_public(), PrivKeyOut :: dh_private() | ecdh_private() | rsa_private() | {srp_public(),srp_private()}, @@ -1663,7 +1770,7 @@ generate_key(Type, Params) -> generate_key(Type, Params, undefined). -spec generate_key(Type, Params, PrivKeyIn) - -> {PublicKey, PrivKeyOut} + -> {PublicKey, PrivKeyOut} when Type :: dh | ecdh | rsa | srp, PublicKey :: dh_public() | ecdh_public() | rsa_public() | srp_public(), PrivKeyIn :: undefined | dh_private() | ecdh_private() | rsa_private() | {srp_public(),srp_private()}, @@ -1814,7 +1921,7 @@ mod_pow(Base, Exponent, Prime) -> %%%====================================================================== %%% %%% Engine functions -%%% +%%% %%%====================================================================== %%%---- Refering to keys stored in an engine: @@ -2121,7 +2228,7 @@ ensure_engine_unloaded(Engine) -> %%---------------------------------------------------------------------- %% Function: ensure_engine_unloaded/2 %%---------------------------------------------------------------------- --spec ensure_engine_unloaded(Engine, EngineMethods) -> +-spec ensure_engine_unloaded(Engine, EngineMethods) -> Result when Engine :: engine_ref(), EngineMethods :: [engine_method_type()], Result :: ok | {error, Reason::term()}. @@ -2203,7 +2310,7 @@ path2bin(Path) when is_list(Path) -> %%%================================================================ %%% %%% Internal functions -%%% +%%% %%%================================================================ max_bytes() -> @@ -2235,43 +2342,6 @@ hash_init_nif(_Hash) -> ?nif_stub. hash_update_nif(_State, _Data) -> ?nif_stub. hash_final_nif(_State) -> ?nif_stub. -%% HMAC -------------------------------------------------------------------- - -hmac(Type, Key, Data, MacSize, Size, MaxBytes) when Size =< MaxBytes -> - notsup_to_error( - case MacSize of - undefined -> hmac_nif(Type, Key, Data); - _ -> hmac_nif(Type, Key, Data, MacSize) - end); -hmac(Type, Key, Data, MacSize, Size, MaxBytes) -> - State0 = hmac_init(Type, Key), - State1 = hmac_update(State0, Data, Size, MaxBytes), - case MacSize of - undefined -> hmac_final(State1); - _ -> hmac_final_n(State1, MacSize) - end. - -hmac_update(State, Data, Size, MaxBytes) when Size =< MaxBytes -> - notsup_to_error(hmac_update_nif(State, Data)); -hmac_update(State0, Data, _, MaxBytes) -> - <<Increment:MaxBytes/binary, Rest/binary>> = Data, - State = notsup_to_error(hmac_update_nif(State0, Increment)), - hmac_update(State, Rest, erlang:byte_size(Rest), MaxBytes). - -hmac_nif(_Type, _Key, _Data) -> ?nif_stub. -hmac_nif(_Type, _Key, _Data, _MacSize) -> ?nif_stub. -hmac_init_nif(_Type, _Key) -> ?nif_stub. -hmac_update_nif(_Context, _Data) -> ?nif_stub. -hmac_final_nif(_Context) -> ?nif_stub. -hmac_final_nif(_Context, _MacSize) -> ?nif_stub. - -%% CMAC -cmac_nif(_Type, _Key, _Data) -> ?nif_stub. - -%% POLY1305 -poly1305_nif(_Key, _Data) -> ?nif_stub. - - %% CIPHERS -------------------------------------------------------------------- cipher_info_nif(_Type) -> ?nif_stub. diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl index 56691223c4..0da70d5592 100644 --- a/lib/crypto/test/crypto_SUITE.erl +++ b/lib/crypto/test/crypto_SUITE.erl @@ -176,19 +176,19 @@ groups() -> ]}, {md4, [], [hash]}, - {md5, [], [hash, hmac]}, + {md5, [], [hash, hmac, hmac_update]}, {ripemd160, [], [hash]}, - {sha, [], [hash, hmac]}, - {sha224, [], [hash, hmac]}, - {sha256, [], [hash, hmac]}, - {sha384, [], [hash, hmac]}, - {sha512, [], [hash, hmac]}, - {sha3_224, [], [hash, hmac]}, - {sha3_256, [], [hash, hmac]}, - {sha3_384, [], [hash, hmac]}, - {sha3_512, [], [hash, hmac]}, - {blake2b, [], [hash, hmac]}, - {blake2s, [], [hash, hmac]}, + {sha, [], [hash, hmac, hmac_update]}, + {sha224, [], [hash, hmac, hmac_update]}, + {sha256, [], [hash, hmac, hmac_update]}, + {sha384, [], [hash, hmac, hmac_update]}, + {sha512, [], [hash, hmac, hmac_update]}, + {sha3_224, [], [hash, hmac, hmac_update]}, + {sha3_256, [], [hash, hmac, hmac_update]}, + {sha3_384, [], [hash, hmac, hmac_update]}, + {sha3_512, [], [hash, hmac, hmac_update]}, + {blake2b, [], [hash, hmac, hmac_update]}, + {blake2s, [], [hash, hmac, hmac_update]}, {no_blake2b, [], [no_hash, no_hmac]}, {no_blake2s, [], [no_hash, no_hmac]}, {rsa, [], [sign_verify, @@ -265,9 +265,9 @@ groups() -> %% New cipher nameing schema {des_ede3_cbc, [], [api_ng, api_ng_one_shot, api_ng_tls]}, {des_ede3_cfb, [], [api_ng, api_ng_one_shot, api_ng_tls]}, - {aes_128_cbc, [], [api_ng, api_ng_one_shot, api_ng_tls]}, + {aes_128_cbc, [], [api_ng, api_ng_one_shot, api_ng_tls, cmac]}, {aes_192_cbc, [], [api_ng, api_ng_one_shot, api_ng_tls]}, - {aes_256_cbc, [], [api_ng, api_ng_one_shot, api_ng_tls]}, + {aes_256_cbc, [], [api_ng, api_ng_one_shot, api_ng_tls, cmac]}, {aes_128_ctr, [], [api_ng, api_ng_one_shot, api_ng_tls]}, {aes_192_ctr, [], [api_ng, api_ng_one_shot, api_ng_tls]}, {aes_256_ctr, [], [api_ng, api_ng_one_shot, api_ng_tls]}, @@ -386,7 +386,7 @@ init_per_testcase(info, Config) -> init_per_testcase(cmac, Config) -> case is_supported(cmac) of true -> - Config; + configure_mac(cmac, proplists:get_value(type,Config), Config); false -> {skip, "CMAC is not supported"} end; @@ -405,6 +405,8 @@ init_per_testcase(generate, Config) -> end; _ -> Config end; +init_per_testcase(hmac, Config) -> + configure_mac(hmac, proplists:get_value(type,Config), Config); init_per_testcase(_Name,Config) -> Config. @@ -452,27 +454,41 @@ no_hash(Config) when is_list(Config) -> notsup(fun crypto:hash_init/1, [Type]). %%-------------------------------------------------------------------- hmac() -> - [{doc, "Test all different hmac functions"}]. + [{doc, "Test hmac function"}]. hmac(Config) when is_list(Config) -> - {Type, Keys, DataLE, Expected} = proplists:get_value(hmac, Config), - Data = lazy_eval(DataLE), - hmac(Type, Keys, Data, Expected), - hmac(Type, lists:map(fun iolistify/1, Keys), lists:map(fun iolistify/1, Data), Expected), - hmac_increment(Type). + Tuples = lazy_eval(proplists:get_value(hmac, Config)), + lists:foreach(fun hmac_check/1, Tuples), + lists:foreach(fun hmac_check/1, mac_listify(Tuples)). + %%-------------------------------------------------------------------- no_hmac() -> [{doc, "Test all disabled hmac functions"}]. no_hmac(Config) when is_list(Config) -> Type = ?config(type, Config), - notsup(fun crypto:hmac/3, [Type, <<"Key">>, <<"Hi There">>]), + notsup(fun crypto:hmac/3, [Type, <<"Key">>, <<"Hi There">>]). + +%%-------------------------------------------------------------------- +hmac_update() -> + [{doc, "Test all incremental hmac functions"}]. +hmac_update(Config) -> + Type = ?config(type, Config), + hmac_increment(Type). + +%%-------------------------------------------------------------------- +no_hmac_update() -> + [{doc, "Test all disabled incremental hmac functions"}]. +no_hmac_update(Config) -> + Type = ?config(type, Config), notsup(fun crypto:hmac_init/2, [Type, <<"Key">>]). + %%-------------------------------------------------------------------- cmac() -> [{doc, "Test all different cmac functions"}]. cmac(Config) when is_list(Config) -> Pairs = lazy_eval(proplists:get_value(cmac, Config)), lists:foreach(fun cmac_check/1, Pairs), - lists:foreach(fun cmac_check/1, cmac_iolistify(Pairs)). + lists:foreach(fun cmac_check/1, mac_listify(Pairs)). + %%-------------------------------------------------------------------- poly1305() -> [{doc, "Test poly1305 function"}]. @@ -957,33 +973,46 @@ hash_increment(State0, [Increment | Rest]) -> State = crypto:hash_update(State0, Increment), hash_increment(State, Rest). -hmac(_, [],[],[]) -> - ok; -hmac(sha = Type, [Key | Keys], [ <<"Test With Truncation">> = Data| Rest], [Expected | Expects]) -> - call_crypto_hmac([Type, Key, Data, 20], Type, Expected), - hmac(Type, Keys, Rest, Expects); -hmac(Type, [Key | Keys], [ <<"Test With Truncation">> = Data| Rest], [Expected | Expects]) -> - call_crypto_hmac([Type, Key, Data, 16], Type, Expected), - hmac(Type, Keys, Rest, Expects); -hmac(Type, [Key | Keys], [Data| Rest], [Expected | Expects]) -> - call_crypto_hmac([Type, Key, Data], Type, Expected), - hmac(Type, Keys, Rest, Expects). - -call_crypto_hmac(Args, Type, Expected) -> - try apply(crypto, hmac, Args) + +%%%---------------------------------------------------------------- +hmac_check({hmac, sha=Type, Key, <<"Test With Truncation">>=Data, Expected}) -> + do_hmac_check(Type, Key, Data, 20, Expected); +hmac_check({hmac, Type, Key, <<"Test With Truncation">>=Data, Expected}) -> + do_hmac_check(Type, Key, Data, 16, Expected); +hmac_check({hmac, Type, Key, Data, Expected}) -> + do_hmac_check(Type, Key, Data, Expected). + + +do_hmac_check(Type, Key, Data, Expected) -> + try crypto:hmac(Type, Key, Data) of Expected -> ok; Other -> - ct:fail({{crypto,hmac,Args}, {expected,Expected}, {got,Other}}) + ct:fail({{crypto,hmac,[Type,Key,Data]}, {expected,Expected}, {got,Other}}) catch error:notsup -> ct:fail("HMAC ~p not supported", [Type]); Class:Cause -> - ct:fail({{crypto,hmac,Args}, {expected,Expected}, {got,{Class,Cause}}}) + ct:fail({{crypto,hmac,[Type,Key,Data]}, {expected,Expected}, {got,{Class,Cause}}}) end. +do_hmac_check(Type, Key, Data, MacLength, Expected) -> + try crypto:hmac(Type, Key, Data, MacLength) + of + Expected -> + ok; + Other -> + ct:fail({{crypto,hmac,[Type,Key,Data,MacLength]}, {expected,Expected}, {got,Other}}) + catch + error:notsup -> + ct:fail("HMAC ~p not supported", [Type]); + Class:Cause -> + ct:fail({{crypto,hmac,[Type,Key,Data,MacLength]}, {expected,Expected}, {got,{Class,Cause}}}) + end. + +%%%---------------------------------------------------------------- hmac_increment(Type) -> Key = hmac_key(Type), Increments = hmac_inc(Type), @@ -1002,7 +1031,8 @@ hmac_increment(State0, [Increment | Rest]) -> State = crypto:hmac_update(State0, Increment), hmac_increment(State, Rest). -cmac_check({Type, Key, Text, CMac}) -> +%%%---------------------------------------------------------------- +cmac_check({cmac, Type, Key, Text, CMac}) -> ExpCMac = iolist_to_binary(CMac), case crypto:cmac(Type, Key, Text) of ExpCMac -> @@ -1010,7 +1040,7 @@ cmac_check({Type, Key, Text, CMac}) -> Other -> ct:fail({{crypto, cmac, [Type, Key, Text]}, {expected, ExpCMac}, {got, Other}}) end; -cmac_check({Type, Key, Text, Size, CMac}) -> +cmac_check({cmac, Type, Key, Text, Size, CMac}) -> ExpCMac = iolist_to_binary(CMac), case crypto:cmac(Type, Key, Text, Size) of ExpCMac -> @@ -1020,6 +1050,24 @@ cmac_check({Type, Key, Text, Size, CMac}) -> end. +mac_check({MacType, SubType, Key, Text, Mac}) -> + ExpMac = iolist_to_binary(Mac), + case crypto:mac(MacType, SubType, Key, Text) of + ExpMac -> + ok; + Other -> + ct:fail({{crypto, mac, [MacType, SubType, Key, Text]}, {expected, ExpMac}, {got, Other}}) + end; +mac_check({MacType, SubType, Key, Text, Size, Mac}) -> + ExpMac = iolist_to_binary(Mac), + case crypto:mac(MacType, SubType, Key, Text, Size) of + ExpMac -> + ok; + Other -> + ct:fail({{crypto, mac, [MacType, SubType, Key, Text]}, {expected, ExpMac}, {got, Other}}) + end. + + block_cipher({Type, Key, PlainText}) -> Plain = iolist_to_binary(PlainText), CipherText = crypto:block_encrypt(Type, Key, PlainText), @@ -1450,17 +1498,17 @@ decstr2int(S) -> is_supported(Group) -> lists:member(Group, lists:append([Algo || {_, Algo} <- crypto:supports()])). -cmac_iolistify(Blocks) -> - lists:map(fun do_cmac_iolistify/1, Blocks). +mac_listify(Blocks) -> + lists:map(fun do_mac_listify/1, Blocks). block_iolistify(Blocks) -> lists:map(fun do_block_iolistify/1, Blocks). stream_iolistify(Streams) -> lists:map(fun do_stream_iolistify/1, Streams). -do_cmac_iolistify({Type, Key, Text, CMac}) -> - {Type, iolistify(Key), iolistify(Text), CMac}; -do_cmac_iolistify({Type, Key, Text, Size, CMac}) -> - {Type, iolistify(Key), iolistify(Text), Size, CMac}. +do_mac_listify({MType, Type, Key, Text, CMac}) -> + {MType, Type, iolistify(Key), iolistify(Text), CMac}; +do_mac_listify({MType, Type, Key, Text, Size, CMac}) -> + {MType, Type, iolistify(Key), iolistify(Text), Size, CMac}. do_stream_iolistify({Type, Key, PlainText}) -> {Type, iolistify(Key), iolistify(PlainText)}; @@ -1694,10 +1742,7 @@ group_config(md4 = Type, Config) -> group_config(md5 = Type, Config) -> Msgs = rfc_1321_msgs(), Digests = rfc_1321_md5_digests(), - Keys = rfc_2202_md5_keys() ++ [long_hmac_key(md5)], - Data = rfc_2202_msgs() ++ [long_msg()], - Hmac = rfc_2202_hmac_md5() ++ [long_hmac(md5)], - [{hash, {Type, Msgs, Digests}}, {hmac, {Type, Keys, Data, Hmac}} | Config]; + [{hash, {Type, Msgs, Digests}} | Config]; group_config(ripemd160 = Type, Config) -> Msgs = ripemd160_msgs(), Digests = ripemd160_digests(), @@ -1705,56 +1750,41 @@ group_config(ripemd160 = Type, Config) -> group_config(sha = Type, Config) -> Msgs = [rfc_4634_test1(), rfc_4634_test2_1(),long_msg()], Digests = rfc_4634_sha_digests() ++ [long_sha_digest()], - Keys = rfc_2202_sha_keys() ++ [long_hmac_key(sha)], - Data = rfc_2202_msgs() ++ [long_msg()], - Hmac = rfc_2202_hmac_sha() ++ [long_hmac(sha)], - [{hash, {Type, Msgs, Digests}}, {hmac, {Type, Keys, Data, Hmac}} | Config]; + [{hash, {Type, Msgs, Digests}} | Config]; group_config(sha224 = Type, Config) -> Msgs = [rfc_4634_test1(), rfc_4634_test2_1()], Digests = rfc_4634_sha224_digests(), - Keys = rfc_4231_keys(), - Data = rfc_4231_msgs(), - Hmac = rfc4231_hmac_sha224(), - [{hash, {Type, Msgs, Digests}}, {hmac, {Type, Keys, Data, Hmac}} | Config]; + [{hash, {Type, Msgs, Digests}} | Config]; group_config(sha256 = Type, Config) -> Msgs = [rfc_4634_test1(), rfc_4634_test2_1(), long_msg()], Digests = rfc_4634_sha256_digests() ++ [long_sha256_digest()], - Keys = rfc_4231_keys() ++ [long_hmac_key(sha256)], - Data = rfc_4231_msgs() ++ [long_msg()], - Hmac = rfc4231_hmac_sha256() ++ [long_hmac(sha256)], - [{hash, {Type, Msgs, Digests}}, {hmac, {Type, Keys, Data, Hmac}} | Config]; + [{hash, {Type, Msgs, Digests}} | Config]; group_config(sha384 = Type, Config) -> Msgs = [rfc_4634_test1(), rfc_4634_test2(), long_msg()], Digests = rfc_4634_sha384_digests() ++ [long_sha384_digest()], - Keys = rfc_4231_keys() ++ [long_hmac_key(sha384)], - Data = rfc_4231_msgs() ++ [long_msg()], - Hmac = rfc4231_hmac_sha384() ++ [long_hmac(sha384)], - [{hash, {Type, Msgs, Digests}}, {hmac, {Type, Keys, Data, Hmac}} | Config]; + [{hash, {Type, Msgs, Digests}} | Config]; group_config(sha512 = Type, Config) -> Msgs = [rfc_4634_test1(), rfc_4634_test2(), long_msg()], Digests = rfc_4634_sha512_digests() ++ [long_sha512_digest()], - Keys = rfc_4231_keys() ++ [long_hmac_key(sha512)], - Data = rfc_4231_msgs() ++ [long_msg()], - Hmac = rfc4231_hmac_sha512() ++ [long_hmac(sha512)], - [{hash, {Type, Msgs, Digests}}, {hmac, {Type, Keys, Data, Hmac}} | Config]; + [{hash, {Type, Msgs, Digests}} | Config]; group_config(sha3_224 = Type, Config) -> {Msgs,Digests} = sha3_test_vectors(Type), - [{hash, {Type, Msgs, Digests}}, {hmac, hmac_sha3(Type)} | Config]; + [{hash, {Type, Msgs, Digests}} | Config]; group_config(sha3_256 = Type, Config) -> {Msgs,Digests} = sha3_test_vectors(Type), - [{hash, {Type, Msgs, Digests}}, {hmac, hmac_sha3(Type)} | Config]; + [{hash, {Type, Msgs, Digests}} | Config]; group_config(sha3_384 = Type, Config) -> {Msgs,Digests} = sha3_test_vectors(Type), - [{hash, {Type, Msgs, Digests}}, {hmac, hmac_sha3(Type)} | Config]; + [{hash, {Type, Msgs, Digests}} | Config]; group_config(sha3_512 = Type, Config) -> {Msgs,Digests} = sha3_test_vectors(Type), - [{hash, {Type, Msgs, Digests}}, {hmac, hmac_sha3(Type)} | Config]; + [{hash, {Type, Msgs, Digests}} | Config]; group_config(blake2b = Type, Config) -> {Msgs, Digests} = blake2_test_vectors(Type), - [{hash, {Type, Msgs, Digests}}, {hmac, blake2_hmac(Type)} | Config]; + [{hash, {Type, Msgs, Digests}} | Config]; group_config(blake2s = Type, Config) -> {Msgs, Digests} = blake2_test_vectors(Type), - [{hash, {Type, Msgs, Digests}}, {hmac, blake2_hmac(Type)} | Config]; + [{hash, {Type, Msgs, Digests}} | Config]; group_config(rsa, Config) -> Msg = rsa_plain(), Public = rsa_public(), @@ -1828,7 +1858,6 @@ group_config(Type, Config) when Type == ed25519 ; Type == ed448 -> group_config(srp, Config) -> GenerateCompute = [srp3(), srp6(), srp6a(), srp6a_smaller_prime()], [{generate_compute, GenerateCompute} | Config]; - group_config(ecdh, Config) -> Compute = ecdh(), Generate = ecc(), @@ -1836,19 +1865,6 @@ group_config(ecdh, Config) -> group_config(dh, Config) -> GenerateCompute = [dh()], [{generate_compute, GenerateCompute} | Config]; - -group_config(aes_cbc128 = Type, Config) -> - Block = fun() -> aes_cbc128(Config) end, - Pairs = fun() -> cmac_nist(Config, Type) end, - [{cipher, Block}, {cmac, Pairs} | Config]; -group_config(aes_cbc256 = Type, Config) -> - Block = fun() -> aes_cbc256(Config) end, - Pairs = fun() -> cmac_nist(Config, Type) end, - [{cipher, Block}, {cmac, Pairs} | Config]; -group_config(chacha20_poly1305, Config) -> - AEAD = chacha20_poly1305(Config), - [{cipher, AEAD} | Config]; - group_config(poly1305, Config) -> V = [%% {Key, Txt, Expect} {%% RFC7539 2.5.2 @@ -1864,6 +1880,76 @@ group_config(F, Config) -> [{cipher, TestVectors} | Config]. +configure_mac(MacType, SubType, Config) -> + case do_configure_mac(MacType, SubType, Config) of + undefined -> + {skip, io:format("No ~p test vectors for ~p", [MacType, SubType])}; + Pairs -> + [{MacType, Pairs} | Config] + end. + +do_configure_mac(hmac, Type, _Config) -> + case Type of + md5 -> + Keys = rfc_2202_md5_keys() ++ [long_hmac_key(md5)], + Data = rfc_2202_msgs() ++ [long_msg()], + Hmac = rfc_2202_hmac_md5() ++ [long_hmac(md5)], + zip3_special(hmac, Type, Keys, Data, Hmac); + sha -> + Keys = rfc_2202_sha_keys() ++ [long_hmac_key(sha)], + Data = rfc_2202_msgs() ++ [long_msg()], + Hmac = rfc_2202_hmac_sha() ++ [long_hmac(sha)], + zip3_special(hmac, Type, Keys, Data, Hmac); + sha224 -> + Keys = rfc_4231_keys(), + Data = rfc_4231_msgs(), + Hmac = rfc4231_hmac_sha224(), + zip3_special(hmac, Type, Keys, Data, Hmac); + sha256 -> + Keys = rfc_4231_keys() ++ [long_hmac_key(sha256)], + Data = rfc_4231_msgs() ++ [long_msg()], + Hmac = rfc4231_hmac_sha256() ++ [long_hmac(sha256)], + zip3_special(hmac, Type, Keys, Data, Hmac); + sha384 -> + Keys = rfc_4231_keys() ++ [long_hmac_key(sha384)], + Data = rfc_4231_msgs() ++ [long_msg()], + Hmac = rfc4231_hmac_sha384() ++ [long_hmac(sha384)], + zip3_special(hmac, Type, Keys, Data, Hmac); + sha512 -> + Keys = rfc_4231_keys() ++ [long_hmac_key(sha512)], + Data = rfc_4231_msgs() ++ [long_msg()], + Hmac = rfc4231_hmac_sha512() ++ [long_hmac(sha512)], + zip3_special(hmac, Type, Keys, Data, Hmac); + sha3_224 -> + hmac_sha3(Type); + sha3_256 -> + hmac_sha3(Type); + sha3_384 -> + hmac_sha3(Type); + sha3_512 -> + hmac_sha3(Type); + blake2b -> + blake2_hmac(Type); + blake2s -> + blake2_hmac(Type); + _ -> + undefined + end; +do_configure_mac(cmac, Cipher, Config) -> + case Cipher of + aes_128_cbc -> + fun() -> read_rsp(Config, Cipher, ["CMACGenAES128.rsp", "CMACVerAES128.rsp"]) end; + aes_256_cbc -> + fun() -> read_rsp(Config, Cipher, ["CMACGenAES256.rsp", "CMACVerAES256.rsp"]) end; + _ -> + undefined + end. + + +zip3_special(Type, SubType, As, Bs, Cs) -> + [{Type, SubType, A, B, C} + || {A,B,C} <- lists:zip3(As, Bs, Cs)]. + rsa_sign_verify_tests(Config, Msg, Public, Private, PublicS, PrivateS, OptsToTry) -> case ?config(fips, Config) of @@ -1981,10 +2067,8 @@ blake2_test_vectors(blake2s) -> ]}. blake2_hmac(Type) -> - {Ks, Ds, Hs} = lists:unzip3( - [ {hexstr2bin(K), hexstr2bin(D), H} - || {{K, D}, H} <- lists:zip(blake2_hmac_key_data(), blake2_hmac_hmac(Type)) ]), - {Type, Ks, Ds, Hs}. + [{hmac, Type, hexstr2bin(K), hexstr2bin(D), H} + || {{K, D}, H} <- lists:zip(blake2_hmac_key_data(), blake2_hmac_hmac(Type)) ]. blake2_hmac_key_data() -> [ {"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b 0b0b0b0b", @@ -2083,12 +2167,8 @@ hmac_sha3(Type) -> sha3_384 -> 3; sha3_512 -> 4 end, - {Keys, Datas, Hmacs} = - lists:unzip3( - [{hexstr2bin(Key), hexstr2bin(Data), hexstr2bin(element(N,Hmacs))} - || {Key,Data,Hmacs} <- hmac_sha3_data()]), - {Type, Keys, Datas, Hmacs}. - + [{hmac, Type, hexstr2bin(Key), hexstr2bin(Data), hexstr2bin(element(N,Hmacs))} + || {Key,Data,Hmacs} <- hmac_sha3_data()]. hmac_sha3_data() -> [ @@ -3843,14 +3923,6 @@ ecc() -> end, TestCases). -cmac_nist(Config, aes_cbc128 = Type) -> - read_rsp(Config, Type, - ["CMACGenAES128.rsp", "CMACVerAES128.rsp"]); - -cmac_nist(Config, aes_cbc256 = Type) -> - read_rsp(Config, Type, - ["CMACGenAES256.rsp", "CMACVerAES256.rsp"]). - int_to_bin(X) when X < 0 -> int_to_bin_neg(X, []); int_to_bin(X) -> int_to_bin_pos(X, []). @@ -4068,12 +4140,11 @@ parse_rsp_cmac(Type, Key0, Msg0, Mlen0, Tlen, MAC0, Next, State, Acc) -> Mlen = binary_to_integer(Mlen0), <<Msg:Mlen/bytes, _/binary>> = hexstr2bin(Msg0), MAC = hexstr2bin(MAC0), - case binary_to_integer(Tlen) of 0 -> - parse_rsp(Type, Next, State, [{Type, Key, Msg, MAC}|Acc]); + parse_rsp(Type, Next, State, [{cmac, Type, Key, Msg, MAC}|Acc]); I -> - parse_rsp(Type, Next, State, [{Type, Key, Msg, I, MAC}|Acc]) + parse_rsp(Type, Next, State, [{cmac, Type, Key, Msg, I, MAC}|Acc]) end. api_errors_ecdh(Config) when is_list(Config) -> diff --git a/lib/crypto/vsn.mk b/lib/crypto/vsn.mk index 72a51bfec9..2315cb3c48 100644 --- a/lib/crypto/vsn.mk +++ b/lib/crypto/vsn.mk @@ -1 +1 @@ -CRYPTO_VSN = 4.5 +CRYPTO_VSN = 4.5.1 diff --git a/lib/dialyzer/doc/src/dialyzer.xml b/lib/dialyzer/doc/src/dialyzer.xml index 443de7b0dd..8dd814982d 100644 --- a/lib/dialyzer/doc/src/dialyzer.xml +++ b/lib/dialyzer/doc/src/dialyzer.xml @@ -537,7 +537,10 @@ Option :: {files, [Filename :: string()]} 'plt_check' | 'plt_remove'} | {warnings, [WarnOpts]} - | {get_warnings, bool()} + | {get_warnings, boolean()} + | {native, boolean()} + %% Defaults to false when invoked from Erlang + | {native_cache, boolean()} WarnOpts :: error_handling | no_behaviours diff --git a/lib/dialyzer/doc/src/notes.xml b/lib/dialyzer/doc/src/notes.xml index dd0a2bfd7d..b72453aac7 100644 --- a/lib/dialyzer/doc/src/notes.xml +++ b/lib/dialyzer/doc/src/notes.xml @@ -32,6 +32,38 @@ <p>This document describes the changes made to the Dialyzer application.</p> +<section><title>Dialyzer 4.0.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> Make sure Dialyzer does not crash if the formatting + of results fails. Instead of crashing, an unformatted + version of the results is returned. </p> + <p> + Own Id: OTP-15922 Aux Id: PR-2240, ERL-949 </p> + </item> + </list> + </section> + +</section> + +<section><title>Dialyzer 4.0.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> Fix a bug that caused a crash when indenting a + Dialyzer warning mentioning more than one record field. + </p> + <p> + Own Id: OTP-15861 Aux Id: ERL-953 </p> + </item> + </list> + </section> + +</section> + <section><title>Dialyzer 4.0</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/dialyzer/src/dialyzer.erl b/lib/dialyzer/src/dialyzer.erl index cfe5fa9b3f..c1bc5ff597 100644 --- a/lib/dialyzer/src/dialyzer.erl +++ b/lib/dialyzer/src/dialyzer.erl @@ -605,11 +605,9 @@ ordinal(N) when is_integer(N) -> io_lib:format("~wth", [N]). %% Functions that parse type strings, literal strings, and contract %% strings. Return strings formatted by erl_pp. -%% If lib/hipe/cerl/erl_types.erl is compiled with DEBUG=true, -%% the contents of opaque types are showed inside brackets. -%% Since erl_parse:parse_form() cannot handle the bracket syntax, -%% no indentation is done. -%%-define(DEBUG , true). +%% Note we always have to catch any error when trying to parse +%% the syntax because other BEAM languages may not emit an +%% Erlang AST that transforms into valid Erlang Source Code. -define(IND, 10). @@ -620,13 +618,15 @@ con(M, F, Src, I) -> sig(Src, false) -> Src; sig(Src, true) -> - Str = lists:flatten(io_lib:format("-spec ~w:~tw~ts.", [a, b, Src])), - {ok, Tokens, _EndLocation} = erl_scan:string(Str), - exec(fun() -> - {ok, {attribute, _, spec, {_MFA, Types}}} = - erl_parse:parse_form(Tokens), - indentation(?IND) ++ pp_spec(Types) - end, Src). + try + Str = lists:flatten(io_lib:format("-spec ~w:~tw~ts.", [a, b, Src])), + {ok, Tokens, _EndLocation} = erl_scan:string(Str), + {ok, {attribute, _, spec, {_MFA, Types}}} = + erl_parse:parse_form(Tokens), + indentation(?IND) ++ pp_spec(Types) + catch + _:_ -> Src + end. %% Argument(list)s are a mix of types and Erlang code. Note: sometimes %% (contract_range, call_without_opaque, opaque_type_test), the initial @@ -642,11 +642,11 @@ c(Cerl, _I) -> field_diffs(Src, false) -> Src; field_diffs(Src, true) -> - Fields = string:split(Src, " and "), + Fields = string:split(Src, " and ", all), lists:join(" and ", [field_diff(Field) || Field <- Fields]). field_diff(Field) -> - [F | Ts] = string:split(Field, "::"), + [F | Ts] = string:split(Field, "::", all), F ++ " ::" ++ t(lists:flatten(lists:join("::", Ts)), true). rec_type("record "++Src, I) -> @@ -658,7 +658,7 @@ ps("pattern "++Src, I) -> ps("variable "++_=Src, _I) -> Src; ps("record field"++Rest, I) -> - [S, TypeStr] = string:split(Rest, "of type "), + [S, TypeStr] = string:split(Rest, "of type ", all), "record field" ++ S ++ "of type " ++ t(TypeStr, I). %% Scan and parse a type or a literal, and pretty-print it using erl_pp. @@ -681,21 +681,15 @@ ts(Src) -> [C1|Src1] = Src, % $< (product) or $( (arglist) [C2|RevSrc2] = lists:reverse(Src1), Src2 = lists:reverse(RevSrc2), - exec(fun() -> - Types = parse_types_and_literals(Src2), - CommaInd = [$, | Ind], - (indentation(?IND-1) ++ - [C1 | lists:join(CommaInd, [pp_type(Type) || Type <- Types])] ++ - [C2]) - end, Src). - --ifdef(DEBUG). -exec(F, R) -> - try F() catch _:_ -> R end. --else. -exec(F, _) -> - F(). --endif. + try + Types = parse_types_and_literals(Src2), + CommaInd = [$, | Ind], + (indentation(?IND-1) ++ + [C1 | lists:join(CommaInd, [pp_type(Type) || Type <- Types])] ++ + [C2]) + catch + _:_ -> Src + end. indentation(I) -> [$\n | lists:duplicate(I, $\s)]. diff --git a/lib/dialyzer/src/dialyzer.hrl b/lib/dialyzer/src/dialyzer.hrl index 4a12b9b671..e1821f10eb 100644 --- a/lib/dialyzer/src/dialyzer.hrl +++ b/lib/dialyzer/src/dialyzer.hrl @@ -160,7 +160,9 @@ indent_opt = ?INDENT_OPT :: iopt(), callgraph_file = "" :: file:filename(), check_plt = true :: boolean(), - solvers = [] :: [solver()]}). + solvers = [] :: [solver()], + native = maybe :: boolean() | 'maybe', + native_cache = true :: boolean()}). -record(contract, {contracts = [] :: [contract_pair()], args = [] :: [erl_types:erl_type()], diff --git a/lib/dialyzer/src/dialyzer_cl_parse.erl b/lib/dialyzer/src/dialyzer_cl_parse.erl index 280cae81d5..cadc2116b0 100644 --- a/lib/dialyzer/src/dialyzer_cl_parse.erl +++ b/lib/dialyzer/src/dialyzer_cl_parse.erl @@ -316,7 +316,9 @@ common_options() -> {use_spec, get(dialyzer_options_use_contracts)}, {warnings, get(dialyzer_warnings)}, {check_plt, get(dialyzer_options_check_plt)}, - {solvers, get(dialyzer_solvers)}]. + {solvers, get(dialyzer_solvers)}, + {native, get(dialyzer_options_native)}, + {native_cache, get(dialyzer_options_native_cache)}]. %%----------------------------------------------------------------------- diff --git a/lib/dialyzer/src/dialyzer_options.erl b/lib/dialyzer/src/dialyzer_options.erl index 3b30036c1c..f88f4f8ea2 100644 --- a/lib/dialyzer/src/dialyzer_options.erl +++ b/lib/dialyzer/src/dialyzer_options.erl @@ -197,6 +197,10 @@ build_options([{OptionName, Value} = Term|Rest], Options) -> solvers -> assert_solvers(Value), build_options(Rest, Options#options{solvers = Value}); + native -> + build_options(Rest, Options#options{native = Value}); + native_cache -> + build_options(Rest, Options#options{native_cache = Value}); _ -> bad_option("Unknown dialyzer command line option", Term) end; diff --git a/lib/dialyzer/test/small_SUITE_data/results/union_paren b/lib/dialyzer/test/small_SUITE_data/results/union_paren index 3a3526df89..1766773f2d 100644 --- a/lib/dialyzer/test/small_SUITE_data/results/union_paren +++ b/lib/dialyzer/test/small_SUITE_data/results/union_paren @@ -1,7 +1,25 @@ -union_paren.erl:12: Function t2/0 has no local return -union_paren.erl:13: The call union_paren:t2(3.14) breaks the contract (integer() | atom()) -> integer() -union_paren.erl:19: Function t3/0 has no local return -union_paren.erl:20: The pattern 3.14 can never match the type atom() | integer() -union_paren.erl:5: Function t1/0 has no local return -union_paren.erl:6: The call union_paren:t1(3.14) breaks the contract ((A::integer()) | (B::atom())) -> integer() +union_paren.erl:20: Function r1/0 has no local return +union_paren.erl:21: Record construction #r1{f1::[4,...],f2::'undefined',f3::'undefined',f8::float()} violates the declared type of field f2::[atom() | pid() | integer()] and f3::[atom() | pid() | integer()] and f8::[atom() | pid() | integer()] +union_paren.erl:23: Function t1/0 has no local return +union_paren.erl:24: The call union_paren:t1(3.14) breaks the contract ((A::integer()) | (B::atom())) -> integer() +union_paren.erl:30: Function t2/0 has no local return +union_paren.erl:31: The call union_paren:t2(3.14) breaks the contract (integer() | atom()) -> integer() +union_paren.erl:37: Function t3/0 has no local return +union_paren.erl:38: The pattern 3.14 can never match the type atom() | integer() +union_paren.erl:44: Function c1/0 has no local return +union_paren.erl:45: The call union_paren:c1({'r0', 'a', 'undefined', 'undefined'}) breaks the contract (#r0{f1::integer() | pid()}) -> atom() +union_paren.erl:51: Function c2/0 has no local return +union_paren.erl:52: The call union_paren:c2({'r0', 'a', 'undefined', 'undefined'}) breaks the contract (#r0{f1::A::integer() | pid()}) -> atom() +union_paren.erl:58: Function c3/0 has no local return +union_paren.erl:59: The call union_paren:c3({'r0', 'a', 'undefined', 'undefined'}) breaks the contract (#r0{f1::(A::integer()) | (B::pid())}) -> atom() +union_paren.erl:65: Function c4/0 has no local return +union_paren.erl:66: The call union_paren:c4({'r0', 'a', 'undefined', 'undefined'}) breaks the contract (#r0{f1::X::(A::integer()) | (B::pid())}) -> atom() +union_paren.erl:72: Function c5/0 has no local return +union_paren.erl:73: The call union_paren:c5({'r1', ['a'], [1], ['a'], ['u']}) breaks the contract (#r1{f1::[integer()] | [pid()]}) -> atom() +union_paren.erl:79: Function c6/0 has no local return +union_paren.erl:80: The call union_paren:c6({'r1', ['a'], [1], ['a'], ['u']}) breaks the contract (#r1{f1::A::[integer()] | [pid()]}) -> atom() +union_paren.erl:86: Function c7/0 has no local return +union_paren.erl:87: The call union_paren:c7({'r1', ['a'], [1], ['a'], ['u']}) breaks the contract (#r1{f1::(A::[integer()]) | (B::[pid()])}) -> atom() +union_paren.erl:93: Function c8/0 has no local return +union_paren.erl:94: The call union_paren:c8({'r1', ['a'], [1], ['a'], ['u']}) breaks the contract (#r1{f1::X::(A::[integer()]) | (B::[pid()])}) -> atom() diff --git a/lib/dialyzer/test/small_SUITE_data/src/union_paren.erl b/lib/dialyzer/test/small_SUITE_data/src/union_paren.erl index 4691a57d98..65bda1876e 100644 --- a/lib/dialyzer/test/small_SUITE_data/src/union_paren.erl +++ b/lib/dialyzer/test/small_SUITE_data/src/union_paren.erl @@ -2,6 +2,24 @@ -compile(export_all). +-record(r0, + { + f1 = 4 :: atom () | integer() | pid(), + f2 :: atom() | integer() | pid(), + f3 :: A :: atom() | integer() | pid + }). + +-record(r1, + { + f1 = [4] :: [atom ()] | [integer()] | [pid()], + f2 :: [atom()] | [integer()] | [pid()], + f3 :: A :: [atom()] | [integer()] | [pid()], + f8 = [u] :: X :: [A :: atom()] | [B :: integer()] | (C :: [pid()]) + }). + +r1() -> + #r1{f8 = 3.14}. + t1() -> t1(3.14). @@ -22,3 +40,59 @@ t3() -> -spec t3(_) -> (I :: integer()) | (A :: atom()). t3(A) when is_atom(A) -> A; t3(I) when is_integer(I) -> I. + +c1() -> + c1(#r0{f1 = a}). + +-spec c1(#r0{f1 :: integer() | pid()}) -> atom(). +c1(_) -> + a. + +c2() -> + c2(#r0{f1 = a}). + +-spec c2(#r0{f1 :: A :: integer() | pid()}) -> atom(). +c2(_) -> + a. + +c3() -> + c3(#r0{f1 = a}). + +-spec c3(#r0{f1 :: (A :: integer()) | (B :: pid())}) -> atom(). +c3(_) -> + a. + +c4() -> + c4(#r0{f1 = a}). + +-spec c4(#r0{f1 :: X :: (A :: integer()) | (B :: pid())}) -> atom(). +c4(_) -> + a. + +c5() -> + c5(#r1{f1 = [a], f2 = [1], f3 = [a]}). + +-spec c5(#r1{f1 :: [integer()] | [pid()]}) -> atom(). +c5(_) -> + a. + +c6() -> + c6(#r1{f1 = [a], f2 = [1], f3 = [a]}). + +-spec c6(#r1{f1 :: A :: [integer()] | [pid()]}) -> atom(). +c6(_) -> + a. + +c7() -> + c7(#r1{f1 = [a], f2 = [1], f3 = [a]}). + +-spec c7(#r1{f1 :: (A :: [integer()]) | (B :: [pid()])}) -> atom(). +c7(_) -> + a. + +c8() -> + c8(#r1{f1 = [a], f2 = [1], f3 = [a]}). + +-spec c8(#r1{f1 :: X :: (A :: [integer()]) | (B :: [pid()])}) -> atom(). +c8(_) -> + a. diff --git a/lib/dialyzer/vsn.mk b/lib/dialyzer/vsn.mk index 95984c7c85..7a44daf683 100644 --- a/lib/dialyzer/vsn.mk +++ b/lib/dialyzer/vsn.mk @@ -1 +1 @@ -DIALYZER_VSN = 4.0 +DIALYZER_VSN = 4.0.2 diff --git a/lib/erl_interface/src/decode/decode_fun.c b/lib/erl_interface/src/decode/decode_fun.c index 3a7a2b01c1..db71007505 100644 --- a/lib/erl_interface/src/decode/decode_fun.c +++ b/lib/erl_interface/src/decode/decode_fun.c @@ -77,10 +77,12 @@ int ei_decode_fun(const char *buf, int *index, erlang_fun *p) } if (p != NULL) { p->u.closure.n_free_vars = n; - p->u.closure.free_var_len = ix - ix0; - p->u.closure.free_vars = ei_malloc(ix - ix0); - if (!(p->u.closure.free_vars)) return -1; - memcpy(p->u.closure.free_vars, s + ix0, ix - ix0); + p->u.closure.free_var_len = ix - ix0; + if (p->u.closure.free_var_len > 0) { + p->u.closure.free_vars = ei_malloc(p->u.closure.free_var_len); + if (!(p->u.closure.free_vars)) return -1; + memcpy(p->u.closure.free_vars, s + ix0, p->u.closure.free_var_len); + } } s += ix; *index += s-s0; @@ -146,6 +148,7 @@ int ei_decode_fun(const char *buf, int *index, erlang_fun *p) else { p_arity = NULL; } + ix = 0; if (ei_decode_atom_as(s, &ix, p_module, MAXATOMLEN_UTF8, ERLANG_UTF8, NULL, NULL) < 0) return -1; @@ -171,6 +174,8 @@ int ei_decode_fun(const char *buf, int *index, erlang_fun *p) } if (ei_decode_long(s, &ix, p_arity) < 0) return -1; + s += ix; + *index += s - s0; return 0; } default: diff --git a/lib/erl_interface/src/decode/decode_skip.c b/lib/erl_interface/src/decode/decode_skip.c index 736c00e074..0622ce7d59 100644 --- a/lib/erl_interface/src/decode/decode_skip.c +++ b/lib/erl_interface/src/decode/decode_skip.c @@ -97,6 +97,7 @@ int ei_skip_term(const char* buf, int* index) break; case ERL_FUN_EXT: case ERL_NEW_FUN_EXT: + case ERL_EXPORT_EXT: if (ei_decode_fun(buf, index, NULL) < 0) return -1; break; default: diff --git a/lib/erl_interface/src/misc/ei_printterm.c b/lib/erl_interface/src/misc/ei_printterm.c index 5c40fb7747..aee7f7eeb0 100644 --- a/lib/erl_interface/src/misc/ei_printterm.c +++ b/lib/erl_interface/src/misc/ei_printterm.c @@ -121,8 +121,10 @@ static int print_term(FILE* fp, ei_x_buff* x, erlang_pid pid; erlang_port port; erlang_ref ref; + erlang_fun fun; double d; long l; + const char* delim; int tindex = *index; @@ -239,41 +241,47 @@ static int print_term(FILE* fp, ei_x_buff* x, m = BINPRINTSIZE; else m = l; - --m; + delim = ""; for (i = 0; i < m; ++i) { - ch_written += xprintf(fp, x, "%d,", p[i]); + ch_written += xprintf(fp, x, "%s%u", delim, (unsigned char)p[i]); + delim = ","; } - ch_written += xprintf(fp, x, "%d", p[i]); if (l > BINPRINTSIZE) ch_written += xprintf(fp, x, ",..."); xputc('>', fp, x); ++ch_written; ei_free(p); break; case ERL_BIT_BINARY_EXT: { - const char* cp; + const unsigned char* cp; size_t bits; unsigned int bitoffs; int trunc = 0; - if (ei_decode_bitstring(buf, index, &cp, &bitoffs, &bits) < 0 + if (ei_decode_bitstring(buf, index, (const char**)&cp, &bitoffs, &bits) < 0 || bitoffs != 0) { goto err; } ch_written += xprintf(fp, x, "#Bits<"); - m = (bits+7) / 8; - if (m > BINPRINTSIZE) { + if ((bits+7) / 8 > BINPRINTSIZE) { m = BINPRINTSIZE; trunc = 1; } - --m; + else + m = bits / 8; + + delim = ""; for (i = 0; i < m; ++i) { - ch_written += xprintf(fp, x, "%d,", cp[i]); + ch_written += xprintf(fp, x, "%s%u", delim, cp[i]); + delim = ","; } - ch_written += xprintf(fp, x, "%d", cp[i]); if (trunc) ch_written += xprintf(fp, x, ",..."); - else if (bits % 8 != 0) - ch_written += xprintf(fp, x, ":%u", (unsigned)(bits % 8)); + else { + bits %= 8; + if (bits) + ch_written += xprintf(fp, x, "%s%u:%u", delim, + (cp[i] >> (8-bits)), bits); + } xputc('>', fp, x); ++ch_written; break; } @@ -306,12 +314,46 @@ static int print_term(FILE* fp, ei_x_buff* x, } break; - case ERL_FLOAT_EXT: case NEW_FLOAT_EXT: if (ei_decode_double(buf, index, &d) < 0) goto err; ch_written += xprintf(fp, x, "%f", d); break; + case ERL_MAP_EXT: + if (ei_decode_map_header(buf, &tindex, &n) < 0) goto err; + ch_written += xprintf(fp, x, "#{"); + for (i = 0; i < n; ++i) { + r = print_term(fp, x, buf, &tindex); + if (r < 0) goto err; + ch_written += r; + ch_written += xprintf(fp, x, " => "); + r = print_term(fp, x, buf, &tindex); + if (r < 0) goto err; + ch_written += r; + if (i < n-1) { + xputs(", ", fp, x); ch_written += 2; + } + } + *index = tindex; + xputc('}', fp, x); ch_written++; + break; + case ERL_FUN_EXT: + case ERL_NEW_FUN_EXT: + case ERL_EXPORT_EXT: + if (ei_decode_fun(buf, &tindex, &fun) < 0) goto err; + if (fun.type == EI_FUN_EXPORT) { + ch_written += xprintf(fp, x, "fun %s:%s/%ld", + fun.module, + fun.u.exprt.func, + fun.arity); + } else { + ch_written += xprintf(fp, x, "#Fun{%s.%ld.%lu}", + fun.module, + fun.u.closure.index, + fun.u.closure.uniq); + } + *index = tindex; + break; default: goto err; } diff --git a/lib/erl_interface/test/ei_decode_encode_SUITE.erl b/lib/erl_interface/test/ei_decode_encode_SUITE.erl index 3451d9f503..0204b4cfd6 100644 --- a/lib/erl_interface/test/ei_decode_encode_SUITE.erl +++ b/lib/erl_interface/test/ei_decode_encode_SUITE.erl @@ -48,7 +48,8 @@ init_per_testcase(Case, Config) -> test_ei_decode_encode(Config) when is_list(Config) -> P = runner:start(Config, ?test_ei_decode_encode), - Fun = fun (X) -> {X,true} end, + Fun1 = fun (X) -> {X,true} end, + Fun2 = fun runner:init_per_testcase/3, Pid = self(), Port = case os:type() of {win32,_} -> @@ -70,7 +71,8 @@ test_ei_decode_encode(Config) when is_list(Config) -> BigLargeB = 1 bsl 11112 + BigSmallB, BigLargeC = BigSmallA * BigSmallB * BigSmallC * BigSmallA, - send_rec(P, Fun), + send_rec(P, Fun1), + send_rec(P, Fun2), send_rec(P, Pid), send_rec(P, Port), send_rec(P, Ref), @@ -115,7 +117,7 @@ test_ei_decode_encode(Config) when is_list(Config) -> send_rec(P, {}), send_rec(P, {atom, Pid, Port, Ref}), send_rec(P, [atom, Pid, Port, Ref]), - send_rec(P, [atom | Fun]), + send_rec(P, [atom | Fun1]), send_rec(P, #{}), send_rec(P, #{key => value}), send_rec(P, maps:put(Port, Ref, #{key => value, key2 => Pid})), diff --git a/lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c b/lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c index 85ca6c56e9..512f9ed0c7 100644 --- a/lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c +++ b/lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c @@ -564,6 +564,7 @@ TESTCASE(test_ei_decode_encode) ei_init(); decode_encode_one(&fun_type); + decode_encode_one(&fun_type); decode_encode_one(&pid_type); decode_encode_one(&port_type); decode_encode_one(&ref_type); diff --git a/lib/erl_interface/test/ei_print_SUITE.erl b/lib/erl_interface/test/ei_print_SUITE.erl index c75ce55a7d..8a35b22ae5 100644 --- a/lib/erl_interface/test/ei_print_SUITE.erl +++ b/lib/erl_interface/test/ei_print_SUITE.erl @@ -26,7 +26,8 @@ -export([all/0, suite/0, init_per_testcase/2, - atoms/1, tuples/1, lists/1, strings/1]). + atoms/1, tuples/1, lists/1, strings/1, + maps/1, funs/1, binaries/1, bitstrings/1]). -import(runner, [get_term/1]). @@ -36,8 +37,8 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. -all() -> - [atoms, tuples, lists, strings]. +all() -> + [atoms, tuples, lists, strings, maps, funs, binaries, bitstrings]. init_per_testcase(Case, Config) -> runner:init_per_testcase(?MODULE, Case, Config). @@ -142,3 +143,64 @@ strings(Config) when is_list(Config) -> runner:recv_eot(P), ok. + +maps(Config) -> + P = runner:start(Config, ?maps), + + {term, "#{}"} = get_term(P), + {term, "#{key => value}"} = get_term(P), + {term, "#{key => value, another_key => {ok, 42}}"} = get_term(P), + + runner:recv_eot(P), + ok. + +funs(Config) -> + P = runner:start(Config, ?funs), + + {term, "#Fun{some_module.42.3735928559}"} = get_term(P), + {term, "#Fun{some_module.37.195935983}"} = get_term(P), + {term, "fun erlang:abs/1"} = get_term(P), + + runner:recv_eot(P), + ok. + +binaries(Config) -> + P = runner:start(Config, ?binaries), + + "#Bin<>" = send_term_get_printed(P, <<>>), + "#Bin<1,2,3>" = send_term_get_printed(P, <<1,2,3>>), + "#Bin<0,127,128,255>" = send_term_get_printed(P, <<0,127,128,255>>), + Bin30 = <<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>>, + "#Bin<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>" + = send_term_get_printed(P, Bin30), + "#Bin<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,...>" + = send_term_get_printed(P, <<Bin30/binary,31>>), + + runner:recv_eot(P), + ok. + +bitstrings(Config) -> + P = runner:start(Config, ?bitstrings), + + "#Bits<1:1>" = send_term_get_printed(P, <<1:1>>), + "#Bits<123:7>" = send_term_get_printed(P, <<123:7>>), + "#Bits<1,2,3:4>" = send_term_get_printed(P, <<1,2,3:4>>), + "#Bits<0,127,128,255,126:7>" = send_term_get_printed(P, <<0,127,128,255,-2:7>>), + Bits30 = <<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:5>>, + "#Bits<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:5>" + = send_term_get_printed(P, Bits30), + "#Bin<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,241>" + = send_term_get_printed(P, <<Bits30/bits,1:3>>), + "#Bits<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,240,...>" + = send_term_get_printed(P, <<Bits30/bits,1:4>>), + + runner:recv_eot(P), + ok. + + + +send_term_get_printed(Port, Term) -> + Port ! {self(), {command, term_to_binary(Term)}}, + {term, String} = get_term(Port), + String. diff --git a/lib/erl_interface/test/ei_print_SUITE_data/ei_print_test.c b/lib/erl_interface/test/ei_print_SUITE_data/ei_print_test.c index 80be3016e6..27d4153250 100644 --- a/lib/erl_interface/test/ei_print_SUITE_data/ei_print_test.c +++ b/lib/erl_interface/test/ei_print_SUITE_data/ei_print_test.c @@ -29,6 +29,46 @@ */ static void +send_printed_buf(ei_x_buff* x) +{ + char* b = NULL; + char fn[256]; + char *tmp = getenv("temp"); + FILE* f; + int n, index = 0, ver; + +#ifdef VXWORKS + tmp = "."; +#else + if (tmp == NULL) { + tmp = "/tmp"; + } +#endif + strcpy(fn, tmp); + strcat(fn, "/ei_print_test.txt"); + f = fopen(fn, "w+"); + ei_decode_version(x->buff, &index, &ver); + n = ei_print_term(f, x->buff, &index); + if (n < 0) { + fclose(f); + x->index = 0; + ei_x_format(x, "~s", "ERROR: term decoding failed"); + send_bin_term(x); + } else { + fseek(f, 0, SEEK_SET); + b = malloc(n+1); + fread(b, 1, n, f); + b[n] = '\0'; + fclose(f); + x->index = 0; + ei_x_format(x, "~s", b); + send_bin_term(x); + free(b); + } +} + + +static void send_printed3(char* format, char* p1, char* p2, int fl) { char* b = NULL; @@ -43,25 +83,7 @@ send_printed3(char* format, char* p1, char* p2, int fl) } else { ei_x_format(&x, format, p1, p2); } -#ifdef VXWORKS - tmp = "."; -#else - if (tmp == NULL) tmp = "/tmp"; -#endif - strcpy(fn, tmp); - strcat(fn, "/ei_print_test.txt"); - f = fopen(fn, "w+"); - ei_decode_version(x.buff, &index, &ver); - n = ei_print_term(f, x.buff, &index); - fseek(f, 0, SEEK_SET); - b = malloc(n+1); - fread(b, 1, n, f); - b[n] = '\0'; - fclose(f); - x.index = 0; - ei_x_format(&x, "~s", b); - send_bin_term(&x); - free(b); + send_printed_buf(&x); ei_x_free(&x); } @@ -184,4 +206,146 @@ TESTCASE(strings) report(1); } +TESTCASE(maps) +{ + ei_x_buff x; + + ei_init(); + + ei_x_new_with_version(&x); + ei_x_encode_map_header(&x, 0); + send_printed_buf(&x); + ei_x_free(&x); + + ei_x_new_with_version(&x); + ei_x_encode_map_header(&x, 1); + ei_x_encode_atom(&x, "key"); + ei_x_encode_atom(&x, "value"); + send_printed_buf(&x); + ei_x_free(&x); + + ei_x_new_with_version(&x); + ei_x_encode_map_header(&x, 2); + ei_x_encode_atom(&x, "key"); + ei_x_encode_atom(&x, "value"); + ei_x_encode_atom(&x, "another_key"); + ei_x_encode_tuple_header(&x, 2); + ei_x_encode_atom(&x, "ok"); + ei_x_encode_long(&x, 42L); + send_printed_buf(&x); + ei_x_free(&x); + + report(1); +} + +TESTCASE(funs) +{ + ei_x_buff x; + erlang_pid self; + erlang_fun fun; + + strcpy(self.node, "node@host"); + self.num = 9; + self.serial = 99; + self.creation = 1; + + ei_init(); + + ei_x_new_with_version(&x); + fun.arity = -1; /* Will encode as FUN_EXT */ + strcpy(fun.module, "some_module"); + fun.type = EI_FUN_CLOSURE; + fun.u.closure.pid = self; + fun.u.closure.index = fun.u.closure.old_index = 42; + fun.u.closure.uniq = 0xDEADBEEF; + fun.u.closure.n_free_vars = 0; + fun.u.closure.free_var_len = 0; + ei_x_encode_fun(&x, &fun); + send_printed_buf(&x); + ei_x_free(&x); + + ei_x_new_with_version(&x); + fun.arity = 0; /* Will encode as NEW_FUN_EXT */ + strcpy(fun.module, "some_module"); + fun.type = EI_FUN_CLOSURE; + fun.u.closure.pid = self; + fun.u.closure.index = fun.u.closure.old_index = 37; + fun.u.closure.uniq = 0xBADBEEF; + fun.u.closure.n_free_vars = 0; + fun.u.closure.free_var_len = 0; + ei_x_encode_fun(&x, &fun); + send_printed_buf(&x); + ei_x_free(&x); + + ei_x_new_with_version(&x); + fun.arity = 1; + strcpy(fun.module, "erlang"); + fun.type = EI_FUN_EXPORT; + fun.u.exprt.func = "abs"; + ei_x_encode_fun(&x, &fun); + send_printed_buf(&x); + ei_x_free(&x); + + report(1); +} + +TESTCASE(binaries) +{ + char *buf; + long len; + int err, n, index; + ei_x_buff x; + + ei_init(); + + for (n = 5; n; n--) { + buf = read_packet(NULL); + + index = 0; + err = ei_decode_version(buf, &index, NULL); + if (err != 0) + fail1("ei_decode_version returned %d", err); + err = ei_decode_binary(buf, &index, NULL, &len); + if (err != 0) + fail1("ei_decode_binary returned %d", err); + + ei_x_new(&x); + ei_x_append_buf(&x, buf, index); + send_printed_buf(&x); + ei_x_free(&x); + + free_packet(buf); + } + report(1); +} + +TESTCASE(bitstrings) +{ + char *buf; + long len; + int err, n, index; + ei_x_buff x; + + ei_init(); + + for (n = 7; n; n--) { + buf = read_packet(NULL); + + index = 0; + err = ei_decode_version(buf, &index, NULL); + if (err != 0) + fail1("ei_decode_version returned %d", err); + err = ei_decode_bitstring(buf, &index, NULL, NULL, NULL); + if (err != 0) + fail1("ei_decode_bitstring returned %d", err); + + ei_x_new(&x); + ei_x_append_buf(&x, buf, index); + send_printed_buf(&x); + ei_x_free(&x); + + free_packet(buf); + } + report(1); +} diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml index 03bd1d8042..45533c4f4b 100644 --- a/lib/inets/doc/src/notes.xml +++ b/lib/inets/doc/src/notes.xml @@ -33,7 +33,23 @@ <file>notes.xml</file> </header> - <section><title>Inets 7.0.8</title> + <section><title>Inets 7.0.9</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fix a regression in http client that causes a crash when + request URI has no scheme.</p> + <p> + Own Id: OTP-15930 Aux Id: ERL-969 </p> + </item> + </list> + </section> + +</section> + +<section><title>Inets 7.0.8</title> <section><title>Fixed Bugs and Malfunctions</title> <list> diff --git a/lib/inets/src/http_client/httpc.erl b/lib/inets/src/http_client/httpc.erl index 24a205ced9..9967488f61 100644 --- a/lib/inets/src/http_client/httpc.erl +++ b/lib/inets/src/http_client/httpc.erl @@ -317,7 +317,7 @@ store_cookies(SetCookieHeaders, Url, Profile) {error, Bad, _} -> {error, {parse_failed, Bad}}; URI -> - Scheme = scheme_to_atom(maps:get(scheme, URI, '')), + Scheme = scheme_to_atom(maps:get(scheme, URI, undefined)), Host = maps:get(host, URI, ""), Port = maps:get(port, URI, default_port(Scheme)), Path = uri_string:recompose(#{path => maps:get(path, URI, "")}), @@ -536,7 +536,7 @@ handle_request(Method, Url, BracketedHost = proplists:get_value(ipv6_host_with_brackets, Options), - Scheme = scheme_to_atom(maps:get(scheme, URI, '')), + Scheme = scheme_to_atom(maps:get(scheme, URI, undefined)), Userinfo = maps:get(userinfo, URI, ""), Host = http_util:maybe_add_brackets(maps:get(host, URI, ""), BracketedHost), Port = maps:get(port, URI, default_port(Scheme)), @@ -591,8 +591,8 @@ scheme_to_atom("http") -> http; scheme_to_atom("https") -> https; -scheme_to_atom('') -> - ''; +scheme_to_atom(undefined) -> + throw({error, {no_scheme}}); scheme_to_atom(Scheme) -> throw({error, {bad_scheme, Scheme}}). diff --git a/lib/inets/src/http_server/httpd_request.erl b/lib/inets/src/http_server/httpd_request.erl index 9d7538a13d..f3e24263b8 100644 --- a/lib/inets/src/http_server/httpd_request.erl +++ b/lib/inets/src/http_server/httpd_request.erl @@ -196,9 +196,9 @@ parse_headers(<<?CR,?LF,?LF,Body/binary>>, [], [], Current, Max, Options, Result parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, [], [], Current, Max, Options, Result); -parse_headers(<<?LF,?LF,Body/binary>>, [], [], Current, Max, Options, Result) -> +parse_headers(<<?LF,?LF,Body/binary>>, Header, Headers, Current, Max, Options, Result) -> %% If ?CR is is missing RFC2616 section-19.3 - parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, [], [], Current, Max, + parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, Header, Headers, Current, Max, Options, Result); parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, [], [], _, _, _, Result) -> diff --git a/lib/inets/test/http_format_SUITE.erl b/lib/inets/test/http_format_SUITE.erl index 0a5aed67d5..3ff3ed4e97 100644 --- a/lib/inets/test/http_format_SUITE.erl +++ b/lib/inets/test/http_format_SUITE.erl @@ -414,6 +414,19 @@ http_request(Config) when is_list(Config) -> {max_content_length, ?HTTP_MAX_CONTENT_LENGTH} ]], HttpHead2), + %% If ?CR is is missing RFC2616 section-19.3 + HttpHead3 = ["GET http://www.erlang.org HTTP/1.1", [?LF], + "Accept: text/html", [?LF, ?LF]], + {"GET", + "http://www.erlang.org", + "HTTP/1.1", + {#http_request_h{}, [{"accept","text/html"}]}, <<>>} = + parse(httpd_request, parse, [[{max_header, ?HTTP_MAX_HEADER_SIZE}, + {max_version, ?HTTP_MAX_VERSION_STRING}, + {max_method, ?HTTP_MAX_METHOD_STRING}, + {max_content_length, ?HTTP_MAX_CONTENT_LENGTH} + ]], HttpHead3), + %% Note the following body is not related to the headers above HttpBody = ["<HTML>\n<HEAD>\n<TITLE> dummy </TITLE>\n</HEAD>\n<BODY>\n", "<H1>dummy</H1>\n</BODY>\n</HTML>\n"], diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl index d4b33ae2c6..8ca4f21928 100644 --- a/lib/inets/test/httpc_SUITE.erl +++ b/lib/inets/test/httpc_SUITE.erl @@ -106,7 +106,8 @@ real_requests()-> streaming_error, inet_opts, invalid_headers, - invalid_body + invalid_body, + no_scheme ]. real_requests_esi() -> @@ -1231,6 +1232,16 @@ invalid_body(Config) -> ok end. + +%%------------------------------------------------------------------------- + +no_scheme(_Config) -> + {error,{bad_scheme,"ftp"}} = httpc:request("ftp://foobar"), + {error,{no_scheme}} = httpc:request("//foobar"), + {error,{no_scheme}} = httpc:request("foobar"), + ok. + + %%------------------------------------------------------------------------- remote_socket_close(Config) when is_list(Config) -> URL = url(group_name(Config), "/just_close.html", Config), @@ -2180,7 +2191,7 @@ check_cookie([_Head | Tail]) -> content_length([]) -> 0; -content_length(["content-length:" ++ Value | _]) -> +content_length([{"content-length", Value}|_]) -> list_to_integer(string:strip(Value)); content_length([_Head | Tail]) -> content_length(Tail). diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk index 5dbec9e7b3..d948204618 100644 --- a/lib/inets/vsn.mk +++ b/lib/inets/vsn.mk @@ -19,6 +19,6 @@ # %CopyrightEnd% APPLICATION = inets -INETS_VSN = 7.0.8 +INETS_VSN = 7.0.9 PRE_VSN = APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)" diff --git a/lib/kernel/doc/src/gen_udp.xml b/lib/kernel/doc/src/gen_udp.xml index d20fc1fdfd..6c0d072fed 100644 --- a/lib/kernel/doc/src/gen_udp.xml +++ b/lib/kernel/doc/src/gen_udp.xml @@ -213,12 +213,93 @@ </func> <func> - <name name="send" arity="4" since=""/> + <name name="send" arity="3" since="OTP @OTP-15747@"/> <fsummary>Send a packet.</fsummary> <desc> <p> - Sends a packet to the specified address and port. Argument - <c><anno>Address</anno></c> can be a hostname or a socket address. + Sends a packet to the specified <c><anno>Destination</anno></c>. + </p> + <p> + This function is equivalent to + <seealso marker="#send-4-AncData"><c>send(<anno>Socket</anno>, <anno>Destination</anno>, [], <anno>Packet</anno>)</c></seealso>. + </p> + </desc> + </func> + + <func> + <name name="send" arity="4" clause_i="1" since=""/> + <fsummary>Send a packet.</fsummary> + <desc> + <p> + Sends a packet to the specified <c><anno>Host</anno></c> + and <c><anno>Port</anno></c>. + </p> + <p> + This clause is equivalent to + <seealso marker="#send/5"><c>send(<anno>Socket</anno>, <anno>Host</anno>, <anno>Port</anno>, [], <anno>Packet</anno>)</c></seealso>. + </p> + </desc> + </func> + + <func> + <name name="send" arity="4" clause_i="2" anchor="send-4-AncData" since="OTP @OTP-15747@"/> + <fsummary>Send a packet.</fsummary> + <desc> + <p> + Sends a packet to the specified <c><anno>Destination</anno></c> + with ancillary data <c><anno>AncData</anno></c>. + </p> + <note> + <p> + The ancillary data <c><anno>AncData</anno></c> + contains options that for this single message + override the default options for the socket, + an operation that may not be supported on all platforms, + and if so return <c>{error, einval}</c>. + Using more than one of an ancillary data item type + may also not be supported. + <c><anno>AncData</anno> =:= []</c> is always supported. + </p> + </note> + </desc> + </func> + + <func> + <name name="send" arity="4" clause_i="3" since="OTP @OTP-15747@"/> + <fsummary>Send a packet.</fsummary> + <desc> + <p> + Sends a packet to the specified <c><anno>Destination</anno></c>. + Since <c><anno>Destination</anno></c> is complete, + <c><anno>PortZero</anno></c> is redundant and has to be <c>0</c>. + </p> + <p> + This is a legacy clause mostly for + <c><anno>Destination</anno> = {local, Binary}</c> + where <c><anno>PortZero</anno></c> is superfluous. + It is equivalent to + <seealso marker="#send-4-AncData"><c>send(<anno>Socket</anno>, <anno>Destination</anno>, [], <anno>Packet</anno>)</c></seealso>, the clause right above here. + </p> + </desc> + </func> + + <func> + <name name="send" arity="5" since="OTP @OTP-15747@"/> + <fsummary>Send a packet.</fsummary> + <desc> + <p> + Sends a packet to the specified <c><anno>Host</anno></c> + and <c><anno>Port</anno></c>, + with ancillary data <c><anno>AncData</anno></c>. + </p> + <p> + Argument <c><anno>Host</anno></c> can be + a hostname or a socket address, + and <c><anno>Port</anno></c> can be a port number + or a service name atom. + These are resolved into a <c>Destination</c> and after that + this function is equivalent to + <seealso marker="#send-4-AncData"><c>send(<anno>Socket</anno>, Destination, <anno>AncData</anno>, <anno>Packet</anno>)</c></seealso>, read there about ancillary data. </p> </desc> </func> diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml index d4678ca5db..1011befca0 100644 --- a/lib/kernel/doc/src/inet.xml +++ b/lib/kernel/doc/src/inet.xml @@ -118,6 +118,42 @@ fe80::204:acff:fe17:bf38 <name name="port_number"/> </datatype> <datatype> + <name name="family_address" since="OTP @OTP-15747@"/> + <desc> + <p> + A general address format on the form <c>{Family, Destination}</c> + where <c>Family</c> is an atom such as <c>local</c> + and the format of <c>Destination</c> depends on <c>Family</c>, + and is a complete address + (for example an IP address including port number). + </p> + </desc> + </datatype> + <datatype> + <name name="inet_address" since="OTP @OTP-15747@"/> + <desc> + <warning> + <p> + This address format is for now experimental + and for completeness to make all address families have a + <c>{Family, Destination}</c> representation. + </p> + </warning> + </desc> + </datatype> + <datatype> + <name name="inet6_address" since="OTP @OTP-15747@"/> + <desc> + <warning> + <p> + This address format is for now experimental + and for completeness to make all address families have a + <c>{Family, Destination}</c> representation. + </p> + </warning> + </desc> + </datatype> + <datatype> <name name="local_address"/> <desc> <p> @@ -180,12 +216,16 @@ fe80::204:acff:fe17:bf38 <name name="ancillary_data"/> <desc> <p> - Ancillary data received with the data packet - or read with the socket option + Ancillary data received with the data packet, + read with the socket option <seealso marker="gen_tcp#type-pktoptions_value"> <c>pktoptions</c> </seealso> - from a TCP socket. + from a TCP socket, + or to set in a call to + <seealso marker="gen_udp#send-4-AncData"><c>gen_udp:send/4</c></seealso> + or + <seealso marker="gen_udp#send/5"><c>gen_udp:send/5</c></seealso>. </p> <p> The value(s) correspond to the currently active socket @@ -193,7 +233,9 @@ fe80::204:acff:fe17:bf38 <seealso marker="inet#option-recvtos"><c>recvtos</c></seealso>, <seealso marker="inet#option-recvtclass"><c>recvtclass</c></seealso> and - <seealso marker="inet#option-recvttl"><c>recvttl</c></seealso>. + <seealso marker="inet#option-recvttl"><c>recvttl</c></seealso>, + or for a single send operation the option(s) to override + the currently active socket option(s). </p> </desc> </datatype> diff --git a/lib/kernel/doc/src/logger_chapter.xml b/lib/kernel/doc/src/logger_chapter.xml index 1aa4b7a3a2..5aa2caadf0 100644 --- a/lib/kernel/doc/src/logger_chapter.xml +++ b/lib/kernel/doc/src/logger_chapter.xml @@ -89,8 +89,8 @@ <p>Filter functions can be used for more sophisticated filtering than the log level check provides. A filter function can stop or pass a log event, based on any of the event's contents. It can - also modify all parts of the log event. See see - section <seealso marker="#filters">Filters</seealso> for more + also modify all parts of the log event. See section + <seealso marker="#filters">Filters</seealso> for more details.</p> <p>If a log event passes through all primary filters and all handler filters for a specific handler, Logger forwards the diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml index 6f68a67174..4d31eeea3d 100644 --- a/lib/kernel/doc/src/notes.xml +++ b/lib/kernel/doc/src/notes.xml @@ -31,6 +31,24 @@ </header> <p>This document describes the changes made to the Kernel application.</p> +<section><title>Kernel 6.4.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p><c>user</c>/<c>user_drv</c> could respond to io + requests before they had been processed, which could + cause data to be dropped if the emulator was halted soon + after a call to <c>io:format/2</c>, such as in an + escript.</p> + <p> + Own Id: OTP-15805</p> + </item> + </list> + </section> + +</section> + <section><title>Kernel 6.4</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -129,6 +147,37 @@ </section> +<section><title>Kernel 6.3.1.2</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + The possibility to send ancillary data, in particular the + TOS field, has been added to <c>gen_udp:send/4,5</c>.</p> + <p> + Own Id: OTP-15747 Aux Id: ERIERL-294 </p> + </item> + </list> + </section> + +</section> + +<section><title>Kernel 6.3.1.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fix type spec for <c>seq_trace:set_token/2</c>.</p> + <p> + Own Id: OTP-15858 Aux Id: ERL-700 </p> + </item> + </list> + </section> + +</section> + <section><title>Kernel 6.3.1</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -6027,4 +6076,3 @@ </section> </section> </chapter> - diff --git a/lib/kernel/src/code.erl b/lib/kernel/src/code.erl index 7faef93609..964ede9bc9 100644 --- a/lib/kernel/src/code.erl +++ b/lib/kernel/src/code.erl @@ -19,6 +19,8 @@ %% -module(code). +-include_lib("kernel/include/logger.hrl"). + %% This is the interface module to the code server. It also contains %% some implementation details. See also related modules: code_*.erl %% in this directory. @@ -707,8 +709,20 @@ do_s(Lib) -> start_get_mode() -> case init:get_argument(mode) of - {ok,[["embedded"]]} -> - embedded; + {ok, [FirstMode | Rest]} -> + case Rest of + [] -> + ok; + _ -> + ?LOG_WARNING("Multiple -mode given to erl, using the first, ~p", + [FirstMode]) + end, + case FirstMode of + ["embedded"] -> + embedded; + _ -> + interactive + end; _ -> interactive end. diff --git a/lib/kernel/src/gen_sctp.erl b/lib/kernel/src/gen_sctp.erl index d893d44079..a63df54ff9 100644 --- a/lib/kernel/src/gen_sctp.erl +++ b/lib/kernel/src/gen_sctp.erl @@ -217,24 +217,29 @@ peeloff(S, AssocId) when is_port(S), is_integer(AssocId) -> Error -> Error end. --spec connect(Socket, Addr, Port, Opts) -> {ok, Assoc} | {error, inet:posix()} when +-spec connect(Socket, Addr, Port, Opts) -> + {ok, #sctp_assoc_change{state :: 'comm_up'}} | + {error, #sctp_assoc_change{state :: 'cant_assoc'}} | + {error, inet:posix()} + when Socket :: sctp_socket(), Addr :: inet:ip_address() | inet:hostname(), Port :: inet:port_number(), - Opts :: [Opt :: option()], - Assoc :: #sctp_assoc_change{}. + Opts :: [Opt :: option()]. connect(S, Addr, Port, Opts) -> connect(S, Addr, Port, Opts, infinity). -spec connect(Socket, Addr, Port, Opts, Timeout) -> - {ok, Assoc} | {error, inet:posix()} when + {ok, #sctp_assoc_change{state :: 'comm_up'}} | + {error, #sctp_assoc_change{state :: 'cant_assoc'}} | + {error, inet:posix()} + when Socket :: sctp_socket(), Addr :: inet:ip_address() | inet:hostname(), Port :: inet:port_number(), Opts :: [Opt :: option()], - Timeout :: timeout(), - Assoc :: #sctp_assoc_change{}. + Timeout :: timeout(). connect(S, Addr, Port, Opts, Timeout) -> case do_connect(S, Addr, Port, Opts, Timeout, true) of diff --git a/lib/kernel/src/gen_udp.erl b/lib/kernel/src/gen_udp.erl index fad7b2f887..3001948209 100644 --- a/lib/kernel/src/gen_udp.erl +++ b/lib/kernel/src/gen_udp.erl @@ -20,7 +20,7 @@ -module(gen_udp). -export([open/1, open/2, close/1]). --export([send/2, send/4, recv/2, recv/3, connect/3]). +-export([send/2, send/3, send/4, send/5, recv/2, recv/3, connect/3]). -export([controlling_process/2]). -export([fdopen/2]). @@ -125,20 +125,80 @@ open(Port, Opts0) -> close(S) -> inet:udp_close(S). --spec send(Socket, Address, Port, Packet) -> ok | {error, Reason} when +-spec send(Socket, Destination, Packet) -> ok | {error, Reason} when Socket :: socket(), - Address :: inet:socket_address() | inet:hostname(), - Port :: inet:port_number(), + Destination :: {inet:ip_address(), inet:port_number()} | + inet:family_address(), + Packet :: iodata(), + Reason :: not_owner | inet:posix(). +%%% +send(Socket, Destination, Packet) -> + send(Socket, Destination, [], Packet). + +-spec send(Socket, Host, Port, Packet) -> ok | {error, Reason} when + Socket :: socket(), + Host :: inet:hostname() | inet:ip_address(), + Port :: inet:port_number() | atom(), + Packet :: iodata(), + Reason :: not_owner | inet:posix(); +%%% + (Socket, Destination, AncData, Packet) -> ok | {error, Reason} when + Socket :: socket(), + Destination :: {inet:ip_address(), inet:port_number()} | + inet:family_address(), + AncData :: inet:ancillary_data(), + Packet :: iodata(), + Reason :: not_owner | inet:posix(); +%%% + (Socket, Destination, PortZero, Packet) -> ok | {error, Reason} when + Socket :: socket(), + Destination :: {inet:ip_address(), inet:port_number()} | + inet:family_address(), + PortZero :: inet:port_number(), Packet :: iodata(), Reason :: not_owner | inet:posix(). +%%% +send(S, {_,_} = Destination, PortZero = AncData, Packet) when is_port(S) -> + %% Destination is {Family,Addr} | {IP,Port}, + %% so it is complete - argument PortZero is redundant + if + PortZero =:= 0 -> + case inet_db:lookup_socket(S) of + {ok, Mod} -> + Mod:send(S, Destination, [], Packet); + Error -> + Error + end; + is_integer(PortZero) -> + %% Redundant PortZero; must be 0 + {error, einval}; + is_list(AncData) -> + case inet_db:lookup_socket(S) of + {ok, Mod} -> + Mod:send(S, Destination, AncData, Packet); + Error -> + Error + end + end; +send(S, Host, Port, Packet) when is_port(S) -> + send(S, Host, Port, [], Packet). -send(S, Address, Port, Packet) when is_port(S) -> +-spec send(Socket, Host, Port, AncData, Packet) -> ok | {error, Reason} when + Socket :: socket(), + Host :: inet:hostname() | inet:ip_address() | inet:local_address(), + Port :: inet:port_number() | atom(), + AncData :: inet:ancillary_data(), + Packet :: iodata(), + Reason :: not_owner | inet:posix(). +%%% +send(S, Host, Port, AncData, Packet) + when is_port(S), is_list(AncData) -> case inet_db:lookup_socket(S) of {ok, Mod} -> - case Mod:getaddr(Address) of + case Mod:getaddr(Host) of {ok,IP} -> case Mod:getserv(Port) of - {ok,UP} -> Mod:send(S, IP, UP, Packet); + {ok,P} -> Mod:send(S, {IP,P}, AncData, Packet); {error,einval} -> exit(badarg); Error -> Error end; @@ -149,6 +209,7 @@ send(S, Address, Port, Packet) when is_port(S) -> Error end. +%% Connected send send(S, Packet) when is_port(S) -> case inet_db:lookup_socket(S) of {ok, Mod} -> diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl index 9f22eb6aaa..24aff83fbd 100644 --- a/lib/kernel/src/inet.erl +++ b/lib/kernel/src/inet.erl @@ -75,7 +75,8 @@ -export_type([address_family/0, socket_protocol/0, hostent/0, hostname/0, ip4_address/0, ip6_address/0, ip_address/0, port_number/0, - local_address/0, socket_address/0, returned_non_ip_address/0, + family_address/0, local_address/0, + socket_address/0, returned_non_ip_address/0, socket_setopt/0, socket_getopt/0, ancillary_data/0, posix/0, socket/0, stat_option/0]). %% imports @@ -100,11 +101,16 @@ 0..65535,0..65535,0..65535,0..65535}. -type ip_address() :: ip4_address() | ip6_address(). -type port_number() :: 0..65535. --type local_address() :: {local, File :: binary() | string()}. +-type family_address() :: inet_address() | inet6_address() | local_address(). +-type inet_address() :: + {'inet', {ip4_address() | 'any' | 'loopback', port_number()}}. +-type inet6_address() :: + {'inet6', {ip6_address() | 'any' | 'loopback', port_number()}}. +-type local_address() :: {'local', File :: binary() | string()}. -type returned_non_ip_address() :: - {local, binary()} | - {unspec, <<>>} | - {undefined, any()}. + {'local', binary()} | + {'unspec', <<>>} | + {'undefined', any()}. -type posix() :: 'eaddrinuse' | 'eaddrnotavail' | 'eafnosupport' | 'ealready' | 'econnaborted' | 'econnrefused' | 'econnreset' | @@ -1639,6 +1645,7 @@ fmt_addr({ok,Addr}, Proto) -> {{0,0,0,0,0,0,0,0},Port} -> "*:" ++ fmt_port(Port, Proto); {{127,0,0,1},Port} -> "localhost:" ++ fmt_port(Port, Proto); {{0,0,0,0,0,0,0,1},Port} -> "localhost:" ++ fmt_port(Port, Proto); + {local, Path} -> "local:" ++ binary_to_list(Path); {IP,Port} -> inet_parse:ntoa(IP) ++ ":" ++ fmt_port(Port, Proto) end. diff --git a/lib/kernel/src/inet6_udp.erl b/lib/kernel/src/inet6_udp.erl index 71db0357cd..cb95a69798 100644 --- a/lib/kernel/src/inet6_udp.erl +++ b/lib/kernel/src/inet6_udp.erl @@ -65,16 +65,25 @@ open(Port, Opts) -> {ok, _} -> exit(badarg) end. -send(S, Addr = {A,B,C,D,E,F,G,H}, P, Data) - when ?ip6(A,B,C,D,E,F,G,H), ?port(P) -> - prim_inet:sendto(S, Addr, P, Data). +send(S, {A,B,C,D,E,F,G,H} = IP, Port, Data) + when ?ip6(A,B,C,D,E,F,G,H), ?port(Port) -> + prim_inet:sendto(S, {IP, Port}, [], Data); +send(S, {{A,B,C,D,E,F,G,H}, Port} = Addr, AncData, Data) + when ?ip6(A,B,C,D,E,F,G,H), ?port(Port), is_list(AncData) -> + prim_inet:sendto(S, Addr, AncData, Data); +send(S, {?FAMILY, {{A,B,C,D,E,F,G,H}, Port}} = Address, AncData, Data) + when ?ip6(A,B,C,D,E,F,G,H), ?port(Port), is_list(AncData) -> + prim_inet:sendto(S, Address, AncData, Data); +send(S, {?FAMILY, {loopback, Port}} = Address, AncData, Data) + when ?port(Port), is_list(AncData) -> + prim_inet:sendto(S, Address, AncData, Data). send(S, Data) -> - prim_inet:sendto(S, {0,0,0,0,0,0,0,0}, 0, Data). + prim_inet:sendto(S, {any, 0}, [], Data). -connect(S, Addr = {A,B,C,D,E,F,G,H}, P) - when ?ip6(A,B,C,D,E,F,G,H), ?port(P) -> - prim_inet:connect(S, Addr, P). +connect(S, Addr = {A,B,C,D,E,F,G,H}, Port) + when ?ip6(A,B,C,D,E,F,G,H), ?port(Port) -> + prim_inet:connect(S, Addr, Port). recv(S, Len) -> prim_inet:recvfrom(S, Len). diff --git a/lib/kernel/src/inet_udp.erl b/lib/kernel/src/inet_udp.erl index 1e624b9e90..083059a2dc 100644 --- a/lib/kernel/src/inet_udp.erl +++ b/lib/kernel/src/inet_udp.erl @@ -66,16 +66,25 @@ open(Port, Opts) -> {ok, _} -> exit(badarg) end. -send(S, {A,B,C,D} = Addr, P, Data) - when ?ip(A,B,C,D), ?port(P) -> - prim_inet:sendto(S, Addr, P, Data). +send(S, {A,B,C,D} = IP, Port, Data) + when ?ip(A,B,C,D), ?port(Port) -> + prim_inet:sendto(S, {IP, Port}, [], Data); +send(S, {{A,B,C,D}, Port} = Addr, AncData, Data) + when ?ip(A,B,C,D), ?port(Port), is_list(AncData) -> + prim_inet:sendto(S, Addr, AncData, Data); +send(S, {?FAMILY, {{A,B,C,D}, Port}} = Address, AncData, Data) + when ?ip(A,B,C,D), ?port(Port), is_list(AncData) -> + prim_inet:sendto(S, Address, AncData, Data); +send(S, {?FAMILY, {loopback, Port}} = Address, AncData, Data) + when ?port(Port), is_list(AncData) -> + prim_inet:sendto(S, Address, AncData, Data). send(S, Data) -> - prim_inet:sendto(S, {0,0,0,0}, 0, Data). + prim_inet:sendto(S, {any, 0}, [], Data). -connect(S, Addr = {A,B,C,D}, P) - when ?ip(A,B,C,D), ?port(P) -> - prim_inet:connect(S, Addr, P). +connect(S, Addr = {A,B,C,D}, Port) + when ?ip(A,B,C,D), ?port(Port) -> + prim_inet:connect(S, Addr, Port). recv(S, Len) -> prim_inet:recvfrom(S, Len). diff --git a/lib/kernel/src/kernel.appup.src b/lib/kernel/src/kernel.appup.src index cd0397a98c..95853a7a8f 100644 --- a/lib/kernel/src/kernel.appup.src +++ b/lib/kernel/src/kernel.appup.src @@ -38,7 +38,9 @@ {<<"^6\\.2\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, {<<"^6\\.3$">>,[restart_new_emulator]}, {<<"^6\\.3\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, - {<<"^6\\.3\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}], + {<<"^6\\.3\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^6\\.4$">>,[restart_new_emulator]}, + {<<"^6\\.4\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}], [{<<"^6\\.0$">>,[restart_new_emulator]}, {<<"^6\\.0\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, {<<"^6\\.0\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, @@ -50,4 +52,6 @@ {<<"^6\\.2\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, {<<"^6\\.3$">>,[restart_new_emulator]}, {<<"^6\\.3\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, - {<<"^6\\.3\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}]}. + {<<"^6\\.3\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^6\\.4$">>,[restart_new_emulator]}, + {<<"^6\\.4\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}]}. diff --git a/lib/kernel/src/kernel.erl b/lib/kernel/src/kernel.erl index bfa091a036..c8c631ab23 100644 --- a/lib/kernel/src/kernel.erl +++ b/lib/kernel/src/kernel.erl @@ -116,7 +116,7 @@ init([]) -> restart => temporary, shutdown => 2000, type => supervisor, - modules => [user_sup]}, + modules => [standard_error]}, User = #{id => user, start => {user_sup, start, []}, @@ -141,7 +141,7 @@ init([]) -> modules => [logger_sup]}, case init:get_argument(mode) of - {ok, [["minimal"]]} -> + {ok, [["minimal"]|_]} -> {ok, {SupFlags, [Code, File, StdError, User, LoggerSup, Config, RefC, SafeSup]}}; _ -> diff --git a/lib/kernel/src/local_udp.erl b/lib/kernel/src/local_udp.erl index 481a8c4910..933e56228b 100644 --- a/lib/kernel/src/local_udp.erl +++ b/lib/kernel/src/local_udp.erl @@ -70,11 +70,13 @@ open(0, Opts) -> {ok, _} -> exit(badarg) end. -send(S, Addr = {?FAMILY,_}, 0, Data) -> - prim_inet:sendto(S, Addr, 0, Data). +send(S, {?FAMILY,_} = Addr, 0, Data) -> + prim_inet:sendto(S, Addr, [], Data); +send(S, {?FAMILY,_} = Addr, AncData, Data) when is_list(AncData) -> + prim_inet:sendto(S, Addr, AncData, Data). %% send(S, Data) -> - prim_inet:sendto(S, {?FAMILY,<<>>}, 0, Data). + prim_inet:sendto(S, {?FAMILY,<<>>}, [], Data). connect(S, Addr = {?FAMILY,_}, 0) -> prim_inet:connect(S, Addr, 0). diff --git a/lib/kernel/src/logger_std_h.erl b/lib/kernel/src/logger_std_h.erl index c8f1acfca4..2b078ef091 100644 --- a/lib/kernel/src/logger_std_h.erl +++ b/lib/kernel/src/logger_std_h.erl @@ -170,9 +170,11 @@ check_h_config(_Type,[]) -> ok. normalize_config(#{type:={file,File}}=HConfig) -> - HConfig#{type=>file,file=>File}; + normalize_config(HConfig#{type=>file,file=>File}); normalize_config(#{type:={file,File,Modes}}=HConfig) -> - HConfig#{type=>file,file=>File,modes=>Modes}; + normalize_config(HConfig#{type=>file,file=>File,modes=>Modes}); +normalize_config(#{file:=File}=HConfig) -> + HConfig#{file=>filename:absname(File)}; normalize_config(HConfig) -> HConfig. @@ -188,7 +190,7 @@ merge_default_config(Name,Type,HConfig) -> get_default_config(Name,file) -> #{type => file, - file => atom_to_list(Name), + file => filename:absname(atom_to_list(Name)), modes => [raw,append], file_check => 0, max_no_bytes => infinity, diff --git a/lib/kernel/src/seq_trace.erl b/lib/kernel/src/seq_trace.erl index 4f9d7b3e5c..f0bd1fabe9 100644 --- a/lib/kernel/src/seq_trace.erl +++ b/lib/kernel/src/seq_trace.erl @@ -59,7 +59,7 @@ set_token({Flags,Label,Serial,_From,Lastcnt}) -> F = decode_flags(Flags), set_token2([{label,Label},{serial,{Lastcnt, Serial}} | F]). --spec set_token(Component, Val) -> {Component, OldVal} when +-spec set_token(Component, Val) -> OldVal when Component :: component(), Val :: value(), OldVal :: value(). diff --git a/lib/kernel/src/user.erl b/lib/kernel/src/user.erl index 0c9e1ea303..5a3487a9ba 100644 --- a/lib/kernel/src/user.erl +++ b/lib/kernel/src/user.erl @@ -296,7 +296,8 @@ io_requests([], Stat, _) -> %% port. put_port(List, Port) -> - send_port(Port, {command, List}). + true = port_command(Port, List), + ok. %% send_port(Port, Command) diff --git a/lib/kernel/src/user_drv.erl b/lib/kernel/src/user_drv.erl index 69ff8e7971..644aa752b6 100644 --- a/lib/kernel/src/user_drv.erl +++ b/lib/kernel/src/user_drv.erl @@ -543,19 +543,14 @@ set_unicode_state(Iport, Bool) -> %% io_request(Request, InPort, OutPort) %% io_requests(Requests, InPort, OutPort) %% Note: InPort is unused. - -io_request(Request, Iport, Oport) -> - try io_command(Request) of - {command,_} = Command -> - Oport ! {self(),Command}, - ok; - {Command,Reply} -> - Oport ! {self(),Command}, - Reply - catch - {requests,Rs} -> - io_requests(Rs, Iport, Oport); - _ -> +io_request({requests,Rs}, Iport, Oport) -> + io_requests(Rs, Iport, Oport); +io_request(Request, _Iport, Oport) -> + case io_command(Request) of + {Data, Reply} -> + true = port_command(Oport, Data), + Reply; + unhandled -> ok end. @@ -575,19 +570,19 @@ put_int16(N, Tail) -> %% to the console before the vm stops when calling erlang:halt(integer()). -dialyzer({no_improper_lists, io_command/1}). io_command({put_chars_sync, unicode,Cs,Reply}) -> - {{command,[?OP_PUTC_SYNC|unicode:characters_to_binary(Cs,utf8)]},Reply}; + {[?OP_PUTC_SYNC|unicode:characters_to_binary(Cs,utf8)], Reply}; io_command({put_chars, unicode,Cs}) -> - {command,[?OP_PUTC|unicode:characters_to_binary(Cs,utf8)]}; + {[?OP_PUTC|unicode:characters_to_binary(Cs,utf8)], ok}; io_command({move_rel,N}) -> - {command,[?OP_MOVE|put_int16(N, [])]}; + {[?OP_MOVE|put_int16(N, [])], ok}; io_command({insert_chars,unicode,Cs}) -> - {command,[?OP_INSC|unicode:characters_to_binary(Cs,utf8)]}; + {[?OP_INSC|unicode:characters_to_binary(Cs,utf8)], ok}; io_command({delete_chars,N}) -> - {command,[?OP_DELC|put_int16(N, [])]}; + {[?OP_DELC|put_int16(N, [])], ok}; io_command(beep) -> - {command,[?OP_BEEP]}; -io_command(Else) -> - throw(Else). + {[?OP_BEEP], ok}; +io_command(_) -> + unhandled. %% gr_new() %% gr_get_num(Group, Index) diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl index 4f0847084f..6b133f8d6b 100644 --- a/lib/kernel/test/code_SUITE.erl +++ b/lib/kernel/test/code_SUITE.erl @@ -41,7 +41,7 @@ big_boot_embedded/1, module_status/1, native_early_modules/1, get_mode/1, - normalized_paths/1]). + normalized_paths/1, mult_embedded_flags/1]). -export([init_per_testcase/2, end_per_testcase/2, init_per_suite/1, end_per_suite/1]). @@ -72,7 +72,8 @@ all() -> on_load_purge, on_load_self_call, on_load_pending, on_load_deleted, module_status, - big_boot_embedded, native_early_modules, get_mode, normalized_paths]. + big_boot_embedded, native_early_modules, get_mode, normalized_paths, + mult_embedded_flags]. %% These need to run in order groups() -> [{sequence, [sequence], [on_load_update, @@ -354,7 +355,7 @@ load_abs(Config) when is_list(Config) -> ensure_loaded(Config) when is_list(Config) -> {module, lists} = code:ensure_loaded(lists), case init:get_argument(mode) of - {ok, [["embedded"]]} -> + {ok, [["embedded"] | _]} -> {error, embedded} = code:ensure_loaded(code_b_test), {error, badarg} = code:ensure_loaded(34), ok; @@ -1836,6 +1837,28 @@ do_normalized_paths([M|Ms]) -> do_normalized_paths([]) -> ok. +%% Make sure that the extra -mode flags are ignored +mult_embedded_flags(_Config) -> + Modes = [{" -mode embedded", embedded}, + {" -mode interactive", interactive}, + {" -mode invalid", interactive}], + + [ begin + {ArgMode, ExpectedMode} = Mode, + {ok, Node} = start_node(mode_test, ArgMode), + ExpectedMode = rpc:call(Node, code, get_mode, []), + true = stop_node(Node) + end || Mode <- Modes], + + [ begin + {ArgIgnoredMode, _} = IgnoredMode, + {ArgRelevantMode, ExpectedMode} = RelevantMode, + {ok, Node} = start_node(mode_test, ArgRelevantMode ++ ArgIgnoredMode), + ExpectedMode = rpc:call(Node, code, get_mode, []), + true = stop_node(Node) + end || IgnoredMode <- Modes, RelevantMode <- Modes], + ok. + %% Test that module_status/1 behaves as expected module_status(_Config) -> case test_server:is_cover() of diff --git a/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl index edf30448c4..421510f9d6 100644 --- a/lib/kernel/test/gen_tcp_misc_SUITE.erl +++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl @@ -25,6 +25,7 @@ init_per_group/2,end_per_group/2, controlling_process/1, controlling_process_self/1, no_accept/1, close_with_pending_output/1, active_n/1, + active_n_closed/1, data_before_close/1, iter_max_socks/0, iter_max_socks/1, get_status/1, @@ -36,7 +37,8 @@ show_econnreset_passive/1, econnreset_after_sync_send/1, econnreset_after_async_send_active/1, econnreset_after_async_send_active_once/1, - econnreset_after_async_send_passive/1, linger_zero/1, + econnreset_after_async_send_passive/1, + linger_zero/1, linger_zero_sndbuf/1, default_options/1, http_bad_packet/1, busy_send/1, busy_disconnect_passive/1, busy_disconnect_active/1, fill_sendq/1, partial_recv_and_close/1, @@ -73,14 +75,15 @@ suite() -> all() -> [controlling_process, controlling_process_self, no_accept, close_with_pending_output, data_before_close, - iter_max_socks, passive_sockets, active_n, + iter_max_socks, passive_sockets, active_n, active_n_closed, accept_closed_by_other_process, otp_3924, closed_socket, shutdown_active, shutdown_passive, shutdown_pending, show_econnreset_active, show_econnreset_active_once, show_econnreset_passive, econnreset_after_sync_send, econnreset_after_async_send_active, econnreset_after_async_send_active_once, - econnreset_after_async_send_passive, linger_zero, + econnreset_after_async_send_passive, + linger_zero, linger_zero_sndbuf, default_options, http_bad_packet, busy_send, busy_disconnect_passive, busy_disconnect_active, fill_sendq, partial_recv_and_close, @@ -1356,7 +1359,42 @@ linger_zero(Config) when is_list(Config) -> ok = gen_tcp:close(Client), ok = ct:sleep(1), undefined = erlang:port_info(Client, connected), - {error, econnreset} = gen_tcp:recv(S, PayloadSize). + {error, econnreset} = gen_tcp:recv(S, PayloadSize), + ok. + + +linger_zero_sndbuf(Config) when is_list(Config) -> + %% All the econnreset tests will prove that {linger, {true, 0}} aborts + %% a connection when the driver queue is empty. We will test here + %% that it also works when the driver queue is not empty + %% and the linger zero option is set on the listen socket. + {OS, _} = os:type(), + {ok, Listen} = + gen_tcp:listen(0, [{active, false}, + {recbuf, 4096}, + {show_econnreset, true}, + {linger, {true, 0}}]), + {ok, Port} = inet:port(Listen), + {ok, Client} = + gen_tcp:connect(localhost, Port, + [{active, false}, + {sndbuf, 4096}]), + {ok, Server} = gen_tcp:accept(Listen), + ok = gen_tcp:close(Listen), + PayloadSize = 1024 * 1024, + Payload = binary:copy(<<"0123456789ABCDEF">>, 256 * 1024), % 1 MB + ok = gen_tcp:send(Server, Payload), + case erlang:port_info(Server, queue_size) of + {queue_size, N} when N > 0 -> ok; + {queue_size, 0} when OS =:= win32 -> ok; + {queue_size, 0} = T -> ct:fail(T) + end, + {ok, [{linger, {true, 0}}]} = inet:getopts(Server, [linger]), + ok = gen_tcp:close(Server), + ok = ct:sleep(1), + undefined = erlang:port_info(Server, connected), + {error, closed} = gen_tcp:recv(Client, PayloadSize), + ok. %% Thanks to Luke Gorrie. Tests for a very specific problem with @@ -1984,7 +2022,7 @@ recvtclass(_Config) -> recvtos_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,4,0}); recvtos_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {19,0,0}); %% Using the option returns einval, so it is not implemented. -recvtos_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {11,2,0}); +recvtos_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {12,1,0}); recvtos_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0}); %% Does not return any value - not implemented for pktoptions recvtos_ok({unix,linux}, OSVer) -> not semver_lt(OSVer, {3,1,0}); @@ -1996,7 +2034,7 @@ recvtos_ok(_, _) -> false. recvttl_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,4,0}); recvttl_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {19,0,0}); %% Using the option returns einval, so it is not implemented. -recvttl_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {11,2,0}); +recvttl_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {12,1,0}); recvttl_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0}); %% Does not return any value - not implemented for pktoptions recvttl_ok({unix,linux}, OSVer) -> not semver_lt(OSVer, {2,7,0}); @@ -2009,7 +2047,7 @@ recvtclass_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,4,0}); recvtclass_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {19,0,0}); recvtclass_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0}); %% Using the option returns einval, so it is not implemented. -recvtclass_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {11,2,0}); +recvtclass_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {12,1,0}); %% Does not return any value - not implemented for pktoptions recvtclass_ok({unix,linux}, OSVer) -> not semver_lt(OSVer, {3,1,0}); %% @@ -2582,7 +2620,51 @@ active_once_closed(Config) when is_list(Config) -> ok = inet:setopts(A,[{active,once}]), ok = receive {tcp_closed, A} -> ok after 1000 -> error end end)(). - + +%% Check that active n and tcp_close messages behave as expected. +active_n_closed(Config) when is_list(Config) -> + {ok, L} = gen_tcp:listen(0, [binary, {active, false}]), + + P = self(), + + {ok,Port} = inet:port(L), + + spawn_link(fun() -> + Payload = <<0:50000/unit:8>>, + Cnt = 10000, + P ! {size,Cnt * byte_size(Payload)}, + {ok, S} = gen_tcp:connect("localhost", Port, [binary, {active, false}]), + _ = [gen_tcp:send(S, Payload) || _ <- lists:seq(1, Cnt)], + gen_tcp:close(S) + end), + + receive {size,SendSize} -> SendSize end, + {ok, S} = gen_tcp:accept(L), + inet:setopts(S, [{active, 10}]), + RecvSize = + (fun Server(Size) -> + receive + {tcp, S, Bin} -> + Server(byte_size(Bin) + Size); + {tcp_closed, S} -> + Size; + {tcp_passive, S} -> + inet:setopts(S, [{active, 10}]), + Server(Size); + Msg -> + io:format("~p~n", [Msg]), + Server(Size) + end + end)(0), + + gen_tcp:close(L), + + if SendSize =:= RecvSize -> + ok; + true -> + ct:fail("Send and Recv size not equal: ~p ~p",[SendSize, RecvSize]) + end. + %% Test the send_timeout socket option. send_timeout(Config) when is_list(Config) -> Dir = filename:dirname(code:which(?MODULE)), diff --git a/lib/kernel/test/gen_udp_SUITE.erl b/lib/kernel/test/gen_udp_SUITE.erl index af9985de45..730886865c 100644 --- a/lib/kernel/test/gen_udp_SUITE.erl +++ b/lib/kernel/test/gen_udp_SUITE.erl @@ -37,6 +37,7 @@ buffer_size/1, binary_passive_recv/1, max_buffer_size/1, bad_address/1, read_packets/1, open_fd/1, connect/1, implicit_inet6/1, recvtos/1, recvtosttl/1, recvttl/1, recvtclass/1, + sendtos/1, sendtosttl/1, sendttl/1, sendtclass/1, local_basic/1, local_unbound/1, local_fdopen/1, local_fdopen_unbound/1, local_abstract/1]). @@ -49,6 +50,7 @@ all() -> bad_address, read_packets, open_fd, connect, implicit_inet6, active_n, recvtos, recvtosttl, recvttl, recvtclass, + sendtos, sendtosttl, sendttl, sendtclass, {group, local}]. groups() -> @@ -312,7 +314,6 @@ read_packets(Config) when is_list(Config) -> {ok,R} = gen_udp:open(0, [{read_packets,N1}]), {ok,RP} = inet:port(R), {ok,Node} = start_node(gen_udp_SUITE_read_packets), - Die = make_ref(), %% {V1, Trace1} = read_packets_test(R, RP, Msgs, Node), {ok,[{read_packets,N1}]} = inet:getopts(R, [read_packets]), @@ -324,7 +325,7 @@ read_packets(Config) when is_list(Config) -> stop_node(Node), ct:log("N1=~p, V1=~p vs N2=~p, V2=~p",[N1,V1,N2,V2]), - dump_terms(Config, "trace1.terms", Trace2), + dump_terms(Config, "trace1.terms", Trace1), dump_terms(Config, "trace2.terms", Trace2), %% Because of the inherit racy-ness of the feature it is @@ -348,15 +349,6 @@ dump_terms(Config, Name, Terms) -> file:write_file(FName, term_to_binary(Terms)), ct:log("Logged terms to ~s",[FName]). -infinite_loop(Die) -> - receive - Die -> - ok - after - 0 -> - infinite_loop(Die) - end. - read_packets_test(R, RP, Msgs, Node) -> Receiver = self(), Tracer = @@ -577,19 +569,19 @@ active_n(Config) when is_list(Config) -> recvtos(_Config) -> test_recv_opts( - inet, [{recvtos,tos,96}], + inet, [{recvtos,tos,96}], false, fun recvtos_ok/2). recvtosttl(_Config) -> test_recv_opts( - inet, [{recvtos,tos,96},{recvttl,ttl,33}], + inet, [{recvtos,tos,96},{recvttl,ttl,33}], false, fun (OSType, OSVer) -> recvtos_ok(OSType, OSVer) andalso recvttl_ok(OSType, OSVer) end). recvttl(_Config) -> test_recv_opts( - inet, [{recvttl,ttl,33}], + inet, [{recvttl,ttl,33}], false, fun recvttl_ok/2). recvtclass(_Config) -> @@ -601,15 +593,48 @@ recvtclass(_Config) -> of [_] -> test_recv_opts( - inet6, [{recvtclass,tclass,224}], + inet6, [{recvtclass,tclass,224}], false, fun recvtclass_ok/2); [] -> {skip,ipv6_not_supported,IFs} end. + +sendtos(_Config) -> + test_recv_opts( + inet, [{recvtos,tos,96}], true, + fun sendtos_ok/2). + +sendtosttl(_Config) -> + test_recv_opts( + inet, [{recvtos,tos,96},{recvttl,ttl,33}], true, + fun (OSType, OSVer) -> + sendtos_ok(OSType, OSVer) andalso sendttl_ok(OSType, OSVer) + end). + +sendttl(_Config) -> + test_recv_opts( + inet, [{recvttl,ttl,33}], true, + fun sendttl_ok/2). + +sendtclass(_Config) -> + {ok,IFs} = inet:getifaddrs(), + case + [Name || + {Name,Opts} <- IFs, + lists:member({addr,{0,0,0,0,0,0,0,1}}, Opts)] + of + [_] -> + test_recv_opts( + inet6, [{recvtclass,tclass,224}], true, + fun sendtclass_ok/2); + [] -> + {skip,ipv6_not_supported,IFs} + end. + %% These version numbers are just above the highest noted in daily tests %% where the test fails for a plausible reason, that is the lowest -%% where we can expect that the test mighe succeed, so +%% where we can expect that the test might succeed, so %% skip on platforms lower than this. %% %% On newer versions it might be fixed, but we'll see about that @@ -628,16 +653,55 @@ recvtos_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0}); recvtos_ok({unix,_}, _) -> true; recvtos_ok(_, _) -> false. +%% Option has no effect +recvttl_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0}); +%% recvttl_ok({unix,_}, _) -> true; recvttl_ok(_, _) -> false. %% Using the option returns einval, so it is not implemented. recvtclass_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {9,9,0}); recvtclass_ok({unix,linux}, OSVer) -> not semver_lt(OSVer, {2,6,11}); +%% Option has no effect +recvtclass_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0}); %% recvtclass_ok({unix,_}, _) -> true; recvtclass_ok(_, _) -> false. + +%% To send ancillary data seems to require much higher version numbers +%% than receiving it... +%% + +%% Using the option returns einval, so it is not implemented. +sendtos_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {19,0,0}); +sendtos_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,5,0}); +sendtos_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0}); +sendtos_ok({unix,linux}, OSVer) -> not semver_lt(OSVer, {4,0,0}); +sendtos_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {12,1,0}); +%% +sendtos_ok({unix,_}, _) -> true; +sendtos_ok(_, _) -> false. + +%% Using the option returns einval, so it is not implemented. +sendttl_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {19,0,0}); +sendttl_ok({unix,linux}, OSVer) -> not semver_lt(OSVer, {4,0,0}); +%% Using the option returns enoprotoopt, so it is not implemented. +sendttl_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {12,1,0}); +%% Option has no effect +sendttl_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,5,0}); +%% +sendttl_ok({unix,_}, _) -> true; +sendttl_ok(_, _) -> false. + +%% Using the option returns einval, so it is not implemented. +sendtclass_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {9,9,0}); +sendtclass_ok({unix,linux}, OSVer) -> not semver_lt(OSVer, {2,6,11}); +%% +sendtclass_ok({unix,_}, _) -> true; +sendtclass_ok(_, _) -> false. + + semver_lt({X1,Y1,Z1}, {X2,Y2,Z2}) -> if X1 > X2 -> false; @@ -650,18 +714,18 @@ semver_lt({X1,Y1,Z1}, {X2,Y2,Z2}) -> end; semver_lt(_, {_,_,_}) -> false. -test_recv_opts(Family, Spec, OSFilter) -> +test_recv_opts(Family, Spec, TestSend, OSFilter) -> OSType = os:type(), OSVer = os:version(), case OSFilter(OSType, OSVer) of true -> io:format("Os: ~p, ~p~n", [OSType,OSVer]), - test_recv_opts(Family, Spec, OSType, OSVer); + test_recv_opts(Family, Spec, TestSend, OSType, OSVer); false -> {skip,{not_supported_for_os_version,{OSType,OSVer}}} end. %% -test_recv_opts(Family, Spec, _OSType, _OSVer) -> +test_recv_opts(Family, Spec, TestSend, _OSType, _OSVer) -> Timeout = 5000, RecvOpts = [RecvOpt || {RecvOpt,_,_} <- Spec], TrueRecvOpts = [{RecvOpt,true} || {RecvOpt,_,_} <- Spec], @@ -686,16 +750,33 @@ test_recv_opts(Family, Spec, _OSType, _OSVer) -> ok = inet:setopts(S1, TrueRecvOpts_OptsVals), {ok,TrueRecvOpts_OptsVals} = inet:getopts(S1, RecvOpts ++ Opts), %% + %% S1 now has true receive options and set option values + %% {ok,S2} = gen_udp:open(0, [Family,binary,{active,true}|FalseRecvOpts]), {ok,P2} = inet:port(S2), {ok,FalseRecvOpts_OptsVals2} = inet:getopts(S2, RecvOpts ++ Opts), OptsVals2 = FalseRecvOpts_OptsVals2 -- FalseRecvOpts, %% - ok = gen_udp:send(S2, Addr, P1, <<"abcde">>), + %% S2 now has false receive options and default option values, + %% OptsVals2 contains the default option values + %% + ok = gen_udp:send(S2, {Addr,P1}, <<"abcde">>), ok = gen_udp:send(S1, Addr, P2, <<"fghij">>), + TestSend andalso + begin + ok = gen_udp:send(S2, Addr, P1, OptsVals, <<"ABCDE">>), + ok = gen_udp:send(S2, {Addr,P1}, OptsVals, <<"12345">>) + end, {ok,{_,P2,OptsVals3,<<"abcde">>}} = gen_udp:recv(S1, 0, Timeout), verify_sets_eq(OptsVals3, OptsVals2), + TestSend andalso + begin + {ok,{_,P2,OptsVals0,<<"ABCDE">>}} = gen_udp:recv(S1, 0, Timeout), + {ok,{_,P2,OptsVals1,<<"12345">>}} = gen_udp:recv(S1, 0, Timeout), + verify_sets_eq(OptsVals0, OptsVals), + verify_sets_eq(OptsVals1, OptsVals) + end, receive {udp,S2,_,P1,<<"fghij">>} -> ok; @@ -710,8 +791,16 @@ test_recv_opts(Family, Spec, _OSType, _OSVer) -> ok = inet:setopts(S2, TrueRecvOpts), {ok,TrueRecvOpts} = inet:getopts(S2, RecvOpts), %% - ok = gen_udp:send(S2, Addr, P1, <<"klmno">>), - ok = gen_udp:send(S1, Addr, P2, <<"pqrst">>), + %% S1 now has false receive options and set option values + %% + %% S2 now has true receive options and default option values + %% + ok = gen_udp:send(S2, {Addr,P1}, [], <<"klmno">>), + ok = gen_udp:send(S1, {Family,{loopback,P2}}, <<"pqrst">>), + TestSend andalso + begin + ok = gen_udp:send(S1, {Family,{loopback,P2}}, OptsVals2, <<"PQRST">>) + end, {ok,{_,P2,<<"klmno">>}} = gen_udp:recv(S1, 0, Timeout), receive {udp,S2,_,P1,OptsVals4,<<"pqrst">>} -> @@ -721,9 +810,18 @@ test_recv_opts(Family, Spec, _OSType, _OSVer) -> after Timeout -> exit(timeout) end, + TestSend andalso + receive + {udp,S2,_,P1,OptsVals5,<<"PQRST">>} -> + verify_sets_eq(OptsVals5, OptsVals2); + Other3 -> + exit({unexpected,Other3}) + after Timeout -> + exit(timeout) + end, ok = gen_udp:close(S1), ok = gen_udp:close(S2), -%% exit({{OSType,OSVer},success}), % In search for the truth +%%% exit({{_OSType,_OSVer},success}), % In search for the truth ok. verify_sets_eq(L1, L2) -> @@ -877,6 +975,10 @@ connect(Config) when is_list(Config) -> implicit_inet6(Config) when is_list(Config) -> Host = ok(inet:gethostname()), case inet:getaddr(Host, inet6) of + {ok,{16#fe80,0,0,0,_,_,_,_} = Addr} -> + {skip, + "Got link local IPv6 address: " + ++inet:ntoa(Addr)}; {ok,Addr} -> implicit_inet6(Host, Addr); {error,Reason} -> @@ -927,11 +1029,12 @@ ok({ok,V}) -> V; ok(NotOk) -> try throw(not_ok) catch - throw:Thrown:Stacktrace -> - erlang:raise( - error, {Thrown, NotOk}, tl(Stacktrace)) + throw:not_ok:Stacktrace -> + raise_error({not_ok, NotOk}, tl(Stacktrace)) end. +raise_error(Reason, Stacktrace) -> + erlang:raise(error, Reason, Stacktrace). local_filename(Tag) -> "/tmp/" ?MODULE_STRING "_" ++ os:getpid() ++ "_" ++ atom_to_list(Tag). diff --git a/lib/kernel/test/logger_std_h_SUITE.erl b/lib/kernel/test/logger_std_h_SUITE.erl index 16ab0e97fc..2b2d509860 100644 --- a/lib/kernel/test/logger_std_h_SUITE.erl +++ b/lib/kernel/test/logger_std_h_SUITE.erl @@ -132,6 +132,7 @@ all() -> bad_input, reconfig, file_opts, + relative_file_path, sync, write_failure, sync_failure, @@ -693,6 +694,54 @@ file_opts(Config) -> file_opts(cleanup, _Config) -> logger:remove_handler(?MODULE). +relative_file_path(_Config) -> + {ok,Dir} = file:get_cwd(), + AbsName1 = filename:join(Dir,?MODULE), + ok = logger:add_handler(?MODULE, + logger_std_h, + #{config => #{type=>file}, + filter_default=>log, + filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), + formatter=>{?MODULE,self()}}), + #{cb_state := #{handler_state := #{file:=AbsName1}}} = + logger_olp:info(h_proc_name()), + {ok,#{config := #{file:=AbsName1}}} = + logger:get_handler_config(?MODULE), + ok = logger:remove_handler(?MODULE), + + RelName2 = filename:join(atom_to_list(?FUNCTION_NAME), + lists:concat([?FUNCTION_NAME,".log"])), + AbsName2 = filename:join(Dir,RelName2), + ok = logger:add_handler(?MODULE, + logger_std_h, + #{config => #{file => RelName2}, + filter_default=>log, + filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), + formatter=>{?MODULE,self()}}), + #{cb_state := #{handler_state := #{file:=AbsName2}}} = + logger_olp:info(h_proc_name()), + {ok,#{config := #{file:=AbsName2}}} = + logger:get_handler_config(?MODULE), + logger:notice(M1=?msg,?domain), + ?check(M1), + B1 = ?bin(M1), + try_read_file(AbsName2, {ok,B1}, filesync_rep_int()), + + ok = file:set_cwd(".."), + logger:notice(M2=?msg,?domain), + ?check(M2), + B20 = ?bin(M2), + B2 = <<B1/binary,B20/binary>>, + try_read_file(AbsName2, {ok,B2}, filesync_rep_int()), + + {error,_} = logger:update_handler_config(?MODULE,config,#{file=>RelName2}), + ok = logger:update_handler_config(?MODULE,config,#{file=>AbsName2}), + ok = file:set_cwd(Dir), + ok = logger:update_handler_config(?MODULE,config,#{file=>RelName2}), + ok. +relative_file_path(cleanup,_Config) -> + logger:remove_handler(?MODULE). + sync(Config) -> Dir = ?config(priv_dir,Config), diff --git a/lib/kernel/test/seq_trace_SUITE.erl b/lib/kernel/test/seq_trace_SUITE.erl index 663f910751..83a94ab087 100644 --- a/lib/kernel/test/seq_trace_SUITE.erl +++ b/lib/kernel/test/seq_trace_SUITE.erl @@ -26,6 +26,7 @@ init_per_group/2,end_per_group/2, init_per_testcase/2,end_per_testcase/2]). -export([token_set_get/1, tracer_set_get/1, print/1, + old_heap_token/1, send/1, distributed_send/1, recv/1, distributed_recv/1, trace_exit/1, distributed_exit/1, call/1, port/1, match_set_seq_token/1, gc_seq_token/1, label_capability_mismatch/1, @@ -50,6 +51,7 @@ suite() -> all() -> [token_set_get, tracer_set_get, print, send, send_literal, distributed_send, recv, distributed_recv, trace_exit, + old_heap_token, distributed_exit, call, port, match_set_seq_token, gc_seq_token, label_capability_mismatch]. @@ -149,17 +151,19 @@ tracer_set_get(Config) when is_list(Config) -> ok. print(Config) when is_list(Config) -> - lists:foreach(fun do_print/1, ?TIMESTAMP_MODES). + [do_print(TsType, Label) || TsType <- ?TIMESTAMP_MODES, + Label <- [17, "label"]]. -do_print(TsType) -> +do_print(TsType, Label) -> start_tracer(), + seq_trace:set_token(label, Label), set_token_flags([print, TsType]), - seq_trace:print(0,print1), + seq_trace:print(Label,print1), seq_trace:print(1,print2), seq_trace:print(print3), seq_trace:reset_trace(), - [{0,{print,_,_,[],print1}, Ts0}, - {0,{print,_,_,[],print3}, Ts1}] = stop_tracer(2), + [{Label,{print,_,_,[],print1}, Ts0}, + {Label,{print,_,_,[],print3}, Ts1}] = stop_tracer(2), check_ts(TsType, Ts0), check_ts(TsType, Ts1). @@ -563,6 +567,24 @@ get_port_message(Port) -> end. +%% OTP-15849 ERL-700 +%% Verify changing label on existing token when it resides on old heap. +%% Bug caused faulty ref from old to new heap. +old_heap_token(Config) when is_list(Config) -> + seq_trace:set_token(label, 1), + erlang:garbage_collect(self(), [{type, minor}]), + erlang:garbage_collect(self(), [{type, minor}]), + %% Now token tuple should be on old-heap. + %% Set a new non-literal label which should reside on new-heap. + NewLabel = {self(), "new label"}, + 1 = seq_trace:set_token(label, NewLabel), + + %% If bug, we now have a ref from old to new heap. Yet another minor gc + %% will make that a ref to deallocated memory. + erlang:garbage_collect(self(), [{type, minor}]), + {label,NewLabel} = seq_trace:get_token(label), + ok. + match_set_seq_token(doc) -> ["Tests that match spec function set_seq_token does not " diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk index 765e890157..e5188aa9b5 100644 --- a/lib/kernel/vsn.mk +++ b/lib/kernel/vsn.mk @@ -1 +1 @@ -KERNEL_VSN = 6.4 +KERNEL_VSN = 6.4.1 diff --git a/lib/megaco/Makefile b/lib/megaco/Makefile index 1d0bb6778c..f385df6a5c 100644 --- a/lib/megaco/Makefile +++ b/lib/megaco/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1999-2016. All Rights Reserved. +# Copyright Ericsson AB 1999-2019. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -206,11 +206,18 @@ $(APP_TAR_FILE): $(APP_DIR) dialyzer_plt: $(DIA_PLT) -$(DIA_PLT): +$(DIA_PLT): Makefile @echo "Building $(APPLICATION) plt file" @dialyzer --build_plt \ --output_plt $@ \ -r ../$(APPLICATION)/ebin \ + ../../lib/kernel/ebin \ + ../../lib/stdlib/ebin \ + ../../lib/runtime_tools/ebin \ + ../../lib/asn1/ebin \ + ../../lib/debugger/ebin \ + ../../lib/et/ebin \ + ../../erts/preloaded/ebin \ --output $(DIA_ANALYSIS) \ --verbose diff --git a/lib/megaco/doc/src/megaco_edist_compress.xml b/lib/megaco/doc/src/megaco_edist_compress.xml index 16443e469c..8461c59a00 100644 --- a/lib/megaco/doc/src/megaco_edist_compress.xml +++ b/lib/megaco/doc/src/megaco_edist_compress.xml @@ -43,8 +43,8 @@ <name since="">Module:encode(R, Version) -> T</name> <fsummary>Encode (compress) a megaco component.</fsummary> <type> - <v>R = megaco_message() | transaction() | action_reply() | action_request() | command_request()</v> - <v>Version = integer()</v> + <v>R = megaco_encoder:megaco_message() | megaco_encoder:transaction() | megaco_encoder:action_reply() | megaco_encoder:action_request() | megaco_encoder:command_request()</v> + <v>Version = megaco_encoder:protocol_version()</v> <v>T = term()</v> </type> <desc> @@ -57,8 +57,8 @@ <fsummary>Decode (decompress) a megaco component.</fsummary> <type> <v>T = term()</v> - <v>Version = integer()</v> - <v>R = megaco_message() | transaction() | action_reply() | action_request() | command_request()</v> + <v>Version = megaco_encoder:protocol_version()</v> + <v>R = megaco_encoder:megaco_message() | megaco_encoder:transaction() | megaco_encoder:action_reply() | megaco_encoder:action_request() | megaco_encoder:command_request()</v> </type> <desc> <p>Decompress a megaco component. </p> diff --git a/lib/megaco/doc/src/megaco_encoder.xml b/lib/megaco/doc/src/megaco_encoder.xml index cc8270440b..0632a55d48 100644 --- a/lib/megaco/doc/src/megaco_encoder.xml +++ b/lib/megaco/doc/src/megaco_encoder.xml @@ -42,7 +42,16 @@ <section> <title>DATA TYPES</title> + <note> + <p>Note that the actual definition of (some of) these records depend on + the megaco protocol version used. For instance, the + <c>'TransactionReply'</c> record + has two more fields in version 3, so a simple erlang type definition + cannot be made here. </p> + </note> <code type="none"><![CDATA[ +protocol_version() = integer() +segment_no() = integer() megaco_message() = #'MegacoMessage{}' transaction() = {transactionRequest, transaction_request()} | {transactionPending, transaction_reply()} | @@ -57,6 +66,8 @@ transaction_ack() = #'TransactionAck'{} segment_reply() = #'SegmentReply'{} action_request() = #'ActionRequest'{} action_reply() = #'ActionReply'{} +command_request() = #'CommandRequest'{} +error_desc() = #'ErrorDescriptor'{} ]]></code> <marker id="encode_message"></marker> diff --git a/lib/megaco/doc/src/megaco_user.xml b/lib/megaco/doc/src/megaco_user.xml index 198f2aa24c..56d4d51cde 100644 --- a/lib/megaco/doc/src/megaco_user.xml +++ b/lib/megaco/doc/src/megaco_user.xml @@ -165,7 +165,7 @@ protocol_version() = integer() ]]></code> <funcs> <func> <name since="">handle_connect(ConnHandle, ProtocolVersion) -> ok | error | {error,ErrorDescr}</name> - <name since="">handle_connect(ConnHandle, ProtocolVersion, Extra]) -> ok | error | {error,ErrorDescr}</name> + <name since="">handle_connect(ConnHandle, ProtocolVersion, Extra) -> ok | error | {error,ErrorDescr}</name> <fsummary>Invoked when a new connection is established</fsummary> <type> <v>ConnHandle = conn_handle()</v> diff --git a/lib/megaco/examples/simple/megaco_simple_mgc.erl b/lib/megaco/examples/simple/megaco_simple_mgc.erl index f324e17a3a..8a78262b86 100644 --- a/lib/megaco/examples/simple/megaco_simple_mgc.erl +++ b/lib/megaco/examples/simple/megaco_simple_mgc.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2016. All Rights Reserved. +%% Copyright Ericsson AB 2001-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -193,15 +193,17 @@ stop(Mid) -> d("stop -> entry with~n Mid: ~p", [Mid]), Disco = fun(CH) -> d("stop -> CH: ~p", [CH]), - Reason = stopped_by_user, - Pid = megaco:conn_info(CH, control_pid), - SendMod = megaco:conn_info(CH, send_mod), + Reason = stopped_by_user, + Pid = megaco:conn_info(CH, control_pid), + SendMod = megaco:conn_info(CH, send_mod), SendHandle = megaco:conn_info(CH, send_handle), d("stop -> disconnect", []), megaco:disconnect(CH, Reason), + d("stop -> cancel", []), - megaco:cancel(CH, Reason), + megaco:cancel(CH, Reason), % see handle_disconnect + d("stop -> close transport" "~n SendMod: ~p" "~n SendHandle: ~p", [SendMod, SendHandle]), @@ -247,6 +249,7 @@ handle_disconnect(ConnHandle, ProtocolVersion, Reason) -> "~n ProtocolVersion: ~p" "~n Reason: ~p" "", [ConnHandle, ProtocolVersion, Reason]), + info_msg("handle_disconnect - cancel outstanding messages~n"), megaco:cancel(ConnHandle, Reason), % Cancel the outstanding messages ok. @@ -443,6 +446,12 @@ get_arg(Key, Args) -> %% DEBUGGING %%---------------------------------------------------------------------- +info_msg(F) -> + info_msg(F, []). +info_msg(F, A) -> + io:format("~p MGC: " ++ F ++ "~n", [self()|A]). + + d(F) -> d(F, []). diff --git a/lib/megaco/src/app/megaco.app.src b/lib/megaco/src/app/megaco.app.src index c54c80351c..5fb7273b4a 100644 --- a/lib/megaco/src/app/megaco.app.src +++ b/lib/megaco/src/app/megaco.app.src @@ -107,6 +107,7 @@ megaco_udp, megaco_udp_server, megaco_udp_sup, + megaco_user, megaco_user_default ]}, {registered, [megaco_config, megaco_monitor, diff --git a/lib/megaco/src/app/megaco.erl b/lib/megaco/src/app/megaco.erl index f0c209fd6c..9ed042401b 100644 --- a/lib/megaco/src/app/megaco.erl +++ b/lib/megaco/src/app/megaco.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2016. All Rights Reserved. +%% Copyright Ericsson AB 1999-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -94,6 +94,11 @@ %% This is for XREF -deprecated([{format_versions, 1, eventually}]). +-export_type([ + void/0 + ]). + +-type void() :: term(). -include("megaco_internal.hrl"). @@ -686,13 +691,8 @@ sys_info() -> [{arch, SysArch}, {ver, SysVer}]. os_info() -> - V = os:version(), - case os:type() of - {OsFam, OsName} -> - [{fam, OsFam}, {name, OsName}, {ver, V}]; - OsFam -> - [{fam, OsFam}, {ver, V}] - end. + {OsFam, OsName} = os:type(), + [{fam, OsFam}, {name, OsName}, {ver, os:version()}]. ms() -> ms1(). diff --git a/lib/megaco/src/binary/megaco_binary_encoder_lib.erl b/lib/megaco/src/binary/megaco_binary_encoder_lib.erl index 5e9836dc48..fdbb42c2f8 100644 --- a/lib/megaco/src/binary/megaco_binary_encoder_lib.erl +++ b/lib/megaco/src/binary/megaco_binary_encoder_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2016. All Rights Reserved. +%% Copyright Ericsson AB 2005-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -141,6 +141,7 @@ encode_transaction(_EC, T, _AsnMod, _TransMod, _Type) -> TransMod :: atom(), Type :: atom()) -> {'ok', binary()} | {'error', any()}. +-dialyzer({nowarn_function, do_encode_transaction/5}). % Future compat do_encode_transaction([native], _Trans, _AsnMod, _TransMod, binary) -> %% asn1rt:encode(AsnMod, element(1, T), T); {error, not_implemented}; @@ -173,6 +174,7 @@ do_encode_transaction(EC, _Trans, _AsnMod, _TransMod, _Type) -> TransMod :: atom(), Type :: atom()) -> {'ok', binary()} | {'error', any()}. +-dialyzer({nowarn_function, encode_action_requests/5}). % Future compat encode_action_requests([native], _ARs, _AsnMod, _TransMod, binary) -> %% asn1rt:encode(AsnMod, element(1, T), T); {error, not_implemented}; @@ -203,6 +205,7 @@ encode_action_requests(EC, _ARs, _AsnMod, _TransMod, _Type) -> TransMod :: atom(), Type :: atom()) -> {'ok', binary()} | {'error', any()}. +-dialyzer({nowarn_function, encode_action_request/5}). % Future compat encode_action_request([native], _AR, _AsnMod, _TransMod, binary) -> %% asn1rt:encode(AsnMod, element(1, T), T); {error, not_implemented}; @@ -226,6 +229,8 @@ encode_action_request(EC, _AR, _AsnMod, _TransMod, _Type) -> %% Convert a ActionReply record into a binary %% Return {ok, DeepIoList} | {error, Reason} %%---------------------------------------------------------------------- + +-dialyzer({nowarn_function, encode_action_reply/5}). % Future compat encode_action_reply([native], _ARs, _AsnMod, _TransMod, binary) -> %% asn1rt:encode(AsnMod, element(1, T), T); {error, not_implemented}; diff --git a/lib/megaco/src/binary/megaco_binary_name_resolver_prev3a.erl b/lib/megaco/src/binary/megaco_binary_name_resolver_prev3a.erl index af97056d5d..f9e3fe39e3 100644 --- a/lib/megaco/src/binary/megaco_binary_name_resolver_prev3a.erl +++ b/lib/megaco/src/binary/megaco_binary_name_resolver_prev3a.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2016. All Rights Reserved. +%% Copyright Ericsson AB 2004-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -1715,7 +1715,7 @@ decode_nt({event_parameter, Item}, SubItem) -> [16#00, 16#01] -> "cs" end; [16#00, 16#06] -> % Event qualert - case Item of + case SubItem of [16#00, 16#01] -> "th" end end; diff --git a/lib/megaco/src/binary/megaco_binary_name_resolver_prev3b.erl b/lib/megaco/src/binary/megaco_binary_name_resolver_prev3b.erl index b543abe7c8..141225f501 100644 --- a/lib/megaco/src/binary/megaco_binary_name_resolver_prev3b.erl +++ b/lib/megaco/src/binary/megaco_binary_name_resolver_prev3b.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2016. All Rights Reserved. +%% Copyright Ericsson AB 2005-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -1715,7 +1715,7 @@ decode_nt({event_parameter, Item}, SubItem) -> [16#00, 16#01] -> "cs" end; [16#00, 16#06] -> % Event qualert - case Item of + case SubItem of [16#00, 16#01] -> "th" end end; diff --git a/lib/megaco/src/binary/megaco_binary_name_resolver_prev3c.erl b/lib/megaco/src/binary/megaco_binary_name_resolver_prev3c.erl index 827cb3920b..b27a0be26e 100644 --- a/lib/megaco/src/binary/megaco_binary_name_resolver_prev3c.erl +++ b/lib/megaco/src/binary/megaco_binary_name_resolver_prev3c.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2016. All Rights Reserved. +%% Copyright Ericsson AB 2005-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -1716,7 +1716,7 @@ decode_nt({event_parameter, Item}, SubItem) -> [16#00, 16#01] -> "cs" end; [16#00, 16#06] -> % Event qualert - case Item of + case SubItem of [16#00, 16#01] -> "th" end end; diff --git a/lib/megaco/src/binary/megaco_binary_name_resolver_v1.erl b/lib/megaco/src/binary/megaco_binary_name_resolver_v1.erl index 1fba60fed6..aba64bb9f2 100644 --- a/lib/megaco/src/binary/megaco_binary_name_resolver_v1.erl +++ b/lib/megaco/src/binary/megaco_binary_name_resolver_v1.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2016. All Rights Reserved. +%% Copyright Ericsson AB 2000-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -1325,7 +1325,7 @@ decode_nt({event_parameter, Item}, SubItem) -> [16#00, 16#01] -> "cs" end; [16#00, 16#06] -> % Event qualert - case Item of + case SubItem of [16#00, 16#01] -> "th" end end; diff --git a/lib/megaco/src/binary/megaco_binary_name_resolver_v2.erl b/lib/megaco/src/binary/megaco_binary_name_resolver_v2.erl index 45b9b32772..dd07f8b404 100644 --- a/lib/megaco/src/binary/megaco_binary_name_resolver_v2.erl +++ b/lib/megaco/src/binary/megaco_binary_name_resolver_v2.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2016. All Rights Reserved. +%% Copyright Ericsson AB 2003-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -1393,7 +1393,7 @@ decode_nt({event_parameter, Item}, SubItem) -> [16#00, 16#01] -> "cs" end; [16#00, 16#06] -> % Event qualert - case Item of + case SubItem of [16#00, 16#01] -> "th" end end; diff --git a/lib/megaco/src/binary/megaco_binary_name_resolver_v3.erl b/lib/megaco/src/binary/megaco_binary_name_resolver_v3.erl index f1482bc252..a8c4211235 100644 --- a/lib/megaco/src/binary/megaco_binary_name_resolver_v3.erl +++ b/lib/megaco/src/binary/megaco_binary_name_resolver_v3.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2016. All Rights Reserved. +%% Copyright Ericsson AB 2005-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -1728,7 +1728,7 @@ decode_nt({event_parameter, Item}, SubItem) -> [16#00, 16#01] -> "cs" end; [16#00, 16#06] -> % Event qualert - case Item of + case SubItem of [16#00, 16#01] -> "th" end end; diff --git a/lib/megaco/src/engine/depend.mk b/lib/megaco/src/engine/depend.mk index 96ee337e3a..ba919659db 100644 --- a/lib/megaco/src/engine/depend.mk +++ b/lib/megaco/src/engine/depend.mk @@ -2,7 +2,7 @@ # %CopyrightBegin% # -# Copyright Ericsson AB 2003-2016. All Rights Reserved. +# Copyright Ericsson AB 2003-2019. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -78,6 +78,10 @@ $(EBIN)/megaco_transport.$(EMULATOR): megaco_transport.erl $(EBIN)/megaco_user.$(EMULATOR): megaco_user.erl +$(EBIN)/megaco_user.$(EMULATOR): megaco_user.erl \ + ../../include/megaco.hrl \ + ../../include/megaco_message_v1.hrl + $(EBIN)/megaco_user_default.$(EMULATOR): megaco_user_default.erl \ ../../include/megaco.hrl \ ../../include/megaco_message_v1.hrl diff --git a/lib/megaco/src/engine/megaco_edist_compress.erl b/lib/megaco/src/engine/megaco_edist_compress.erl index 987a5ec717..968ab6f16e 100644 --- a/lib/megaco/src/engine/megaco_edist_compress.erl +++ b/lib/megaco/src/engine/megaco_edist_compress.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2016. All Rights Reserved. +%% Copyright Ericsson AB 2007-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -25,10 +25,21 @@ -module(megaco_edist_compress). --export([behaviour_info/1]). +-callback encode(R, Version) -> T when + R :: megaco_encoder:megaco_message() | + megaco_encoder:transaction() | + megaco_encoder:action_reply() | + megaco_encoder:action_request() | + megaco_encoder:command_request(), + Version :: megaco_encoder:protocol_version(), + T :: term(). + +-callback decode(T, Version) -> R when + T :: term(), + Version :: megaco_encoder:protocol_version() | dynamic, + R :: megaco_encoder:megaco_message() | + megaco_encoder:transaction() | + megaco_encoder:action_reply() | + megaco_encoder:action_request() | + megaco_encoder:command_request(). -behaviour_info(callbacks) -> - [{encode,2}, - {decode,2}]; -behaviour_info(_) -> - undefined. diff --git a/lib/megaco/src/engine/megaco_encoder.erl b/lib/megaco/src/engine/megaco_encoder.erl index 7ade349083..dd5a3458fc 100644 --- a/lib/megaco/src/engine/megaco_encoder.erl +++ b/lib/megaco/src/engine/megaco_encoder.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2016. All Rights Reserved. +%% Copyright Ericsson AB 2003-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -25,14 +25,108 @@ -module(megaco_encoder). --export([behaviour_info/1]). - -behaviour_info(callbacks) -> - [{encode_message, 3}, - {decode_message, 3}, - {decode_mini_message, 3}, - {encode_transaction, 3}, - {encode_action_requests, 3}, - {encode_action_reply, 3}]; -behaviour_info(_) -> - undefined. +-export_type([ + protocol_version/0, + segment_no/0, + megaco_message/0, + transaction/0, + transaction_request/0, + transaction_pending/0, + transaction_reply/0, + transaction_response_ack/0, + transaction_ack/0, + segment_reply/0, + action_request/0, + action_reply/0, + command_request/0, + error_desc/0 + ]). + + +-include("megaco_message_internal.hrl"). + +-type protocol_version() :: integer(). +-type segment_no() :: integer(). +-type megaco_message() :: #'MegacoMessage'{}. +-type transaction() :: {transactionRequest, transaction_request()} | + {transactionPending, transaction_reply()} | + {transactionReply, transaction_pending()} | + {transactionResponseAck, transaction_response_ack()} | + {segmentReply, segment_reply()}. +-type transaction_request() :: #'TransactionRequest'{}. +-type transaction_pending() :: #'TransactionPending'{}. +%% The problem with TransactionReply is that its definition depend +%% on which version of the protocol we are using. As of version 3, +%% it has two more fields. +%% -type transaction_reply() :: #'TransactionReply'{}. +-type transaction_reply() :: {'TransactionReply', _, _} | + {'TransactionReply', _, _, _, _}. +-type transaction_response_ack() :: [transaction_ack()]. +-type transaction_ack() :: #'TransactionAck'{}. +-type segment_reply() :: #'SegmentReply'{}. +%% -type action_request() :: #'ActionRequest'{}. +-type action_request() :: {'ActionRequest', _, _, _, _}. +%% -type action_reply() :: #'ActionReply'{}. +-type action_reply() :: {'ActionReply', _, _, _}. +%% -type command_request() :: #'CommandRequest'{}. +-type command_request() :: {'CommandRequest', _, _, _}. +-type error_desc() :: #'ErrorDescriptor'{}. + +-callback encode_message(EncodingConfig, + Version, + Message) -> {ok, Bin} | Error when + EncodingConfig :: list(), + Version :: protocol_version(), + Message :: megaco_message(), + Bin :: binary(), + Error :: term(). + +-callback decode_message(EncodingConfig, + Version, + Bin) -> {ok, Message} | Error when + EncodingConfig :: list(), + Version :: protocol_version() | dynamic, + Bin :: binary(), + Message :: megaco_message(), + Error :: term(). + +-callback decode_mini_message(EncodingConfig, + Version, + Bin) -> {ok, Message} | Error when + EncodingConfig :: list(), + Version :: protocol_version() | dynamic, + Bin :: binary(), + Message :: megaco_message(), + Error :: term(). + +-callback encode_transaction(EncodingConfig, + Version, + Transaction) -> {ok, Bin} | {error, Reason} when + EncodingConfig :: list(), + Version :: protocol_version(), + Transaction :: transaction(), + Bin :: binary(), + Reason :: not_implemented | term(). + +-callback encode_action_requests(EncodingConfig, + Version, + ARs) -> {ok, Bin} | {error, Reason} when + EncodingConfig :: list(), + Version :: protocol_version(), + ARs :: [action_request()], + Bin :: binary(), + Reason :: not_implemented | term(). + +-callback encode_action_reply(EncodingConfig, + Version, + AR) -> {ok, Bin} | {error, Reason} when + EncodingConfig :: list(), + Version :: protocol_version(), + AR :: action_reply(), + Bin :: binary(), + Reason :: not_implemented | term(). + +-optional_callbacks( + [ + encode_action_reply/3 % Only used if segementation is used + ]). diff --git a/lib/megaco/src/engine/megaco_messenger.erl b/lib/megaco/src/engine/megaco_messenger.erl index 1d462b2140..2a9ecee2a7 100644 --- a/lib/megaco/src/engine/megaco_messenger.erl +++ b/lib/megaco/src/engine/megaco_messenger.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2016. All Rights Reserved. +%% Copyright Ericsson AB 1999-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -1391,7 +1391,7 @@ prepare_request(ConnData, T, Rest, AckList, ReqList, Extra) -> %% don't restart the reply_timer. ConnData2 = ConnData#conn_data{protocol_version = Version}, ?report_trace(ConnData2, - "re-send trans reply", [T | {bytes, Bin}]), + "re-send trans reply", [T, {bytes, Bin}]), case megaco_messenger_misc:send_message(ConnData2, true, Bin) of {ok, _} -> ok; diff --git a/lib/megaco/src/engine/megaco_user.erl b/lib/megaco/src/engine/megaco_user.erl new file mode 100644 index 0000000000..47fb1a119d --- /dev/null +++ b/lib/megaco/src/engine/megaco_user.erl @@ -0,0 +1,386 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2019-2019. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +%% +%%------------------------------------------------------------------------- +%% Purpose: Megaco user behaviour module +%% +%% This callback functions are the default! Its possible for the user to +%% provide a arbitrary number of "extra" arguments via the user_args +%% config option. +%% So, for instance, the handle_connect/2 could instead become +%% handle_connect/4 if the user sets the user_args option to [foo, bar]. +%% This means that its impossible to define a proper behaviour. +%% So what we do here is to define a behaviour with the "default interface" +%% (the user_args option has the [] as the default value) and set them +%% all to be optional! +%%------------------------------------------------------------------------- + +-module(megaco_user). + +-export_type([ + receive_handle/0, + conn_handle/0, + megaco_timer/0 + ]). + +-include_lib("megaco/include/megaco.hrl"). +%% -include_lib("megaco/include/megaco_message_v1.hrl"). + +-type receive_handle() :: #megaco_receive_handle{}. +-type conn_handle() :: #megaco_conn_handle{}. +-type megaco_timer() :: infinity | non_neg_integer() | #megaco_incr_timer{}. + +-callback handle_connect(ConnHandle, ProtocolVersion) -> + ok | error | {error, ErrorDescr} when + ConnHandle :: conn_handle(), + ProtocolVersion :: megaco_encoder:protocol_version(), + ErrorDescr :: megaco_encoder:error_desc(). +-callback handle_connect(ConnHandle, ProtocolVersion, Extra) -> + ok | error | {error, ErrorDescr} when + ConnHandle :: conn_handle(), + ProtocolVersion :: megaco_encoder:protocol_version(), + Extra :: term(), + ErrorDescr :: megaco_encoder:error_desc(). + +-callback handle_disconnect(ConnHandle, ProtocolVersion, Reason) -> + megaco:void() when + ConnHandle :: conn_handle(), + ProtocolVersion :: megaco_encoder:protocol_version(), + Reason :: term(). + +-callback handle_syntax_error(ReceiveHandle, ProtocolVersion, DefaultED) -> + reply | {reply, ED} | no_reply | {no_reply, ED} when + ReceiveHandle :: receive_handle(), + ProtocolVersion :: megaco_encoder:protocol_version(), + DefaultED :: megaco_encoder:error_desc(), + ED :: megaco_encoder:error_desc(). +-callback handle_syntax_error(ReceiveHandle, ProtocolVersion, DefaultED, Extra) -> + reply | {reply, ED} | no_reply | {no_reply, ED} when + ReceiveHandle :: receive_handle(), + ProtocolVersion :: megaco_encoder:protocol_version(), + DefaultED :: megaco_encoder:error_desc(), + ED :: megaco_encoder:error_desc(), + Extra :: term(). + +-callback handle_message_error(ConnHandle, ProtocolVersion, ErrorDescr) -> + megaco:void() when + ConnHandle :: conn_handle(), + ProtocolVersion :: megaco_encoder:protocol_version(), + ErrorDescr :: megaco_encoder:error_desc(). +-callback handle_message_error(ConnHandle, ProtocolVersion, ErrorDescr, Extra) -> + megaco:void() when + ConnHandle :: conn_handle(), + ProtocolVersion :: megaco_encoder:protocol_version(), + ErrorDescr :: megaco_encoder:error_desc(), + Extra :: term(). + +-callback handle_trans_request(ConnHandle, ProtocolVersion, ActionRequests) -> + Pending | Reply | ignore_trans_request when + ConnHandle :: conn_handle(), + ProtocolVersion :: megaco_encoder:protocol_version(), + ActionRequests :: [megaco_encoder:action_request()], + Pending :: {pending, ReqData}, + ReqData :: term(), + Reply :: {AckAction, ActualReply} | + {AckAction, ActualReply, SendOptions}, + AckAction :: discard_ack | + {handle_ack, AckData} | + {handle_pending_ack, AckData} | + {handle_sloppy_ack, AckData}, + ActualReply :: [megaco_encoder:action_reply()] | + megaco_encoder:error_desc(), + AckData :: term(), + SendOptions :: [SendOption], + SendOption :: {reply_timer, megaco_timer()} | + {send_handle, term()} | + {protocol_version, integer()}. +-callback handle_trans_request(ConnHandle, + ProtocolVersion, + ActionRequests, + Extra) -> + Pending | Reply | ignore_trans_request when + ConnHandle :: conn_handle(), + ProtocolVersion :: megaco_encoder:protocol_version(), + ActionRequests :: [megaco_encoder:action_request()], + Extra :: term(), + Pending :: {pending, ReqData}, + ReqData :: term(), + Reply :: {AckAction, ActualReply} | + {AckAction, ActualReply, SendOptions}, + AckAction :: discard_ack | + {handle_ack, AckData} | + {handle_pending_ack, AckData} | + {handle_sloppy_ack, AckData}, + ActualReply :: [megaco_encoder:action_reply()] | + megaco_encoder:error_desc(), + AckData :: term(), + SendOptions :: [SendOption], + SendOption :: {reply_timer, megaco_timer()} | + {send_handle, term()} | + {protocol_version, integer()}. + +-callback handle_trans_long_request(ConnHandle, ProtocolVersion, ReqData) -> + Reply when + ConnHandle :: conn_handle(), + ProtocolVersion :: megaco_encoder:protocol_version(), + ReqData :: term(), + Reply :: {AckAction, ActualReply} | + {AckAction, ActualReply, SendOptions}, + AckAction :: discard_ack | + {handle_ack, AckData} | + {handle_sloppy_ack, AckData}, + ActualReply :: [megaco_encoder:action_reply()] | + megaco_encoder:error_desc(), + AckData :: term(), + SendOptions :: [SendOption], + SendOption :: {reply_timer, megaco_timer()} | + {send_handle, term()} | + {protocol_version, megaco_encoder:protocol_version()}. +-callback handle_trans_long_request(ConnHandle, ProtocolVersion, ReqData, Extra) -> + Reply when + ConnHandle :: conn_handle(), + ProtocolVersion :: megaco_encoder:protocol_version(), + ReqData :: term(), + Extra :: term(), + Reply :: {AckAction, ActualReply} | + {AckAction, ActualReply, SendOptions}, + AckAction :: discard_ack | + {handle_ack, AckData} | + {handle_sloppy_ack, AckData}, + ActualReply :: [megaco_encoder:action_reply()] | + megaco_encoder:error_desc(), + AckData :: term(), + SendOptions :: [SendOption], + SendOption :: {reply_timer, megaco_timer()} | + {send_handle, term()} | + {protocol_version, megaco_encoder:protocol_version()}. + +-callback handle_trans_reply(ConnHandle, + ProtocolVersion, + UserReply, + ReplyData) -> + ok when + ConnHandle :: conn_handle(), + ProtocolVersion :: megaco_encoder:protocol_version(), + UserReply :: Success | Failure, + ReplyData :: term(), + Success :: {ok, Result}, + Result :: TransactionResult | SegmentResult, + TransactionResult :: [megaco_encoder:action_reply()], + SegmentResult :: {megaco_encoder:segment_no(), + LastSegment, + [megaco_encoder:action_reply()]}, + Failure :: {error, Reason} | + {error, ReplyNo, Reason}, + Reason :: TransactionReason | + SegmentReason | + UserCancelReason | + SendReason | + OtherReason, + TransactionReason :: megaco_encoder:error_desc(), + SegmentReason :: {megaco_encoder:segment_no(), + LastSegment, + megaco_encoder:error_desc()}, + OtherReason :: timeout | + {segment_timeout, MissingSegments} | + exceeded_recv_pending_limit | term(), + LastSegment :: boolean(), + MissingSegments :: [megaco_encoder:segment_no()], + UserCancelReason :: {user_cancel, ReasonForUserCancel}, + ReasonForUserCancel :: term(), + SendReason :: SendCancelledReason | SendFailedReason, + SendCancelledReason :: {send_message_cancelled, + ReasonForSendCancel}, + ReasonForSendCancel :: term(), + SendFailedReason :: {send_message_failed, ReasonForSendFailure}, + ReasonForSendFailure :: term(), + ReplyNo :: pos_integer(). +-callback handle_trans_reply(ConnHandle, + ProtocolVersion, + UserReply, + ReplyData, + Extra) -> + ok when + ConnHandle :: conn_handle(), + ProtocolVersion :: megaco_encoder:protocol_version(), + UserReply :: Success | Failure, + ReplyData :: term(), + Extra :: term(), + Success :: {ok, Result}, + Result :: TransactionResult | SegmentResult, + TransactionResult :: [megaco_encoder:action_reply()], + SegmentResult :: {megaco_encoder:segment_no(), + LastSegment, + [megaco_encoder:action_reply()]}, + Failure :: {error, Reason} | + {error, ReplyNo, Reason}, + Reason :: TransactionReason | + SegmentReason | + UserCancelReason | + SendReason | + OtherReason, + TransactionReason :: megaco_encoder:error_desc(), + SegmentReason :: {megaco_encoder:segment_no(), + LastSegment, + megaco_encoder:error_desc()}, + OtherReason :: timeout | + {segment_timeout, MissingSegments} | + exceeded_recv_pending_limit | term(), + LastSegment :: boolean(), + MissingSegments :: [megaco_encoder:segment_no()], + UserCancelReason :: {user_cancel, ReasonForUserCancel}, + ReasonForUserCancel :: term(), + SendReason :: SendCancelledReason | SendFailedReason, + SendCancelledReason :: {send_message_cancelled, + ReasonForSendCancel}, + ReasonForSendCancel :: term(), + SendFailedReason :: {send_message_failed, ReasonForSendFailure}, + ReasonForSendFailure :: term(), + ReplyNo :: pos_integer(). + + +-callback handle_trans_ack(ConnHandle, + ProtocolVersion, + AckStatus, + AckData) -> + ok when + ConnHandle :: conn_handle(), + ProtocolVersion :: megaco_encoder:protocol_version(), + AckStatus :: ok | {error, Reason}, + AckData :: term(), + Reason :: UserCancelReason | SendReason | OtherReason, + UserCancelReason :: {user_cancel, ReasonForUserCancel}, + ReasonForUserCancel :: term(), + SendReason :: SendCancelledReason | SendFailedReason, + SendCancelledReason :: {send_message_cancelled, ReasonForSendCancel}, + ReasonForSendCancel :: term(), + SendFailedReason :: {send_message_failed, ReasonForSendFailure}, + ReasonForSendFailure :: term(), + OtherReason :: term(). +-callback handle_trans_ack(ConnHandle, + ProtocolVersion, + AckStatus, + AckData, + Extra) -> + ok when + ConnHandle :: conn_handle(), + ProtocolVersion :: megaco_encoder:protocol_version(), + AckStatus :: ok | {error, Reason}, + AckData :: term(), + Extra :: term(), + Reason :: UserCancelReason | SendReason | OtherReason, + UserCancelReason :: {user_cancel, ReasonForUserCancel}, + ReasonForUserCancel :: term(), + SendReason :: SendCancelledReason | SendFailedReason, + SendCancelledReason :: {send_message_cancelled, ReasonForSendCancel}, + ReasonForSendCancel :: term(), + SendFailedReason :: {send_message_failed, ReasonForSendFailure}, + ReasonForSendFailure :: term(), + OtherReason :: term(). + +-callback handle_unexpected_trans(ConnHandle, ProtocolVersion, Trans) -> + ok when + ConnHandle :: conn_handle(), + ProtocolVersion :: megaco_encoder:protocol_version(), + Trans :: megaco_encoder:transaction_pending() | + megaco_encoder:transaction_reply() | + megaco_encoder:transaction_response_ack(). +-callback handle_unexpected_trans(ConnHandle, ProtocolVersion, Trans, Extra) -> + ok when + ConnHandle :: conn_handle(), + ProtocolVersion :: megaco_encoder:protocol_version(), + Trans :: megaco_encoder:transaction_pending() | + megaco_encoder:transaction_reply() | + megaco_encoder:transaction_response_ack(), + Extra :: term(). + +-callback handle_trans_request_abort(ConnHandle, + ProtocolVersion, + TransNo, + Pid) -> + ok when + ConnHandle :: conn_handle(), + ProtocolVersion :: megaco_encoder:protocol_version(), + TransNo :: integer(), + Pid :: undefined | pid(). +-callback handle_trans_request_abort(ConnHandle, + ProtocolVersion, + TransNo, + Pid, + Extra) -> + ok when + ConnHandle :: conn_handle(), + ProtocolVersion :: megaco_encoder:protocol_version(), + TransNo :: integer(), + Pid :: undefined | pid(), + Extra :: term(). + +-callback handle_segment_reply(ConnHandle, + ProtocolVersion, + TransNo, + SegNo, + SegCompl) -> + ok when + ConnHandle :: conn_handle(), + ProtocolVersion :: megaco_encoder:protocol_version(), + TransNo :: integer(), + SegNo :: integer(), + SegCompl :: asn1_NOVALUE | 'NULL'. +-callback handle_segment_reply(ConnHandle, + ProtocolVersion, + TransNo, + SegNo, + SegCompl, + Extra) -> + ok when + ConnHandle :: conn_handle(), + ProtocolVersion :: megaco_encoder:protocol_version(), + TransNo :: integer(), + SegNo :: megaco_encoder:segment_no(), + SegCompl :: asn1_NOVALUE | 'NULL', + Extra :: term(). + +-optional_callbacks( + [ + %% The actual number of arguments to *all* functions, + %% depend of the user_args config option. + handle_connect/2, + handle_connect/3, + handle_disconnect/3, + handle_syntax_error/3, + handle_syntax_error/4, + handle_message_error/3, + handle_message_error/4, + handle_trans_request/3, + handle_trans_request/4, + handle_trans_long_request/3, + handle_trans_long_request/4, + handle_trans_reply/4, + handle_trans_reply/5, + handle_trans_ack/4, + handle_trans_ack/5, + handle_unexpected_trans/3, + handle_unexpected_trans/4, + handle_trans_request_abort/4, + handle_trans_request_abort/5, + handle_segment_reply/5, + handle_segment_reply/6 + ]). diff --git a/lib/megaco/src/engine/modules.mk b/lib/megaco/src/engine/modules.mk index b74a096e40..a7f82c1836 100644 --- a/lib/megaco/src/engine/modules.mk +++ b/lib/megaco/src/engine/modules.mk @@ -2,7 +2,7 @@ # %CopyrightBegin% # -# Copyright Ericsson AB 2001-2016. All Rights Reserved. +# Copyright Ericsson AB 2001-2019. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ BEHAVIOUR_MODULES = \ megaco_edist_compress \ megaco_encoder \ + megaco_user \ megaco_transport MODULES = \ diff --git a/lib/megaco/src/text/megaco_text_gen_prev3a.hrl b/lib/megaco/src/text/megaco_text_gen_prev3a.hrl index db7507fd27..4f3c83c6c5 100644 --- a/lib/megaco/src/text/megaco_text_gen_prev3a.hrl +++ b/lib/megaco/src/text/megaco_text_gen_prev3a.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2017. All Rights Reserved. +%% Copyright Ericsson AB 2004-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -2841,6 +2841,7 @@ enc_integer(Val, _State, Min, Max) -> enc_list(List, State) -> enc_list(List, State, fun(_S) -> ?COMMA_INDENT(_S) end, false). +-dialyzer({nowarn_function, enc_list/4}). % Future compat enc_list([], _State, _SepEncoder, _NeedsSep) -> []; enc_list([{Elems, ElemEncoder} | Tail], State, SepEncoder, NeedsSep) -> diff --git a/lib/megaco/src/text/megaco_text_gen_prev3b.hrl b/lib/megaco/src/text/megaco_text_gen_prev3b.hrl index d5f29025c8..de64f8bbdf 100644 --- a/lib/megaco/src/text/megaco_text_gen_prev3b.hrl +++ b/lib/megaco/src/text/megaco_text_gen_prev3b.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2017. All Rights Reserved. +%% Copyright Ericsson AB 2005-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -2862,6 +2862,7 @@ enc_integer(Val, _State, Min, Max) -> enc_list(List, State) -> enc_list(List, State, fun(_S) -> ?COMMA_INDENT(_S) end, false). +-dialyzer({nowarn_function, enc_list/4}). % Future compat enc_list([], _State, _SepEncoder, _NeedsSep) -> []; enc_list([{Elems, ElemEncoder} | Tail], State, SepEncoder, NeedsSep) -> diff --git a/lib/megaco/src/text/megaco_text_gen_prev3c.hrl b/lib/megaco/src/text/megaco_text_gen_prev3c.hrl index 6452be25d0..f73318161f 100644 --- a/lib/megaco/src/text/megaco_text_gen_prev3c.hrl +++ b/lib/megaco/src/text/megaco_text_gen_prev3c.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2017. All Rights Reserved. +%% Copyright Ericsson AB 2005-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -3338,6 +3338,7 @@ enc_integer(Val, _State, Min, Max) -> enc_list(List, State) -> enc_list(List, State, fun(_S) -> ?COMMA_INDENT(_S) end, false). +-dialyzer({nowarn_function, enc_list/4}). % Future compat enc_list([], _State, _SepEncoder, _NeedsSep) -> []; enc_list([{Elems, ElemEncoder} | Tail], State, SepEncoder, NeedsSep) -> diff --git a/lib/megaco/src/text/megaco_text_gen_v1.hrl b/lib/megaco/src/text/megaco_text_gen_v1.hrl index 38a0f6fd6b..6da9ea94ff 100644 --- a/lib/megaco/src/text/megaco_text_gen_v1.hrl +++ b/lib/megaco/src/text/megaco_text_gen_v1.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2016. All Rights Reserved. +%% Copyright Ericsson AB 2000-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -2303,6 +2303,7 @@ enc_integer(Val, _State, Min, Max) -> enc_list(List, State) -> enc_list(List, State, fun(_S) -> ?COMMA_INDENT(_S) end, false). +-dialyzer({nowarn_function, enc_list/4}). % Future compat enc_list([], _State, _SepEncoder, _NeedsSep) -> []; enc_list([{Elems, ElemEncoder} | Tail], State, SepEncoder, NeedsSep) -> diff --git a/lib/megaco/src/text/megaco_text_gen_v2.hrl b/lib/megaco/src/text/megaco_text_gen_v2.hrl index d9443fb2e1..23afa85800 100644 --- a/lib/megaco/src/text/megaco_text_gen_v2.hrl +++ b/lib/megaco/src/text/megaco_text_gen_v2.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2016. All Rights Reserved. +%% Copyright Ericsson AB 2003-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -2689,6 +2689,7 @@ enc_integer(Val, _State, Min, Max) -> enc_list(List, State) -> enc_list(List, State, fun(_S) -> ?COMMA_INDENT(_S) end, false). +-dialyzer({nowarn_function, enc_list/4}). % Future compat enc_list([], _State, _SepEncoder, _NeedsSep) -> []; enc_list([{Elems, ElemEncoder} | Tail], State, SepEncoder, NeedsSep) -> diff --git a/lib/megaco/src/text/megaco_text_gen_v3.hrl b/lib/megaco/src/text/megaco_text_gen_v3.hrl index dc1f2b9665..e440ec6651 100644 --- a/lib/megaco/src/text/megaco_text_gen_v3.hrl +++ b/lib/megaco/src/text/megaco_text_gen_v3.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2016. All Rights Reserved. +%% Copyright Ericsson AB 2005-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -3353,6 +3353,7 @@ enc_integer(Val, _State, Min, Max) -> enc_list(List, State) -> enc_list(List, State, fun(_S) -> ?COMMA_INDENT(_S) end, false). +-dialyzer({nowarn_function, enc_list/4}). % Future compat enc_list([], _State, _SepEncoder, _NeedsSep) -> []; enc_list([{Elems, ElemEncoder} | Tail], State, SepEncoder, NeedsSep) -> diff --git a/lib/megaco/src/text/megaco_text_mini_parser.hrl b/lib/megaco/src/text/megaco_text_mini_parser.hrl index 487958af08..72d8168abf 100644 --- a/lib/megaco/src/text/megaco_text_mini_parser.hrl +++ b/lib/megaco/src/text/megaco_text_mini_parser.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2016. All Rights Reserved. +%% Copyright Ericsson AB 2004-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -1167,6 +1167,7 @@ ensure_pathName({_TokenTag, _Line, Text}) -> % #'PropertyParm'{name = lists:reverse(Name), % value = [lists:reverse(Value)]}. +-dialyzer({nowarn_function, ensure_uint/3}). % Future compat ensure_uint({_TokenTag, Line, Val}, Min, Max) when is_integer(Val) -> if is_integer(Min) andalso (Val >= Min) -> diff --git a/lib/megaco/src/text/megaco_text_parser_prev3a.hrl b/lib/megaco/src/text/megaco_text_parser_prev3a.hrl index edda850c3a..d7e847993a 100644 --- a/lib/megaco/src/text/megaco_text_parser_prev3a.hrl +++ b/lib/megaco/src/text/megaco_text_parser_prev3a.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2016. All Rights Reserved. +%% Copyright Ericsson AB 2004-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -1589,6 +1589,7 @@ ensure_uint(Token, Min, Max) -> -ifdef(megaco_parser_inline). -compile({inline,[{ensure_uint,4}]}). -endif. +-dialyzer({nowarn_function, ensure_uint/4}). % Future compat ensure_uint(Val, Min, Max, Line) -> if is_integer(Min) andalso (Val >= Min) -> diff --git a/lib/megaco/src/text/megaco_text_parser_prev3b.hrl b/lib/megaco/src/text/megaco_text_parser_prev3b.hrl index 4eaa3733c4..479f963c70 100644 --- a/lib/megaco/src/text/megaco_text_parser_prev3b.hrl +++ b/lib/megaco/src/text/megaco_text_parser_prev3b.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2016. All Rights Reserved. +%% Copyright Ericsson AB 2005-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -1635,6 +1635,7 @@ ensure_uint(Token, Min, Max) -> -ifdef(megaco_parser_inline). -compile({inline,[{ensure_uint,4}]}). -endif. +-dialyzer({nowarn_function, ensure_uint/4}). % Future compat ensure_uint(Val, Min, Max, Line) -> if is_integer(Min) andalso (Val >= Min) -> diff --git a/lib/megaco/src/text/megaco_text_parser_prev3c.hrl b/lib/megaco/src/text/megaco_text_parser_prev3c.hrl index d2faad09d9..3ed9582308 100644 --- a/lib/megaco/src/text/megaco_text_parser_prev3c.hrl +++ b/lib/megaco/src/text/megaco_text_parser_prev3c.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2016. All Rights Reserved. +%% Copyright Ericsson AB 2005-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -1898,6 +1898,7 @@ ensure_uint(Token, Min, Max) -> -ifdef(megaco_parser_inline). -compile({inline,[{ensure_uint,4}]}). -endif. +-dialyzer({nowarn_function, ensure_uint/4}). % Future compat ensure_uint(Val, Min, Max, Line) -> if is_integer(Min) andalso (Val >= Min) -> diff --git a/lib/megaco/src/text/megaco_text_parser_v1.hrl b/lib/megaco/src/text/megaco_text_parser_v1.hrl index f48ac745f0..3a19debba0 100644 --- a/lib/megaco/src/text/megaco_text_parser_v1.hrl +++ b/lib/megaco/src/text/megaco_text_parser_v1.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2016. All Rights Reserved. +%% Copyright Ericsson AB 2000-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -1337,6 +1337,7 @@ ensure_uint(Token, Min, Max) -> -ifdef(megaco_parser_inline). -compile({inline,[{ensure_uint,4}]}). -endif. +-dialyzer({nowarn_function, ensure_uint/4}). % Future compat ensure_uint(Val, Min, Max, Line) -> if is_integer(Min) andalso (Val >= Min) -> diff --git a/lib/megaco/src/text/megaco_text_parser_v2.hrl b/lib/megaco/src/text/megaco_text_parser_v2.hrl index f3c2f69193..270541d111 100644 --- a/lib/megaco/src/text/megaco_text_parser_v2.hrl +++ b/lib/megaco/src/text/megaco_text_parser_v2.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2016. All Rights Reserved. +%% Copyright Ericsson AB 2003-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -1563,6 +1563,7 @@ ensure_uint(Token, Min, Max) -> -ifdef(megaco_parser_inline). -compile({inline,[{ensure_uint,4}]}). -endif. +-dialyzer({nowarn_function, ensure_uint/4}). % Future compat ensure_uint(Val, Min, Max, Line) -> if is_integer(Min) andalso (Val >= Min) -> diff --git a/lib/megaco/src/text/megaco_text_parser_v3.hrl b/lib/megaco/src/text/megaco_text_parser_v3.hrl index 38822e4952..7b8fff2080 100644 --- a/lib/megaco/src/text/megaco_text_parser_v3.hrl +++ b/lib/megaco/src/text/megaco_text_parser_v3.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2016. All Rights Reserved. +%% Copyright Ericsson AB 2005-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -1920,6 +1920,7 @@ ensure_uint(Token, Min, Max) -> -ifdef(megaco_parser_inline). -compile({inline,[{ensure_uint,4}]}). -endif. +-dialyzer({nowarn_function, ensure_uint/4}). % Future compat ensure_uint(Val, Min, Max, Line) -> if is_integer(Min) andalso (Val >= Min) -> diff --git a/lib/megaco/test/megaco_examples_test.erl b/lib/megaco/test/megaco_examples_test.erl index 45a6c5011a..10ca0375f6 100644 --- a/lib/megaco/test/megaco_examples_test.erl +++ b/lib/megaco/test/megaco_examples_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2016. All Rights Reserved. +%% Copyright Ericsson AB 2001-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -31,6 +31,8 @@ -include_lib("megaco/include/megaco.hrl"). -include_lib("megaco/include/megaco_message_v1.hrl"). +-define(LIB, megaco_test_lib). + t() -> megaco_test_lib:t(?MODULE). t(Case) -> megaco_test_lib:t({?MODULE, Case}). @@ -56,9 +58,19 @@ load_examples() -> {error, Reason} -> {error, Reason}; Dir -> - [code:load_abs(filename:join([Dir, examples, simple, M])) || M <- example_modules()] + SimpleDir = filename:join([Dir, examples, simple]), + case code:add_path(SimpleDir) of + true -> + ok; + {error, What} -> + error_logger:error_msg("failed adding examples path: " + "~n ~p" + "~n", [What]), + {error, {failed_add_path, What}} + end end. + purge_examples() -> case code:lib_dir(megaco) of {error, Reason} -> @@ -67,6 +79,7 @@ purge_examples() -> [code:purge(M) || M <- example_modules()] end. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Top test case @@ -87,94 +100,263 @@ end_per_group(_GroupName, Config) -> simple(suite) -> []; simple(Config) when is_list(Config) -> - ?ACQUIRE_NODES(1, Config), - d("simple -> proxy start",[]), - ProxyPid = megaco_test_lib:proxy_start({?MODULE, ?LINE}), + process_flag(trap_exit, true), + d("simple -> create node name(s)"), + [_Local, MGC, MG] = ?LIB:mk_nodes(3), %% Grrr + Nodes = [MGC, MG], + + d("simple -> start nodes"), + ok = ?LIB:start_nodes(Nodes, ?MODULE, ?LINE), + + MGCId = "MGC", + MGId = "MG", + + d("simple -> MGC proxy start (on ~p)", [MGC]), + MGCProxy = megaco_test_lib:proxy_start(MGC, "MGC"), + ?SLEEP(1000), + + d("simple -> MG proxy start (on ~p)", [MG]), + MGProxy = megaco_test_lib:proxy_start(MG, "MG"), + ?SLEEP(1000), + + MegacoStart = fun() -> megaco:start() end, + MegacoStartVerify = + fun(_, ok) -> ok; + (Id, Else) -> ?ERROR({failed_starting_megaco, Id, Else}) + end, - d("simple -> start megaco",[]), - ?VERIFY(ok, megaco:start()), + d("simple -> start MGC megaco"), + exec(MGCProxy, MGCId, MegacoStart, + fun(Res) -> MegacoStartVerify(MGCId, Res) end), + %% ?APPLY(MGCProxy, fun() -> ok = megaco:start() end), + ?SLEEP(1000), - d("simple -> start mgc",[]), - ?APPLY(ProxyPid, fun() -> megaco_simple_mgc:start() end), + d("simple -> start MG megaco"), + exec(MGProxy, MGId, MegacoStart, + fun(Res) -> MegacoStartVerify(MGId, Res) end), + %% ?APPLY(MGProxy, fun() -> ok = megaco:start() end), + ?SLEEP(1000), + + d("simple -> start mgc"), + start_mgc(MGCProxy), + ?SLEEP(1000), + + d("simple -> verify MGC info (no mg)"), + info(MGCProxy), + ?SLEEP(1000), + + d("simple -> verify MGC system_info(users) (no mg)"), + users(MGCProxy), + ?SLEEP(1000), + + d("simple -> start mg"), + start_mg(MGProxy), + ?SLEEP(1000), + + d("simple -> verify MGC info (mg)"), + info(MGCProxy), + ?SLEEP(1000), + + d("simple -> verify MGC system_info(users) (mg)"), + users(MGCProxy), + ?SLEEP(1000), + + d("simple -> verify MG info"), + info(MGProxy), + ?SLEEP(1000), + + d("simple -> verify MG system_info(users)"), + users(MGProxy), + ?SLEEP(1000), + + d("simple -> stop mgc"), + exec(MGCProxy, MGCId, + fun() -> megaco_simple_mgc:stop() end, + fun([_]) -> ok; + (L) when is_list(L) -> + ?ERROR({invalid_users, L}); + (X) -> + ?ERROR({invalid_result, X}) + end), + %% ?VERIFY(5, length()), + ?SLEEP(1000), + + d("simple -> verify MGC info (no mgc)"), + info(MGCProxy), + ?SLEEP(1000), + + d("simple -> verify MG info (no mgc)"), + info(MGProxy), + ?SLEEP(1000), + + d("simple -> verify MGC system_info(users) (no mgc)",[]), + users(MGCProxy), + ?SLEEP(1000), + + d("simple -> verify MG system_info(users) (no mgc)",[]), + users(MGProxy), + ?SLEEP(1000), + + MegacoStop = fun() -> megaco:stop() end, + MegacoStopVerify = + fun(_, ok) -> ok; + (Id, Else) -> ?ERROR({failed_stop_megaco, Id, Else}) + end, + + d("simple -> stop MG megaco",[]), + exec(MGProxy, MGId, MegacoStop, + fun(Res) -> MegacoStopVerify(MGId, Res) end), + %% ?VERIFY(ok, megaco:stop()), + ?SLEEP(1000), + + d("simple -> stop MGC megaco",[]), + exec(MGCProxy, MGCId, MegacoStop, + fun(Res) -> MegacoStopVerify(MGCId, Res) end), + %% ?VERIFY(ok, megaco:stop()), + ?SLEEP(1000), + + d("simple -> kill (exit) MG Proxy: ~p", [MGProxy]), + MGProxy ! {stop, self(), normal}, + receive + {'EXIT', MGProxy, _} -> + d("simple -> MG Proxy terminated"), + ok + end, + + d("simple -> kill (exit) MGC Proxy: ~p", [MGCProxy]), + MGCProxy ! {stop, self(), normal}, + receive + {'EXIT', MGCProxy, _} -> + d("simple -> MGC Proxy terminated"), + ok + end, + + d("simple -> stop ~p", [MGC]), + slave:stop(MGC), + + d("simple -> stop ~p", [MG]), + slave:stop(MG), + + d("simple -> done", []), + ok. + + +exec(Proxy, Id, Cmd, Verify) -> + ?APPLY(Proxy, Cmd), + receive + {res, Id, Res} -> + Verify(Res) + end. + + +start_mgc(Proxy) -> + ?APPLY(Proxy, + fun() -> + try megaco_simple_mgc:start() of + Res -> + Res + catch + C:E:S -> + {error, {{catched, C, E, S}}, code:get_path()} + end + end), receive {res, _, {ok, MgcAll}} when is_list(MgcAll) -> MgcBad = [MgcRes || MgcRes <- MgcAll, element(1, MgcRes) /= ok], ?VERIFY([], MgcBad), - %% MgcGood = MgcAll -- MgcBad, - %% MgcRecHandles = [MgcRH || {ok, _MgcPort, MgcRH} <- MgcGood], - - d("simple -> start mg",[]), - ?APPLY(ProxyPid, fun() -> megaco_simple_mg:start() end), - receive - {res, _, MgList} when is_list(MgList) andalso (length(MgList) =:= 4) -> - d("simple -> received res: ~p",[MgList]), - Verify = - fun({_MgMid, {TransId, Res}}) when TransId =:= 1 -> - case Res of - {ok, [AR]} when is_record(AR, 'ActionReply') -> - case AR#'ActionReply'.commandReply of - [{serviceChangeReply, SCR}] -> - case SCR#'ServiceChangeReply'.serviceChangeResult of - {serviceChangeResParms, MgcMid} when MgcMid /= asn1_NOVALUE -> - ok; - Error -> - ?ERROR(Error) - end; - Error -> - ?ERROR(Error) - end; - Error -> - ?ERROR(Error) - end; - (Error) -> - ?ERROR(Error) - end, - lists:map(Verify, MgList); - Error -> - ?ERROR(Error) - end; + ok; Error -> ?ERROR(Error) - end, - d("simple -> verify info()",[]), - info(), - d("simple -> verify system_info(users)",[]), - users(), - d("simple -> stop mgc",[]), - ?VERIFY(5, length(megaco_simple_mgc:stop())), - d("simple -> verify system_info(users)",[]), - users(), - d("simple -> stop megaco",[]), - ?VERIFY(ok, megaco:stop()), - d("simple -> kill (exit) ProxyPid: ~p",[ProxyPid]), - exit(ProxyPid, shutdown), % Controlled kill of transport supervisors + end. - ok. +start_mg(Proxy) -> + ?APPLY(Proxy, fun() -> + try megaco_simple_mg:start() of + Res -> + Res + catch + C:E:S -> + {error, {{catched, C, E, S}}, code:get_path()} + end + end), + receive + {res, _, MGs} when is_list(MGs) andalso (length(MGs) =:= 4) -> + verify_mgs(MGs); + Error -> + ?ERROR(Error) + end. -info() -> - case (catch megaco:info()) of - {'EXIT', _} = Error -> - ?ERROR(Error); - Info -> - ?LOG("Ok, ~p~n", [Info]) +verify_mgs(MGs) -> + Verify = + fun({_MgMid, {TransId, Res}}) when (TransId =:= 1) -> + case Res of + {ok, [AR]} when is_record(AR, 'ActionReply') -> + case AR#'ActionReply'.commandReply of + [{serviceChangeReply, SCR}] -> + case SCR#'ServiceChangeReply'.serviceChangeResult of + {serviceChangeResParms, MgcMid} + when (MgcMid =/= asn1_NOVALUE) -> + ok; + Error -> + ?ERROR(Error) + end; + Error -> + ?ERROR(Error) + end; + Error -> + ?ERROR(Error) + end; + (Error) -> + ?ERROR(Error) + end, + lists:map(Verify, MGs). + + +info(Proxy) -> + ?APPLY(Proxy, + fun() -> + try megaco:info() of + I -> I + catch + C:E:S -> + {error, {C, E, S}} + end + end), + receive + {res, _, Info} when is_list(Info) -> + ?LOG("Ok, ~p~n", [Info]); + {res, _, Error} -> + ?ERROR(Error) end. -users() -> - case (catch megaco:system_info(users)) of - {'EXIT', _} = Error -> - ?ERROR(Error); - Users -> - ?LOG("Ok, ~p~n", [Users]) + +users(Proxy) -> + ?APPLY(Proxy, + fun() -> + try megaco:system_info(users) of + I -> I + catch + C:E:S -> + {error, {C, E, S}} + end + end), + receive + {res, _, Info} when is_list(Info) -> + ?LOG("Ok, ~p~n", [Info]); + {res, _, Error} -> + ?ERROR(Error) end. -d(F,A) -> - d(get(dbg),F,A). +d(F) -> + d(F, []). +d(F, A) -> + d(get(dbg), F, A). -d(true,F,A) -> - io:format("DBG: " ++ F ++ "~n",A); +d(true, F, A) -> + io:format("DBG: ~s " ++ F ++ "~n", [?FTS() | A]); d(_, _F, _A) -> ok. diff --git a/lib/megaco/test/megaco_mess_test.erl b/lib/megaco/test/megaco_mess_test.erl index 7af6f26bf1..d3216c2a6d 100644 --- a/lib/megaco/test/megaco_mess_test.erl +++ b/lib/megaco/test/megaco_mess_test.erl @@ -597,6 +597,7 @@ request_and_no_reply(Config) when is_list(Config) -> ET = [{text,tcp}, {text,udp}, {binary,tcp}, {binary,udp}], {ok, Mgc} = ?MGC_START(MgcNode, {deviceName, "ctrl"}, ET, [], ?MGC_VERBOSITY), + ?SLEEP(?SECONDS(1)), i("[MG] start"), Mg1Mid = {deviceName, "mg1"}, @@ -621,9 +622,13 @@ request_and_no_reply(Config) when is_list(Config) -> {pending_timer, PendingTmr}, {reply_timer, ReplyTmr}], {ok, Mg1} = ?MG_START(Mg1Node, Mg1Mid, text, tcp, MgConfig, ?MG_VERBOSITY), + ?SLEEP(?SECONDS(1)), {ok, Mg2} = ?MG_START(Mg2Node, Mg2Mid, text, udp, MgConfig, ?MG_VERBOSITY), + ?SLEEP(?SECONDS(1)), {ok, Mg3} = ?MG_START(Mg3Node, Mg3Mid, binary, tcp, MgConfig, ?MG_VERBOSITY), + ?SLEEP(?SECONDS(1)), {ok, Mg4} = ?MG_START(Mg4Node, Mg4Mid, binary, udp, MgConfig, ?MG_VERBOSITY), + ?SLEEP(?SECONDS(1)), d("MG1 user info: ~p", [?MG_USER_INFO(Mg1, all)]), d("MG1 conn info: ~p", [?MG_CONN_INFO(Mg1, all)]), @@ -639,55 +644,68 @@ request_and_no_reply(Config) when is_list(Config) -> d("service change result: ~p", [ServChRes1]), d("MG1 user info: ~p", [?MG_USER_INFO(Mg1, all)]), d("MG1 conn info: ~p", [?MG_CONN_INFO(Mg1, all)]), + ?SLEEP(?SECONDS(1)), i("[MG2] connect to the MGC (service change)"), ServChRes2 = ?MG_SERV_CHANGE(Mg2), d("service change result: ~p", [ServChRes2]), d("MG2 user info: ~p", [?MG_USER_INFO(Mg2, all)]), d("MG2 conn info: ~p", [?MG_CONN_INFO(Mg2, all)]), + ?SLEEP(?SECONDS(1)), i("[MG3] connect to the MGC (service change)"), ServChRes3 = ?MG_SERV_CHANGE(Mg3), d("service change result: ~p", [ServChRes3]), d("MG3 user info: ~p", [?MG_USER_INFO(Mg3, all)]), d("MG3 conn info: ~p", [?MG_CONN_INFO(Mg3, all)]), + ?SLEEP(?SECONDS(1)), i("[MG4] connect to the MGC (service change)"), ServChRes4 = ?MG_SERV_CHANGE(Mg4), d("service change result: ~p", [ServChRes4]), d("MG4 user info: ~p", [?MG_USER_INFO(Mg4, all)]), d("MG4 conn info: ~p", [?MG_CONN_INFO(Mg4, all)]), + ?SLEEP(?SECONDS(1)), d("tell the MGC to ignore requests"), ?MGC_REQ_PEND(Mgc, infinity), + ?SLEEP(?SECONDS(1)), d("[MG1] send the notify"), ?MG_NOTIF_REQ(Mg1), + ?SLEEP(?SECONDS(1)), d("[MG2] send the notify"), ?MG_NOTIF_REQ(Mg2), + ?SLEEP(?SECONDS(1)), d("[MG3] send the notify"), ?MG_NOTIF_REQ(Mg3), + ?SLEEP(?SECONDS(1)), d("[MG4] send the notify"), ?MG_NOTIF_REQ(Mg4), + ?SLEEP(?SECONDS(1)), d("[MG1] await notify reply"), {ok, {_Vsn1, {error, timeout}}} = ?MG_AWAIT_NOTIF_REP(Mg1), d("[MG1] received expected reply"), + ?SLEEP(?SECONDS(1)), d("[MG2] await notify reply"), {ok, {_Vsn2, {error, timeout}}} = ?MG_AWAIT_NOTIF_REP(Mg2), d("[MG2] received expected reply"), + ?SLEEP(?SECONDS(1)), d("[MG3] await notify reply"), {ok, {_Vsn3, {error, timeout}}} = ?MG_AWAIT_NOTIF_REP(Mg3), d("[MG3] received expected reply"), + ?SLEEP(?SECONDS(1)), d("[MG4] await notify reply"), {ok, {_Vsn4, {error, timeout}}} = ?MG_AWAIT_NOTIF_REP(Mg4), d("[MG4] received expected reply"), + ?SLEEP(?SECONDS(1)), d("MG1 user info: ~p", [?MG_USER_INFO(Mg1, all)]), d("MG1 conn info: ~p", [?MG_CONN_INFO(Mg1, all)]), @@ -13711,38 +13729,36 @@ i(F) -> i(F, []). i(F, A) -> - print(info, get(verbosity), now(), get(tc), "INF", F, A). + print(info, "INF", F, A). d(F) -> d(F, []). d(F, A) -> - print(debug, get(verbosity), now(), get(tc), "DBG", F, A). + print(debug, "DBG", F, A). -printable(_, debug) -> true; -printable(info, info) -> true; -printable(_,_) -> false. +print(Severity, PRE, FMT, ARGS) -> + print(Severity, get(verbosity), erlang:timestamp(), get(tc), PRE, FMT, ARGS). print(Severity, Verbosity, Ts, Tc, P, F, A) -> print(printable(Severity,Verbosity), Ts, Tc, P, F, A). -print(true, Ts, Tc, P, F, A) -> - io:format("*** [~s] ~s ~p ~s:~w ***" - "~n " ++ F ++ "~n", - [format_timestamp(Ts), P, self(), get(sname), Tc | A]); +print(true, TS, TC, P, F, A) -> + S = ?F("*** [~s] ~s ~p ~s:~w ***" + "~n " ++ F ++ "~n", + [megaco:format_timestamp(TS), P, self(), get(sname), TC | A]), + io:format("~s", [S]), + io:format(user, "~s", [S]); print(_, _, _, _, _, _) -> ok. -format_timestamp({_N1, _N2, N3} = Now) -> - {Date, Time} = calendar:now_to_datetime(Now), - {YYYY,MM,DD} = Date, - {Hour,Min,Sec} = Time, - FormatDate = - io_lib:format("~.4w:~.2.0w:~.2.0w ~.2.0w:~.2.0w:~.2.0w 4~w", - [YYYY,MM,DD,Hour,Min,Sec,round(N3/1000)]), - lists:flatten(FormatDate). + +printable(_, debug) -> true; +printable(info, info) -> true; +printable(_,_) -> false. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -13752,13 +13768,6 @@ to(To, Start) -> %% Time in milli seconds mtime() -> - {A,B,C} = erlang:now(), + {A,B,C} = erlang:timestamp(), A*1000000000+B*1000+(C div 1000). -%% random_init() -> -%% {A,B,C} = now(), -%% random:seed(A,B,C). - -%% random() -> -%% 10 * random:uniform(50). - diff --git a/lib/megaco/test/megaco_mib_test.erl b/lib/megaco/test/megaco_mib_test.erl index d644d6bc09..06ac7c08ed 100644 --- a/lib/megaco/test/megaco_mib_test.erl +++ b/lib/megaco/test/megaco_mib_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2016. All Rights Reserved. +%% Copyright Ericsson AB 2002-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -24,7 +24,32 @@ %%---------------------------------------------------------------------- -module(megaco_mib_test). --compile(export_all). +-export([ + t/0, t/1, + + all/0, + groups/0, + init_per_testcase/2, + end_per_testcase/2, + init_per_group/2, + end_per_group/2, + + plain/1, + connect/1, + traffic/1, + + mg/3, + mgc/3, + + handle_connect/3, + handle_disconnect/4, + handle_syntax_error/4, + handle_message_error/4, + handle_trans_request/4, + handle_trans_long_request/4, + handle_trans_reply/5, + handle_trans_ack/5 + ]). -include("megaco_test_lib.hrl"). -include_lib("megaco/include/megaco.hrl"). @@ -54,6 +79,7 @@ t(Case) -> megaco_test_lib:t({?MODULE, Case}). %% Test server callbacks init_per_testcase(Case, Config) -> + progress("init_per_testcase -> ~w", [Case]), process_flag(trap_exit, true), case Case of traffic -> @@ -65,6 +91,7 @@ init_per_testcase(Case, Config) -> end. end_per_testcase(Case, Config) -> + progress("end_per_testcase -> ~w", [Case]), process_flag(trap_exit, false), megaco_test_lib:end_per_testcase(Case, Config). @@ -197,6 +224,7 @@ connect(Config) when is_list(Config) -> put(verbosity, ?TEST_VERBOSITY), put(sname, "TEST"), i("connect -> starting"), + progress("start nodes"), MgcNode = make_node_name(mgc), Mg1Node = make_node_name(mg1), Mg2Node = make_node_name(mg2), @@ -209,67 +237,85 @@ connect(Config) when is_list(Config) -> %% Start the MGC and MGs ET = [{text,tcp}, {text,udp}, {binary,tcp}, {binary,udp}], + progress("start MGC"), {ok, Mgc} = start_mgc(MgcNode, {deviceName, "ctrl"}, ET, ?MGC_VERBOSITY), + progress("start MG1"), {ok, Mg1} = start_mg(Mg1Node, {deviceName, "mg1"}, text, tcp, ?MG_VERBOSITY), + progress("start MG2"), {ok, Mg2} = start_mg(Mg2Node, {deviceName, "mg2"}, binary, udp, ?MG_VERBOSITY), %% Collect the initial statistics (should be zero if anything) + progress("collect initial MG1 stats"), {ok, Mg1Stats0} = get_stats(Mg1, 1), d("connect -> stats for Mg1: ~n~p", [Mg1Stats0]), + progress("collect initial MG2 stats"), {ok, Mg2Stats0} = get_stats(Mg2, 1), d("connect -> stats for Mg2: ~n~p", [Mg2Stats0]), + progress("collect initial MGC stats"), {ok, MgcStats0} = get_stats(Mgc, 1), d("connect -> stats for Mgc: ~n~p", [MgcStats0]), %% Ask Mg1 to do a service change + progress("perform MG1 service change"), {ok, Res1} = service_change(Mg1), d("connect -> (Mg1) service change result: ~p", [Res1]), %% Collect the statistics + progress("collect MG1 statustics (after service change)"), {ok, Mg1Stats1} = get_stats(Mg1, 1), d("connect -> stats for Mg1: ~n~p", [Mg1Stats1]), + progress("collect MGC statistics (after MG1 service change)"), {ok, MgcStats1} = get_stats(Mgc, 1), d("connect -> stats (1) for Mgc: ~n~p", [MgcStats1]), {ok, MgcStats2} = get_stats(Mgc, 2), d("connect -> stats (2) for Mgc: ~n~p", [MgcStats2]), %% Ask Mg2 to do a service change + progress("perform MG2 service change"), {ok, Res2} = service_change(Mg2), d("connect -> (Mg2) service change result: ~p", [Res2]), %% Collect the statistics + progress("collect MG2 statustics (after service change)"), {ok, Mg2Stats1} = get_stats(Mg2, 1), d("connect -> stats for Mg1: ~n~p", [Mg2Stats1]), + progress("collect MGC statistics (after MG2 service change)"), {ok, MgcStats3} = get_stats(Mgc, 1), d("connect -> stats (1) for Mgc: ~n~p", [MgcStats3]), {ok, MgcStats4} = get_stats(Mgc, 2), d("connect -> stats (2) for Mgc: ~n~p", [MgcStats4]), %% Tell Mg1 to stop + progress("stop MG1"), stop(Mg1), %% Collect the statistics + progress("collect MGC statistics (after MG1 stop)"), {ok, MgcStats5} = get_stats(Mgc, 1), d("connect -> stats (1) for Mgc: ~n~p", [MgcStats5]), {ok, MgcStats6} = get_stats(Mgc, 2), d("connect -> stats (2) for Mgc: ~n~p", [MgcStats6]), %% Tell Mg2 to stop + progress("stop MG2"), stop(Mg2), %% Collect the statistics + progress("collect MGC statistics (after MG2 stop)"), {ok, MgcStats7} = get_stats(Mgc, 1), d("connect -> stats (1) for Mgc: ~n~p", [MgcStats7]), {ok, MgcStats8} = get_stats(Mgc, 2), d("connect -> stats (2) for Mgc: ~n~p", [MgcStats8]), %% Tell Mgc to stop + progress("stop MGC"), stop(Mgc), i("connect -> done", []), + progress("done"), ok. @@ -284,6 +330,7 @@ traffic(Config) when is_list(Config) -> put(verbosity, ?TEST_VERBOSITY), put(sname, "TEST"), i("traffic -> starting"), + progress("start nodes"), MgcNode = make_node_name(mgc), Mg1Node = make_node_name(mg1), Mg2Node = make_node_name(mg2), @@ -302,11 +349,13 @@ traffic(Config) when is_list(Config) -> %% Start the MGC and MGs i("traffic -> start the MGC"), + progress("start MGC"), ET = [{text,tcp}, {text,udp}, {binary,tcp}, {binary,udp}], {ok, Mgc} = start_mgc(MgcNode, {deviceName, "ctrl"}, ET, ?MGC_VERBOSITY), i("traffic -> start and connect the MGs"), + progress("start and connect MGs"), MgConf0 = [{Mg1Node, "mg1", text, tcp}, {Mg2Node, "mg2", text, udp}, {Mg3Node, "mg3", binary, tcp}, @@ -315,36 +364,42 @@ traffic(Config) when is_list(Config) -> %% Collect and check the MGs statistics i("traffic -> collect and check the MGs stats"), + progress("collect and verify MGs (initial) stats"), traffic_verify_mg_stats(MgConf, 1, 1), %% Collect and check the MGC statistics - i("traffic -> collect and check the MGC stats"), + i("traffic -> collect and check the MGC (initial) stats"), + progress("collect and verify MGC stats"), {ok, MgcStats1} = get_stats(Mgc, 1), d("traffic -> stats (1) for Mgc: ~n~p~n", [MgcStats1]), traffic_verify_mgc_stats(Mgc, 1, 1), - sleep(1000), + ?SLEEP(1000), %% And apply some load - i("traffic -> apply traffic load"), + i("traffic -> apply traffic load (1)"), + progress("apply some load (1)"), ok = traffic_apply_load(MgConf), %% Await completion of load part and the collect traffic - i("traffic -> await load competion"), + i("traffic -> await load (1) competion"), + progress("await load (1) completion"), ok = traffic_await_load_complete(MgConf), - sleep(1000), + ?SLEEP(1000), i("traffic -> collect and check the MGs statistics"), + progress("collect and verify MGs (after load 1) stats"), traffic_verify_mg_stats(MgConf, 1 + ?LOAD_COUNTER_START, 1 + ?LOAD_COUNTER_START), i("traffic -> collect and check the MGC statistics"), + progress("collect and verify MGC (after load 1) stats"), {ok, MgcStats3} = get_stats(Mgc, 1), d("traffic -> stats (1) for Mgc: ~n~p~n", [MgcStats3]), traffic_verify_mgc_stats(Mgc, @@ -352,60 +407,70 @@ traffic(Config) when is_list(Config) -> 1 + ?LOAD_COUNTER_START), - sleep(1000), + ?SLEEP(1000), %% Reset counters i("traffic -> reset the MGs statistics"), + progress("reset MGs stats"), traffic_reset_mg_stats(MgConf), i("traffic -> collect and check the MGs statistics"), + progress("collect and verify MGs (after reset) stats"), traffic_verify_mg_stats(MgConf, 0, 0), i("traffic -> reset the MGC statistics"), + progress("reset MGC stats"), traffic_reset_mgc_stats(Mgc), i("traffic -> collect and check the MGC statistics"), + progress("collect and verify MGC (after reset) stats"), traffic_verify_mgc_stats(Mgc, 0, 0), - sleep(1000), + ?SLEEP(1000), %% And apply some load - i("traffic -> apply traffic load"), + i("traffic -> apply traffic load (2)"), + progress("apply some load (2)"), ok = traffic_apply_load(MgConf), %% Await completion of load part and the collect traffic - i("traffic -> await load competion"), + i("traffic -> await load (2) competion"), + progress("await load (2) completion"), ok = traffic_await_load_complete(MgConf), - sleep(1000), + ?SLEEP(1000), i("traffic -> collect and check the MGs statistics"), + progress("collect and verify MGs (after load 2) stats"), traffic_verify_mg_stats(MgConf, ?LOAD_COUNTER_START, ?LOAD_COUNTER_START), i("traffic -> collect and check the MGC statistics"), + progress("collect and verify MGC (after load 2) stats"), traffic_verify_mgc_stats(Mgc, ?LOAD_COUNTER_START, ?LOAD_COUNTER_START), - sleep(1000), + ?SLEEP(1000), %% Tell MGs to stop i("traffic -> stop the MGs"), + progress("stop MGs"), traffic_stop_mg(MgConf), - sleep(1000), + ?SLEEP(1000), %% Collect the statistics i("traffic -> collect the MGC statistics"), + progress("collect and verify MGC (after MGs stop) stats"), {ok, MgcStats7} = get_stats(Mgc, 1), d("traffic -> stats (1) for Mgc: ~n~p~n", [MgcStats7]), {ok, MgcStats8} = get_stats(Mgc, 2), @@ -413,9 +478,11 @@ traffic(Config) when is_list(Config) -> %% Tell Mgc to stop i("traffic -> stop the MGC"), + progress("stop MGC"), stop(Mgc), i("traffic -> done", []), + progress("done"), ok. @@ -516,10 +583,15 @@ traffic_verify_get_stats(S, Stats) -> traffic_verify_counter(Name, Counter, Counters, Expected) -> case lists:keysearch(Counter, 1, Counters) of {value, {Counter, Expected}} -> + i("counter ~w verified for ~p", [Counter, Name]), ok; {value, {Counter, Val}} -> + i("counter ~w *not* verified for ~p: " + "~n Expected: ~w" + "~n Actual: ~w", [Counter, Name, Expected, Val]), exit({illegal_counter_value, Counter, Val, Expected, Name}); false -> + i("counter ~w *not* found for ~p", [Counter, Name]), exit({not_found, Counter, Counters, Name, Expected}) end. @@ -536,8 +608,7 @@ traffic_connect_mg(Node, Name, Coding, Trans) -> %% Ask the MGs to do a service change {ok, Res} = service_change(Pid), - d("traffic_connect_mg -> (~s) service change result: ~p", [Name,Res]), - + d("traffic_connect_mg -> (~s) service change result: ~p", [Name, Res]), Pid. @@ -549,7 +620,9 @@ traffic_get_mg_stats([], Acc) -> lists:reverse(Acc); traffic_get_mg_stats([{Name, Pid}|Mgs], Acc) -> {ok, Stats} = get_stats(Pid, 1), - d("traffic_get_mg_stats -> stats for ~s: ~n~p~n", [Name, Stats]), + d("traffic_get_mg_stats -> stats for ~s: " + "~n ~p" + "~n", [Name, Stats]), traffic_get_mg_stats(Mgs, [{Name, Stats}|Acc]). @@ -903,7 +976,7 @@ mgc_tcp_create_listen(Sup, Opts, MaxN, N, _InitialReason) ok -> Sup; {error, {could_not_start_listener, {gen_tcp_listen, eaddrinuse} = Reason}} -> - sleep(N * 200), + ?SLEEP(N * 200), mgc_tcp_create_listen(Sup, Opts, MaxN, N + 1, Reason); {error, Reason} -> throw({error, {failed_starting_tcp_listen, Reason}}); @@ -1044,7 +1117,6 @@ mg(Parent, Verbosity, Config) -> mg_init(Config) -> d("mg_init -> entry"), - random_init(), Mid = get_conf(local_mid, Config), RI = get_conf(receive_info, Config), d("mg_init -> start megaco"), @@ -1088,12 +1160,12 @@ mg_loop(#mg{state = State} = S) -> %% Give me statistics {statistics, 1, Parent} when S#mg.parent == Parent -> i("mg_loop(~p) -> got request for statistics 1", [State]), - {ok, Gen} = megaco:get_stats(), - CH = S#mg.conn_handle, - Reason = {statistics, CH}, - Pid = megaco:conn_info(CH, control_pid), - SendMod = megaco:conn_info(CH, send_mod), - SendHandle = megaco:conn_info(CH, send_handle), + {ok, Gen} = megaco:get_stats(), + CH = S#mg.conn_handle, + Reason = {statistics, CH}, + Pid = megaco:conn_info(CH, control_pid), + SendMod = megaco:conn_info(CH, send_mod), + SendHandle = megaco:conn_info(CH, send_handle), {ok, Trans} = case SendMod of megaco_tcp -> megaco_tcp:get_stats(SendHandle); @@ -1247,13 +1319,16 @@ mg_start_udp(MgcPort, RH) -> d("start udp transport"), case megaco_udp:start_transport() of {ok, Sup} -> - {ok, LocalHost} = inet:gethostname(), + %% Some linux (Ubuntu) has "crap" in their /etc/hosts, that + %% causes problem for us in this case (UDP). So we can't use + %% local host. Try instead to "figure out" tha actual address... + LocalAddr = which_local_addr(), Opts = [{port, 0}, {receive_handle, RH}], case megaco_udp:open(Sup, Opts) of {ok, Handle, ControlPid} -> MgcMid = preliminary_mid, SendHandle = megaco_udp:create_send_handle(Handle, - LocalHost, + LocalAddr, MgcPort), {ok, ConnHandle} = megaco:connect(RH, MgcMid, @@ -1528,10 +1603,6 @@ request(Pid, Request) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -sleep(X) -> - receive after X -> ok end. - - error_msg(F,A) -> error_logger:error_msg(F ++ "~n",A). @@ -1583,6 +1654,44 @@ get_conf(Key, Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +which_local_addr() -> + case inet:getifaddrs() of + {ok, IFs} -> + which_local_addr(IFs); + {error, Reason} -> + i("Failed get local address: " + "~n ~p", [Reason]), + ?SKIP({failed_get_local_addr, Reason}) + end. + +which_local_addr([]) -> + ?SKIP(failed_get_local_addr); +which_local_addr([{"lo" = _IfName, _IfOpts}|IFs]) -> + which_local_addr(IFs); +which_local_addr([{"br-" ++ _ = _IfName, _IfOpts}|IFs]) -> + which_local_addr(IFs); +which_local_addr([{"docker" ++ _ = _IfName, _IfOpts}|IFs]) -> + which_local_addr(IFs); +which_local_addr([{_IfName, IfOpts}|IFs]) -> + case which_local_addr2(IfOpts) of + {ok, Addr} -> + Addr; + error -> + which_local_addr(IFs) + end. + + +which_local_addr2([]) -> + error; +which_local_addr2([{addr, Addr}|_]) + when (size(Addr) =:= 4) andalso (element(1, Addr) =/= 127) -> + {ok, Addr}; +which_local_addr2([_|T]) -> + which_local_addr2(T). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + i(F) -> i(F, []). @@ -1605,19 +1714,22 @@ print(Severity, Verbosity, P, F, A) -> print(printable(Severity,Verbosity), P, F, A). print(true, P, F, A) -> - io:format("~s~p:~s: " ++ F ++ "~n", [P, self(), get(sname) | A]); + io:format("~s~p:~s:~s: " ++ F ++ "~n", [P, self(), get(sname), ?FTS() | A]); print(_, _, _, _) -> ok. -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +progress(F) -> + progress(F, []). + +progress(F, A) -> + io:format(user, "~s " ++ F ++ "~n", [?FTS()|A]). -random_init() -> - {A,B,C} = now(), - random:seed(A,B,C). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% random() -> - 10 * random:uniform(50). + 10 * rand:uniform(50). apply_load_timer() -> erlang:send_after(random(), self(), apply_load_timeout). diff --git a/lib/megaco/test/megaco_test_lib.erl b/lib/megaco/test/megaco_test_lib.erl index 3934a3a957..0617b96456 100644 --- a/lib/megaco/test/megaco_test_lib.erl +++ b/lib/megaco/test/megaco_test_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2016. All Rights Reserved. +%% Copyright Ericsson AB 1999-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -26,7 +26,47 @@ -module(megaco_test_lib). --compile(export_all). +%% -compile(export_all). + +-export([ + log/4, + error/3, + + sleep/1, + hours/1, minutes/1, seconds/1, + formated_timestamp/0, format_timestamp/1, + + skip/3, + non_pc_tc_maybe_skip/4, + os_based_skip/1, + + flush/0, + still_alive/1, + watchdog/2, + + display_alloc_info/0, + display_system_info/1, display_system_info/2, display_system_info/3, + + tickets/1, + prepare_test_case/5, + + t/1, + groups/1, + init_suite/2, + end_suite/2, + init_group/3, + end_group/3, + t/2, + init_per_testcase/2, + end_per_testcase/2, + + proxy_start/1, proxy_start/2, + + mk_nodes/1, + start_nodes/3 + ]). + +-export([do_eval/4, proxy_init/2]). -include("megaco_test_lib.hrl"). @@ -53,6 +93,13 @@ minutes(N) -> trunc(N * 1000 * 60). seconds(N) -> trunc(N * 1000). +formated_timestamp() -> + format_timestamp(os:timestamp()). + +format_timestamp(TS) -> + megaco:format_timestamp(TS). + + %% ---------------------------------------------------------------- %% Conditional skip of testcases %% @@ -367,7 +414,7 @@ eval(Mod, Fun, Config) -> Flag = process_flag(trap_exit, true), put(megaco_test_server, true), Config2 = Mod:init_per_testcase(Fun, Config), - Pid = spawn_link(?MODULE, do_eval, [self(), Mod, Fun, Config2]), + Pid = spawn_link(fun() -> do_eval(self(), Mod, Fun, Config2) end), R = wait_for_evaluator(Pid, Mod, Fun, Config2, []), Mod:end_per_testcase(Fun, Config2), erase(megaco_test_server), @@ -719,6 +766,7 @@ still_alive(Pid) -> end end. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% The proxy process @@ -730,32 +778,51 @@ proxy_start(Node, ProxyId) -> proxy_init(ProxyId, Controller) -> process_flag(trap_exit, true), - ?LOG("[~p] proxy started by ~p~n",[ProxyId, Controller]), + IdStr = proxyid2string(ProxyId), + put(id, IdStr), + ?LOG("[~s] proxy started by ~p~n", [IdStr, Controller]), proxy_loop(ProxyId, Controller). proxy_loop(OwnId, Controller) -> receive {'EXIT', Controller, Reason} -> - p("proxy_loop -> received exit from controller" + pprint("proxy_loop -> received exit from controller" + "~n Reason: ~p", [Reason]), + exit(Reason); + {stop, Controller, Reason} -> + p("proxy_loop -> received stop from controller" "~n Reason: ~p" "~n", [Reason]), exit(Reason); + {apply, Fun} -> - p("proxy_loop -> received apply request~n", []), + pprint("proxy_loop -> received apply request"), Res = Fun(), - p("proxy_loop -> apply result: " - "~n ~p" - "~n", [Res]), + pprint("proxy_loop -> apply result: " + "~n ~p", [Res]), Controller ! {res, OwnId, Res}, proxy_loop(OwnId, Controller); OtherMsg -> - p("proxy_loop -> received unknown message: " - "~n OtherMsg: ~p" - "~n", [OtherMsg]), + pprint("proxy_loop -> received unknown message: " + "~n ~p", [OtherMsg]), Controller ! {msg, OwnId, OtherMsg}, proxy_loop(OwnId, Controller) end. +proxyid2string(Id) when is_list(Id) -> + Id; +proxyid2string(Id) when is_atom(Id) -> + atom_to_list(Id); +proxyid2string(Id) -> + f("~p", [Id]). + +pprint(F) -> + pprint(F, []). + +pprint(F, A) -> + io:format("[~s] ~p ~s " ++ F ++ "~n", + [get(id), self(), formated_timestamp() | A]). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Test server callbacks @@ -837,7 +904,7 @@ reset_kill_timer(Config) -> end. watchdog(Pid, Time) -> - erlang:now(), + _ = os:timestamp(), receive stop -> ok @@ -897,7 +964,10 @@ default_config() -> [{nodes, default_nodes()}, {ts, megaco}]. default_nodes() -> - mk_nodes(2, []). + mk_nodes(3, []). + +mk_nodes(N) when (N > 0) -> + mk_nodes(N, []). mk_nodes(0, Nodes) -> Nodes; @@ -937,5 +1007,11 @@ start_nodes([Node | Nodes], File, Line) -> start_nodes([], _File, _Line) -> ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +f(F, A) -> + lists:flatten(io_lib:format(F, A)). + p(F, A) -> io:format("~p~w:" ++ F ++ "~n", [self(), ?MODULE |A]). diff --git a/lib/megaco/test/megaco_test_lib.hrl b/lib/megaco/test/megaco_test_lib.hrl index 79a1493c40..409f8d52e5 100644 --- a/lib/megaco/test/megaco_test_lib.hrl +++ b/lib/megaco/test/megaco_test_lib.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2016. All Rights Reserved. +%% Copyright Ericsson AB 1999-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -32,6 +32,8 @@ -define(ERROR(Reason), megaco_test_lib:error(Reason, ?MODULE, ?LINE)). +-define(F(FMT, ARGS), lists:flatten(io_lib:format(FMT, ARGS))). + -define(OS_BASED_SKIP(Skippable), megaco_test_lib:os_based_skip(Skippable)). @@ -80,9 +82,8 @@ -define(SLEEP(MSEC), megaco_test_lib:sleep(MSEC)). --define(M(), megaco_test_lib:millis()). --define(MDIFF(A,B), megaco_test_lib:millis_diff(A,B)). - -define(HOURS(T), megaco_test_lib:hours(T)). -define(MINUTES(T), megaco_test_lib:minutes(T)). -define(SECONDS(T), megaco_test_lib:seconds(T)). +-define(FTS(), megaco:format_timestamp(erlang:timestamp())). + diff --git a/lib/megaco/test/megaco_timer_test.erl b/lib/megaco/test/megaco_timer_test.erl index 34479f7838..d3e8e27636 100644 --- a/lib/megaco/test/megaco_timer_test.erl +++ b/lib/megaco/test/megaco_timer_test.erl @@ -332,7 +332,7 @@ integer_timer_start_and_expire(Config) when is_list(Config) -> receive {timeout, Timeout} -> ok - after Timeout + 100 -> + after Timeout + 500 -> tmr_stop(Ref), error(no_timeout) end, @@ -359,7 +359,12 @@ integer_timer_start_and_stop(Config) when is_list(Config) -> {timeout, Timeout} -> error(bad_timeout) after Timeout - 100 -> - tmr_stop(Ref) + case tmr_stop(Ref) of + ok -> + ok; + CancelRes -> + ?SKIP({cancel_failed, CancelRes}) + end end, %% Make sure it does not reach us after we attempted to stop it. @@ -438,21 +443,6 @@ print1(_, _, _, _) -> print(Prefix, F, A) -> io:format("*** [~s] ~s ~p ~s:~w ***" "~n " ++ F ++ "~n", - [formated_timestamp(), Prefix, self(), get(sname), get(tc) | A]). + [?FTS(), Prefix, self(), get(sname), get(tc) | A]). - - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -formated_timestamp() -> - format_timestamp(now()). - -format_timestamp({_N1, _N2, N3} = Now) -> - {Date, Time} = calendar:now_to_datetime(Now), - {YYYY,MM,DD} = Date, - {Hour,Min,Sec} = Time, - FormatDate = - io_lib:format("~.4w:~.2.0w:~.2.0w ~.2.0w:~.2.0w:~.2.0w 4~w", - [YYYY,MM,DD,Hour,Min,Sec,round(N3/1000)]), - lists:flatten(FormatDate). diff --git a/lib/megaco/test/megaco_trans_test.erl b/lib/megaco/test/megaco_trans_test.erl index 9786307860..fb44a3c6e6 100644 --- a/lib/megaco/test/megaco_trans_test.erl +++ b/lib/megaco/test/megaco_trans_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2016. All Rights Reserved. +%% Copyright Ericsson AB 2003-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -27,7 +27,46 @@ %%---------------------------------------------------------------------- -module(megaco_trans_test). --compile(export_all). +%% -compile(export_all). +-export([ + all/0, + groups/0, + init_per_group/2, end_per_group/2, + init_per_testcase/2, end_per_testcase/2, + + single_ack/1, + multi_ack_timeout/1, + multi_ack_maxcount/1, + + single_trans_req/1, + multi_trans_req_timeout/1, + multi_trans_req_maxcount1/1, + multi_trans_req_maxcount2/1, + multi_trans_req_maxsize1/1, + multi_trans_req_maxsize2/1, + + single_trans_req_and_ack/1, + multi_trans_req_and_ack_timeout/1, + multi_trans_req_and_ack_ackmaxcount/1, + multi_trans_req_and_ack_reqmaxcount/1, + multi_trans_req_and_ack_maxsize1/1, + multi_trans_req_and_ack_maxsize2/1, + + single_trans_req_and_pending/1, + multi_trans_req_and_pending/1, + multi_trans_req_and_ack_and_pending/1, + multi_ack_and_pending/1, + + multi_trans_req_and_reply/1, + multi_trans_req_and_ack_and_reply/1, + multi_ack_and_reply/1, + + otp_7192_1/1, + otp_7192_2/1, + otp_7192_3/1, + + t/0, t/1 + ]). -include("megaco_test_lib.hrl"). -include_lib("megaco/include/megaco.hrl"). @@ -44,44 +83,42 @@ -define(A5555, ["11111111", "11111111", "00000000"]). -define(A5556, ["11111111", "11111111", "11111111"]). --define(MGC_START(Pid, Mid, ET, Verb), - megaco_test_mgc:start(Pid, Mid, ET, Verb)). --define(MGC_STOP(Pid), megaco_test_mgc:stop(Pid)). --define(MGC_GET_STATS(Pid, No), megaco_test_mgc:get_stats(Pid, No)). --define(MGC_RESET_STATS(Pid), megaco_test_mgc:reset_stats(Pid)). --define(MGC_REQ_DISC(Pid,To), megaco_test_mgc:request_discard(Pid,To)). --define(MGC_REQ_PEND(Pid,To), megaco_test_mgc:request_pending(Pid,To)). --define(MGC_REQ_HAND(Pid), megaco_test_mgc:request_handle(Pid)). --define(MGC_REQ_HANDS(Pid), megaco_test_mgc:request_handle_sloppy(Pid)). --define(MGC_UPDATE_UI(Pid,Tag,Val), - megaco_test_mgc:update_user_info(Pid,Tag,Val)). --define(MGC_UPDATE_CI(Pid,Tag,Val), - megaco_test_mgc:update_conn_info(Pid,Tag,Val)). --define(MGC_USER_INFO(Pid,Tag), megaco_test_mgc:user_info(Pid,Tag)). --define(MGC_CONN_INFO(Pid,Tag), megaco_test_mgc:conn_info(Pid,Tag)). --define(MGC_ACK_INFO(Pid,To), megaco_test_mgc:ack_info(Pid,To)). --define(MGC_REQ_INFO(Pid,To), megaco_test_mgc:req_info(Pid,To)). +-define(MG, megaco_test_mg). +-define(MGC, megaco_test_mgc). + +-define(MGC_START(Pid, Mid, ET, Verb), ?MGC:start(Pid, Mid, ET, Verb)). +-define(MGC_STOP(Pid), ?MGC:stop(Pid)). +-define(MGC_GET_STATS(Pid, No), ?MGC:get_stats(Pid, No)). +-define(MGC_RESET_STATS(Pid), ?MGC:reset_stats(Pid)). +-define(MGC_REQ_DISC(Pid,To), ?MGC:request_discard(Pid,To)). +-define(MGC_REQ_PEND(Pid,To), ?MGC:request_pending(Pid,To)). +-define(MGC_REQ_HAND(Pid), ?MGC:request_handle(Pid)). +-define(MGC_REQ_HANDS(Pid), ?MGC:request_handle_sloppy(Pid)). +-define(MGC_UPDATE_UI(Pid,Tag,Val), ?MGC:update_user_info(Pid,Tag,Val)). +-define(MGC_UPDATE_CI(Pid,Tag,Val), ?MGC:update_conn_info(Pid,Tag,Val)). +-define(MGC_USER_INFO(Pid,Tag), ?MGC:user_info(Pid,Tag)). +-define(MGC_CONN_INFO(Pid,Tag), ?MGC:conn_info(Pid,Tag)). +-define(MGC_ACK_INFO(Pid,To), ?MGC:ack_info(Pid,To)). +-define(MGC_REQ_INFO(Pid,To), ?MGC:req_info(Pid,To)). -define(MG_START(Pid, Mid, Enc, Transp, Conf, Verb), - megaco_test_mg:start(Pid, Mid, Enc, Transp, Conf, Verb)). --define(MG_STOP(Pid), megaco_test_mg:stop(Pid)). --define(MG_GET_STATS(Pid), megaco_test_mg:get_stats(Pid)). --define(MG_RESET_STATS(Pid), megaco_test_mg:reset_stats(Pid)). --define(MG_SERV_CHANGE(Pid), megaco_test_mg:service_change(Pid)). --define(MG_NOTIF_RAR(Pid), megaco_test_mg:notify_request_and_reply(Pid)). --define(MG_NOTIF_REQ(Pid), megaco_test_mg:notify_request(Pid)). --define(MG_NOTIF_AR(Pid), megaco_test_mg:await_notify_reply(Pid)). --define(MG_CANCEL(Pid,R), megaco_test_mg:cancel_request(Pid,R)). --define(MG_APPLY_LOAD(Pid,CntStart), megaco_test_mg:apply_load(Pid,CntStart)). --define(MG_UPDATE_UI(Pid,Tag,Val), - megaco_test_mg:update_user_info(Pid,Tag,Val)). --define(MG_UPDATE_CI(Pid,Tag,Val), - megaco_test_mg:update_conn_info(Pid,Tag,Val)). --define(MG_USER_INFO(Pid,Tag), megaco_test_mg:user_info(Pid,Tag)). --define(MG_CONN_INFO(Pid,Tag), megaco_test_mg:conn_info(Pid,Tag)). --define(MG_GRP_REQ(Pid,N), megaco_test_mg:group_requests(Pid,N)). --define(MG_ACK_INFO(Pid,To), megaco_test_mg:ack_info(Pid,To)). --define(MG_REP_INFO(Pid,To), megaco_test_mg:rep_info(Pid,To)). + ?MG:start(Pid, Mid, Enc, Transp, Conf, Verb)). +-define(MG_STOP(Pid), ?MG:stop(Pid)). +-define(MG_GET_STATS(Pid), ?MG:get_stats(Pid)). +-define(MG_RESET_STATS(Pid), ?MG:reset_stats(Pid)). +-define(MG_SERV_CHANGE(Pid), ?MG:service_change(Pid)). +-define(MG_NOTIF_RAR(Pid), ?MG:notify_request_and_reply(Pid)). +-define(MG_NOTIF_REQ(Pid), ?MG:notify_request(Pid)). +-define(MG_NOTIF_AR(Pid), ?MG:await_notify_reply(Pid)). +-define(MG_CANCEL(Pid,R), ?MG:cancel_request(Pid,R)). +-define(MG_APPLY_LOAD(Pid,CntStart), ?MG:apply_load(Pid,CntStart)). +-define(MG_UPDATE_UI(Pid,Tag,Val), ?MG:update_user_info(Pid,Tag,Val)). +-define(MG_UPDATE_CI(Pid,Tag,Val), ?MG:update_conn_info(Pid,Tag,Val)). +-define(MG_USER_INFO(Pid,Tag), ?MG:user_info(Pid,Tag)). +-define(MG_CONN_INFO(Pid,Tag), ?MG:conn_info(Pid,Tag)). +-define(MG_GRP_REQ(Pid,N), ?MG:group_requests(Pid,N)). +-define(MG_ACK_INFO(Pid,To), ?MG:ack_info(Pid,To)). +-define(MG_REP_INFO(Pid,To), ?MG:rep_info(Pid,To)). t() -> megaco_test_lib:t(?MODULE). t(Case) -> megaco_test_lib:t({?MODULE, Case}). @@ -104,35 +141,77 @@ end_per_testcase(Case, Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% all() -> - [{group, ack}, {group, trans_req}, - {group, trans_req_and_ack}, {group, pending}, - {group, reply}, {group, tickets}]. + [{group, ack}, + {group, trans_req}, + {group, trans_req_and_ack}, + {group, pending}, + {group, reply}, + {group, tickets}]. groups() -> - [{ack, [], - [single_ack, multi_ack_timeout, multi_ack_maxcount]}, - {trans_req, [], - [single_trans_req, multi_trans_req_timeout, - multi_trans_req_maxcount1, multi_trans_req_maxcount2, - multi_trans_req_maxsize1, multi_trans_req_maxsize2]}, - {trans_req_and_ack, [], - [single_trans_req_and_ack, - multi_trans_req_and_ack_timeout, - multi_trans_req_and_ack_ackmaxcount, - multi_trans_req_and_ack_reqmaxcount, - multi_trans_req_and_ack_maxsize1, - multi_trans_req_and_ack_maxsize2]}, - {pending, [], - [single_trans_req_and_pending, - multi_trans_req_and_pending, - multi_trans_req_and_ack_and_pending, - multi_ack_and_pending]}, - {reply, [], - [multi_trans_req_and_reply, - multi_trans_req_and_ack_and_reply, - multi_ack_and_reply]}, - {tickets, [], [{group, otp_7192}]}, - {otp_7192, [], [otp_7192_1, otp_7192_2, otp_7192_3]}]. + [ + {ack, [], ack_cases()}, + {trans_req, [], trans_req_cases()}, + {trans_req_and_ack, [], trans_req_and_ack_cases()}, + {pending, [], pending_cases()}, + {reply, [], reply_cases()}, + {tickets, [], tickets_cases()}, + {otp_7192, [], otp_7192_cases()} + ]. + +ack_cases() -> + [ + single_ack, + multi_ack_timeout, + multi_ack_maxcount + ]. + +trans_req_cases() -> + [ + single_trans_req, + multi_trans_req_timeout, + multi_trans_req_maxcount1, + multi_trans_req_maxcount2, + multi_trans_req_maxsize1, + multi_trans_req_maxsize2 + ]. + +trans_req_and_ack_cases() -> + [ + single_trans_req_and_ack, + multi_trans_req_and_ack_timeout, + multi_trans_req_and_ack_ackmaxcount, + multi_trans_req_and_ack_reqmaxcount, + multi_trans_req_and_ack_maxsize1, + multi_trans_req_and_ack_maxsize2 + ]. + +pending_cases() -> + [ + single_trans_req_and_pending, + multi_trans_req_and_pending, + multi_trans_req_and_ack_and_pending, + multi_ack_and_pending + ]. + +reply_cases() -> + [ + multi_trans_req_and_reply, + multi_trans_req_and_ack_and_reply, + multi_ack_and_reply + ]. + +tickets_cases() -> + [ + {group, otp_7192} + ]. + +otp_7192_cases() -> + [ + otp_7192_1, + otp_7192_2, + otp_7192_3 + ]. init_per_group(_GroupName, Config) -> Config. @@ -156,8 +235,8 @@ single_ack(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MGC Node: ~p" + "~n MG Node: ~p", [MgcNode, MgNode]), ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), @@ -231,8 +310,8 @@ multi_ack_timeout(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MGC Node: ~p" + "~n MG Node: ~p", [MgcNode, MgNode]), ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), @@ -308,8 +387,8 @@ multi_ack_maxcount(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MGC Node: ~p" + "~n MG Node: ~p", [MgcNode, MgNode]), ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), @@ -393,8 +472,8 @@ single_trans_req(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MGC Node: ~p" + "~n MG Node: ~p", [MgcNode, MgNode]), ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), @@ -628,26 +707,26 @@ str_mgc_service_change_reply_ar(Mid, Cid) -> CR = cre_cmdReply(SCR), cre_actionReply(Cid, [CR]). -str_mgc_service_change_reply_msg(Mid, TransId, Cid) -> - AR = str_mgc_service_change_reply_ar(Mid, Cid), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% str_mgc_service_change_reply_msg(Mid, TransId, Cid) -> +%% AR = str_mgc_service_change_reply_ar(Mid, Cid), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). str_mgc_notify_reply_ar(Cid, TermId) -> NR = cre_notifyReply([TermId]), CR = cre_cmdReply(NR), cre_actionReply(Cid, [CR]). -str_mgc_notify_reply(Mid, TransId, Cid, TermId) -> - AR = str_mgc_notify_reply_ar(Cid, TermId), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% str_mgc_notify_reply(Mid, TransId, Cid, TermId) -> +%% AR = str_mgc_notify_reply_ar(Cid, TermId), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -778,12 +857,12 @@ str_mg_service_change_request_ar(_Mid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -str_mg_service_change_request_msg(Mid, TransId, Cid) -> - AR = str_mg_service_change_request_ar(Mid, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% str_mg_service_change_request_msg(Mid, TransId, Cid) -> +%% AR = str_mg_service_change_request_ar(Mid, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). str_mg_notify_request_ar(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "22000000"), @@ -794,12 +873,12 @@ str_mg_notify_request_ar(Rid, Tid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -str_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> - AR = str_mg_notify_request_ar(Rid, TermId, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% str_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> +%% AR = str_mg_notify_request_ar(Rid, TermId, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -827,8 +906,8 @@ multi_trans_req_timeout(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MGC Node: ~p" + "~n MG Node: ~p", [MgcNode, MgNode]), ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), @@ -1063,26 +1142,26 @@ mtrt_mgc_service_change_reply_ar(Mid, Cid) -> CR = cre_cmdReply(SCR), cre_actionReply(Cid, [CR]). -mtrt_mgc_service_change_reply_msg(Mid, TransId, Cid) -> - AR = mtrt_mgc_service_change_reply_ar(Mid, Cid), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrt_mgc_service_change_reply_msg(Mid, TransId, Cid) -> +%% AR = mtrt_mgc_service_change_reply_ar(Mid, Cid), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtrt_mgc_notify_reply_ar(Cid, TermId) -> NR = cre_notifyReply([TermId]), CR = cre_cmdReply(NR), cre_actionReply(Cid, [CR]). -mtrt_mgc_notify_reply(Mid, TransId, Cid, TermId) -> - AR = mtrt_mgc_notify_reply_ar(Cid, TermId), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrt_mgc_notify_reply(Mid, TransId, Cid, TermId) -> +%% AR = mtrt_mgc_notify_reply_ar(Cid, TermId), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -1224,12 +1303,12 @@ mtrt_mg_service_change_request_ar(_Mid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -mtrt_mg_service_change_request_msg(Mid, TransId, Cid) -> - AR = mtrt_mg_service_change_request_ar(Mid, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrt_mg_service_change_request_msg(Mid, TransId, Cid) -> +%% AR = mtrt_mg_service_change_request_ar(Mid, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtrt_mg_notify_request_ar(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "22000000"), @@ -1240,12 +1319,12 @@ mtrt_mg_notify_request_ar(Rid, Tid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -mtrt_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> - AR = mtrt_mg_notify_request_ar(Rid, TermId, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrt_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> +%% AR = mtrt_mg_notify_request_ar(Rid, TermId, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -1273,8 +1352,8 @@ multi_trans_req_maxcount1(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MGC Node: ~p" + "~n MG Node: ~p", [MgcNode, MgNode]), ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), @@ -1509,26 +1588,26 @@ mtrmc1_mgc_service_change_reply_ar(Mid, Cid) -> CR = cre_cmdReply(SCR), cre_actionReply(Cid, [CR]). -mtrmc1_mgc_service_change_reply_msg(Mid, TransId, Cid) -> - AR = mtrmc1_mgc_service_change_reply_ar(Mid, Cid), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrmc1_mgc_service_change_reply_msg(Mid, TransId, Cid) -> +%% AR = mtrmc1_mgc_service_change_reply_ar(Mid, Cid), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtrmc1_mgc_notify_reply_ar(Cid, TermId) -> NR = cre_notifyReply([TermId]), CR = cre_cmdReply(NR), cre_actionReply(Cid, [CR]). -mtrmc1_mgc_notify_reply(Mid, TransId, Cid, TermId) -> - AR = mtrmc1_mgc_notify_reply_ar(Cid, TermId), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrmc1_mgc_notify_reply(Mid, TransId, Cid, TermId) -> +%% AR = mtrmc1_mgc_notify_reply_ar(Cid, TermId), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -1675,12 +1754,12 @@ mtrmc1_mg_service_change_request_ar(_Mid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -mtrmc1_mg_service_change_request_msg(Mid, TransId, Cid) -> - AR = mtrmc1_mg_service_change_request_ar(Mid, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrmc1_mg_service_change_request_msg(Mid, TransId, Cid) -> +%% AR = mtrmc1_mg_service_change_request_ar(Mid, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtrmc1_mg_notify_request_ar(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "22000000"), @@ -1691,12 +1770,12 @@ mtrmc1_mg_notify_request_ar(Rid, Tid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -mtrmc1_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> - AR = mtrmc1_mg_notify_request_ar(Rid, TermId, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrmc1_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> +%% AR = mtrmc1_mg_notify_request_ar(Rid, TermId, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -1725,8 +1804,8 @@ multi_trans_req_maxcount2(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MGC Node: ~p" + "~n MG Node: ~p", [MgcNode, MgNode]), ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), @@ -1978,13 +2057,13 @@ mtrmc2_mgc_service_change_reply_ar(Mid, Cid) -> CR = cre_cmdReply(SCR), cre_actionReply(Cid, [CR]). -mtrmc2_mgc_service_change_reply_msg(Mid, TransId, Cid) -> - AR = mtrmc2_mgc_service_change_reply_ar(Mid, Cid), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrmc2_mgc_service_change_reply_msg(Mid, TransId, Cid) -> +%% AR = mtrmc2_mgc_service_change_reply_ar(Mid, Cid), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtrmc2_mgc_notify_reply_ar1(Cid, Tid) -> NR = cre_notifyReply([Tid]), @@ -1995,13 +2074,13 @@ mtrmc2_mgc_notify_reply_ar2(Cid, Tids) -> CRs = [cre_cmdReply(cre_notifyReply([Tid])) || Tid <- Tids], cre_actionReply(Cid, CRs). -mtrmc2_mgc_notify_reply(Mid, TransId, Cid, TermId) -> - AR = mtrmc2_mgc_notify_reply_ar1(Cid, TermId), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrmc2_mgc_notify_reply(Mid, TransId, Cid, TermId) -> +%% AR = mtrmc2_mgc_notify_reply_ar1(Cid, TermId), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -2163,12 +2242,12 @@ mtrmc2_mg_service_change_request_ar(_Mid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -mtrmc2_mg_service_change_request_msg(Mid, TransId, Cid) -> - AR = mtrmc2_mg_service_change_request_ar(Mid, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrmc2_mg_service_change_request_msg(Mid, TransId, Cid) -> +%% AR = mtrmc2_mg_service_change_request_ar(Mid, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtrmc2_mg_notify_request_ar1(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "22000000"), @@ -2194,12 +2273,12 @@ mtrmc2_mg_notify_request_ar2(Rid, Tid, Cid) -> CRs = [F(N) || N <- Ns], cre_actionReq(Cid, CRs). -mtrmc2_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> - AR = mtrmc2_mg_notify_request_ar1(Rid, TermId, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrmc2_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> +%% AR = mtrmc2_mg_notify_request_ar1(Rid, TermId, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -2229,8 +2308,8 @@ multi_trans_req_maxsize1(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MGC Node: ~p" + "~n MG Node: ~p", [MgcNode, MgNode]), ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), @@ -2466,26 +2545,26 @@ mtrms1_mgc_service_change_reply_ar(Mid, Cid) -> CR = cre_cmdReply(SCR), cre_actionReply(Cid, [CR]). -mtrms1_mgc_service_change_reply_msg(Mid, TransId, Cid) -> - AR = mtrms1_mgc_service_change_reply_ar(Mid, Cid), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrms1_mgc_service_change_reply_msg(Mid, TransId, Cid) -> +%% AR = mtrms1_mgc_service_change_reply_ar(Mid, Cid), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtrms1_mgc_notify_reply_ar1(Cid, Tid) -> NR = cre_notifyReply([Tid]), CR = cre_cmdReply(NR), cre_actionReply(Cid, [CR]). -mtrms1_mgc_notify_reply(Mid, TransId, Cid, TermId) -> - AR = mtrms1_mgc_notify_reply_ar1(Cid, TermId), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrms1_mgc_notify_reply(Mid, TransId, Cid, TermId) -> +%% AR = mtrms1_mgc_notify_reply_ar1(Cid, TermId), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -2633,12 +2712,12 @@ mtrms1_mg_service_change_request_ar(_Mid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -mtrms1_mg_service_change_request_msg(Mid, TransId, Cid) -> - AR = mtrms1_mg_service_change_request_ar(Mid, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrms1_mg_service_change_request_msg(Mid, TransId, Cid) -> +%% AR = mtrms1_mg_service_change_request_ar(Mid, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtrms1_mg_notify_request_ar1(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "22000000"), @@ -2649,18 +2728,16 @@ mtrms1_mg_notify_request_ar1(Rid, Tid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -mtrms1_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> - AR = mtrms1_mg_notify_request_ar1(Rid, TermId, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrms1_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> +%% AR = mtrms1_mg_notify_request_ar1(Rid, TermId, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtrms1_err_desc(T) -> - EC = ?megaco_internal_gateway_error, - ET = lists:flatten(io_lib:format("~w",[T])), - #'ErrorDescriptor'{errorCode = EC, errorText = ET}. + cre_ErrDesc(T). @@ -2681,8 +2758,8 @@ multi_trans_req_maxsize2(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MGC Node: ~p" + "~n MG Node: ~p", [MgcNode, MgNode]), ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), @@ -2934,13 +3011,13 @@ mtrms2_mgc_service_change_reply_ar(Mid, Cid) -> CR = cre_cmdReply(SCR), cre_actionReply(Cid, [CR]). -mtrms2_mgc_service_change_reply_msg(Mid, TransId, Cid) -> - AR = mtrms2_mgc_service_change_reply_ar(Mid, Cid), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrms2_mgc_service_change_reply_msg(Mid, TransId, Cid) -> +%% AR = mtrms2_mgc_service_change_reply_ar(Mid, Cid), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtrms2_mgc_notify_reply_ar1(Cid, Tid) -> NR = cre_notifyReply([Tid]), @@ -2951,13 +3028,13 @@ mtrms2_mgc_notify_reply_ar2(Cid, Tids) -> CRs = [cre_cmdReply(cre_notifyReply([Tid])) || Tid <- Tids], cre_actionReply(Cid, CRs). -mtrms2_mgc_notify_reply(Mid, TransId, Cid, TermId) -> - AR = mtrms2_mgc_notify_reply_ar1(Cid, TermId), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrms2_mgc_notify_reply(Mid, TransId, Cid, TermId) -> +%% AR = mtrms2_mgc_notify_reply_ar1(Cid, TermId), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -3115,12 +3192,12 @@ mtrms2_mg_service_change_request_ar(_Mid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -mtrms2_mg_service_change_request_msg(Mid, TransId, Cid) -> - AR = mtrms2_mg_service_change_request_ar(Mid, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrms2_mg_service_change_request_msg(Mid, TransId, Cid) -> +%% AR = mtrms2_mg_service_change_request_ar(Mid, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtrms2_mg_notify_request_ar1(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "22000000"), @@ -3146,18 +3223,16 @@ mtrms2_mg_notify_request_ar2(Rid, Tid, Cid) -> CRs = [F(N) || N <- Ns], cre_actionReq(Cid, CRs). -mtrms2_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> - AR = mtrms2_mg_notify_request_ar1(Rid, TermId, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrms2_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> +%% AR = mtrms2_mg_notify_request_ar1(Rid, TermId, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtrms2_err_desc(T) -> - EC = ?megaco_internal_gateway_error, - ET = lists:flatten(io_lib:format("~w",[T])), - #'ErrorDescriptor'{errorCode = EC, errorText = ET}. + cre_ErrDesc(T). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -3175,8 +3250,8 @@ single_trans_req_and_ack(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MGC Node: ~p" + "~n MG Node: ~p", [MgcNode, MgNode]), ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), @@ -3439,26 +3514,26 @@ straa_mgc_service_change_reply_ar(Mid, Cid) -> CR = cre_cmdReply(SCR), cre_actionReply(Cid, [CR]). -straa_mgc_service_change_reply_msg(Mid, TransId, Cid) -> - AR = straa_mgc_service_change_reply_ar(Mid, Cid), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% straa_mgc_service_change_reply_msg(Mid, TransId, Cid) -> +%% AR = straa_mgc_service_change_reply_ar(Mid, Cid), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). straa_mgc_notify_reply_ar(Cid, TermId) -> NR = cre_notifyReply([TermId]), CR = cre_cmdReply(NR), cre_actionReply(Cid, [CR]). -straa_mgc_notify_reply(Mid, TransId, Cid, TermId) -> - AR = straa_mgc_notify_reply_ar(Cid, TermId), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% straa_mgc_notify_reply(Mid, TransId, Cid, TermId) -> +%% AR = straa_mgc_notify_reply_ar(Cid, TermId), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -3605,12 +3680,12 @@ straa_mg_service_change_request_ar(_Mid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -straa_mg_service_change_request_msg(Mid, TransId, Cid) -> - AR = straa_mg_service_change_request_ar(Mid, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% straa_mg_service_change_request_msg(Mid, TransId, Cid) -> +%% AR = straa_mg_service_change_request_ar(Mid, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). straa_mg_notify_request_ar(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "22000000"), @@ -3621,12 +3696,12 @@ straa_mg_notify_request_ar(Rid, Tid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -straa_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> - AR = straa_mg_notify_request_ar(Rid, TermId, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% straa_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> +%% AR = straa_mg_notify_request_ar(Rid, TermId, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -3634,9 +3709,7 @@ straa_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> %% straa_err_desc(T) -> - EC = ?megaco_internal_gateway_error, - ET = lists:flatten(io_lib:format("~w",[T])), - #'ErrorDescriptor'{errorCode = EC, errorText = ET}. + cre_ErrDesc(T). @@ -3656,8 +3729,8 @@ multi_trans_req_and_ack_timeout(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MGC Node: ~p" + "~n MG Node: ~p", [MgcNode, MgNode]), ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), @@ -3926,26 +3999,26 @@ mtrtaat_mgc_service_change_reply_ar(Mid, Cid) -> CR = cre_cmdReply(SCR), cre_actionReply(Cid, [CR]). -mtrtaat_mgc_service_change_reply_msg(Mid, TransId, Cid) -> - AR = mtrtaat_mgc_service_change_reply_ar(Mid, Cid), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrtaat_mgc_service_change_reply_msg(Mid, TransId, Cid) -> +%% AR = mtrtaat_mgc_service_change_reply_ar(Mid, Cid), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtrtaat_mgc_notify_reply_ar(Cid, TermId) -> NR = cre_notifyReply([TermId]), CR = cre_cmdReply(NR), cre_actionReply(Cid, [CR]). -mtrtaat_mgc_notify_reply(Mid, TransId, Cid, TermId) -> - AR = mtrtaat_mgc_notify_reply_ar(Cid, TermId), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrtaat_mgc_notify_reply(Mid, TransId, Cid, TermId) -> +%% AR = mtrtaat_mgc_notify_reply_ar(Cid, TermId), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -4098,12 +4171,12 @@ mtrtaat_mg_service_change_request_ar(_Mid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -mtrtaat_mg_service_change_request_msg(Mid, TransId, Cid) -> - AR = mtrtaat_mg_service_change_request_ar(Mid, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrtaat_mg_service_change_request_msg(Mid, TransId, Cid) -> +%% AR = mtrtaat_mg_service_change_request_ar(Mid, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtrtaat_mg_notify_request_ar(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "22000000"), @@ -4114,12 +4187,12 @@ mtrtaat_mg_notify_request_ar(Rid, Tid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -mtrtaat_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> - AR = mtrtaat_mg_notify_request_ar(Rid, TermId, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrtaat_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> +%% AR = mtrtaat_mg_notify_request_ar(Rid, TermId, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -4127,9 +4200,7 @@ mtrtaat_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> %% mtrtaat_err_desc(T) -> - EC = ?megaco_internal_gateway_error, - ET = lists:flatten(io_lib:format("~w",[T])), - #'ErrorDescriptor'{errorCode = EC, errorText = ET}. + cre_ErrDesc(T). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -4147,8 +4218,8 @@ multi_trans_req_and_ack_ackmaxcount(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MGC Node: ~p" + "~n MG Node: ~p", [MgcNode, MgNode]), ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), @@ -4422,26 +4493,26 @@ mtrtaaamc_mgc_service_change_reply_ar(Mid, Cid) -> CR = cre_cmdReply(SCR), cre_actionReply(Cid, [CR]). -mtrtaaamc_mgc_service_change_reply_msg(Mid, TransId, Cid) -> - AR = mtrtaaamc_mgc_service_change_reply_ar(Mid, Cid), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrtaaamc_mgc_service_change_reply_msg(Mid, TransId, Cid) -> +%% AR = mtrtaaamc_mgc_service_change_reply_ar(Mid, Cid), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtrtaaamc_mgc_notify_reply_ar(Cid, TermId) -> NR = cre_notifyReply([TermId]), CR = cre_cmdReply(NR), cre_actionReply(Cid, [CR]). -mtrtaaamc_mgc_notify_reply(Mid, TransId, Cid, TermId) -> - AR = mtrtaaamc_mgc_notify_reply_ar(Cid, TermId), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrtaaamc_mgc_notify_reply(Mid, TransId, Cid, TermId) -> +%% AR = mtrtaaamc_mgc_notify_reply_ar(Cid, TermId), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -4596,12 +4667,12 @@ mtrtaaamc_mg_service_change_request_ar(_Mid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -mtrtaaamc_mg_service_change_request_msg(Mid, TransId, Cid) -> - AR = mtrtaaamc_mg_service_change_request_ar(Mid, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrtaaamc_mg_service_change_request_msg(Mid, TransId, Cid) -> +%% AR = mtrtaaamc_mg_service_change_request_ar(Mid, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtrtaaamc_mg_notify_request_ar(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "22000000"), @@ -4612,12 +4683,12 @@ mtrtaaamc_mg_notify_request_ar(Rid, Tid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -mtrtaaamc_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> - AR = mtrtaaamc_mg_notify_request_ar(Rid, TermId, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrtaaamc_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> +%% AR = mtrtaaamc_mg_notify_request_ar(Rid, TermId, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -4625,9 +4696,7 @@ mtrtaaamc_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> %% mtrtaaamc_err_desc(T) -> - EC = ?megaco_internal_gateway_error, - ET = lists:flatten(io_lib:format("~w",[T])), - #'ErrorDescriptor'{errorCode = EC, errorText = ET}. + cre_ErrDesc(T). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -4645,8 +4714,8 @@ multi_trans_req_and_ack_reqmaxcount(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MGC Node: ~p" + "~n MG Node: ~p", [MgcNode, MgNode]), ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), @@ -4919,26 +4988,26 @@ mtrtaarac_mgc_service_change_reply_ar(Mid, Cid) -> CR = cre_cmdReply(SCR), cre_actionReply(Cid, [CR]). -mtrtaarac_mgc_service_change_reply_msg(Mid, TransId, Cid) -> - AR = mtrtaarac_mgc_service_change_reply_ar(Mid, Cid), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrtaarac_mgc_service_change_reply_msg(Mid, TransId, Cid) -> +%% AR = mtrtaarac_mgc_service_change_reply_ar(Mid, Cid), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtrtaarac_mgc_notify_reply_ar(Cid, TermId) -> NR = cre_notifyReply([TermId]), CR = cre_cmdReply(NR), cre_actionReply(Cid, [CR]). -mtrtaarac_mgc_notify_reply(Mid, TransId, Cid, TermId) -> - AR = mtrtaarac_mgc_notify_reply_ar(Cid, TermId), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrtaarac_mgc_notify_reply(Mid, TransId, Cid, TermId) -> +%% AR = mtrtaarac_mgc_notify_reply_ar(Cid, TermId), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -5093,12 +5162,12 @@ mtrtaarac_mg_service_change_request_ar(_Mid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -mtrtaarac_mg_service_change_request_msg(Mid, TransId, Cid) -> - AR = mtrtaarac_mg_service_change_request_ar(Mid, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrtaarac_mg_service_change_request_msg(Mid, TransId, Cid) -> +%% AR = mtrtaarac_mg_service_change_request_ar(Mid, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtrtaarac_mg_notify_request_ar(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "22000000"), @@ -5109,12 +5178,12 @@ mtrtaarac_mg_notify_request_ar(Rid, Tid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -mtrtaarac_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> - AR = mtrtaarac_mg_notify_request_ar(Rid, TermId, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrtaarac_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> +%% AR = mtrtaarac_mg_notify_request_ar(Rid, TermId, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -5142,8 +5211,8 @@ multi_trans_req_and_ack_maxsize1(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MGC Node: ~p" + "~n MG Node: ~p", [MgcNode, MgNode]), ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), @@ -5416,26 +5485,26 @@ mtrtaams1_mgc_service_change_reply_ar(Mid, Cid) -> CR = cre_cmdReply(SCR), cre_actionReply(Cid, [CR]). -mtrtaams1_mgc_service_change_reply_msg(Mid, TransId, Cid) -> - AR = mtrtaams1_mgc_service_change_reply_ar(Mid, Cid), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrtaams1_mgc_service_change_reply_msg(Mid, TransId, Cid) -> +%% AR = mtrtaams1_mgc_service_change_reply_ar(Mid, Cid), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtrtaams1_mgc_notify_reply_ar(Cid, TermId) -> NR = cre_notifyReply([TermId]), CR = cre_cmdReply(NR), cre_actionReply(Cid, [CR]). -mtrtaams1_mgc_notify_reply(Mid, TransId, Cid, TermId) -> - AR = mtrtaams1_mgc_notify_reply_ar(Cid, TermId), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrtaams1_mgc_notify_reply(Mid, TransId, Cid, TermId) -> +%% AR = mtrtaams1_mgc_notify_reply_ar(Cid, TermId), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -5589,12 +5658,12 @@ mtrtaams1_mg_service_change_request_ar(_Mid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -mtrtaams1_mg_service_change_request_msg(Mid, TransId, Cid) -> - AR = mtrtaams1_mg_service_change_request_ar(Mid, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrtaams1_mg_service_change_request_msg(Mid, TransId, Cid) -> +%% AR = mtrtaams1_mg_service_change_request_ar(Mid, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtrtaams1_mg_notify_request_ar(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "22000000"), @@ -5605,12 +5674,12 @@ mtrtaams1_mg_notify_request_ar(Rid, Tid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -mtrtaams1_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> - AR = mtrtaams1_mg_notify_request_ar(Rid, TermId, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrtaams1_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> +%% AR = mtrtaams1_mg_notify_request_ar(Rid, TermId, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -5618,9 +5687,7 @@ mtrtaams1_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> %% mtrtaams1_err_desc(T) -> - EC = ?megaco_internal_gateway_error, - ET = lists:flatten(io_lib:format("~w",[T])), - #'ErrorDescriptor'{errorCode = EC, errorText = ET}. + cre_ErrDesc(T). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -5638,8 +5705,8 @@ multi_trans_req_and_ack_maxsize2(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MGC Node: ~p" + "~n MG Node: ~p", [MgcNode, MgNode]), ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), @@ -5915,13 +5982,13 @@ mtrtaams2_mgc_service_change_reply_ar(Mid, Cid) -> CR = cre_cmdReply(SCR), cre_actionReply(Cid, [CR]). -mtrtaams2_mgc_service_change_reply_msg(Mid, TransId, Cid) -> - AR = mtrtaams2_mgc_service_change_reply_ar(Mid, Cid), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrtaams2_mgc_service_change_reply_msg(Mid, TransId, Cid) -> +%% AR = mtrtaams2_mgc_service_change_reply_ar(Mid, Cid), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtrtaams2_mgc_notify_reply_ar1(Cid, TermId) -> NR = cre_notifyReply([TermId]), @@ -5932,13 +5999,13 @@ mtrtaams2_mgc_notify_reply_ar2(Cid, Tids) -> CRs = [cre_cmdReply(cre_notifyReply([Tid])) || Tid <- Tids], cre_actionReply(Cid, CRs). -mtrtaams2_mgc_notify_reply(Mid, TransId, Cid, TermId) -> - AR = mtrtaams2_mgc_notify_reply_ar1(Cid, TermId), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrtaams2_mgc_notify_reply(Mid, TransId, Cid, TermId) -> +%% AR = mtrtaams2_mgc_notify_reply_ar1(Cid, TermId), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -6093,12 +6160,12 @@ mtrtaams2_mg_service_change_request_ar(_Mid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -mtrtaams2_mg_service_change_request_msg(Mid, TransId, Cid) -> - AR = mtrtaams2_mg_service_change_request_ar(Mid, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrtaams2_mg_service_change_request_msg(Mid, TransId, Cid) -> +%% AR = mtrtaams2_mg_service_change_request_ar(Mid, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtrtaams2_mg_notify_request_ar1(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "22000000"), @@ -6124,12 +6191,12 @@ mtrtaams2_mg_notify_request_ar2(Rid, Tid, Cid) -> CRs = [F(N) || N <- Ns], cre_actionReq(Cid, CRs). -mtrtaams2_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> - AR = mtrtaams2_mg_notify_request_ar1(Rid, TermId, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrtaams2_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> +%% AR = mtrtaams2_mg_notify_request_ar1(Rid, TermId, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -6137,9 +6204,7 @@ mtrtaams2_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> %% mtrtaams2_err_desc(T) -> - EC = ?megaco_internal_gateway_error, - ET = lists:flatten(io_lib:format("~w",[T])), - #'ErrorDescriptor'{errorCode = EC, errorText = ET}. + cre_ErrDesc(T). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -6177,8 +6242,8 @@ multi_trans_req_and_ack_and_pending(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MGC Node: ~p" + "~n MG Node: ~p", [MgcNode, MgNode]), ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), @@ -6469,13 +6534,13 @@ mtraaap_mgc_service_change_reply_ar(Mid, Cid) -> CR = cre_cmdReply(SCR), cre_actionReply(Cid, [CR]). -mtraaap_mgc_service_change_reply_msg(Mid, TransId, Cid) -> - AR = mtraaap_mgc_service_change_reply_ar(Mid, Cid), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtraaap_mgc_service_change_reply_msg(Mid, TransId, Cid) -> +%% AR = mtraaap_mgc_service_change_reply_ar(Mid, Cid), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtraaap_mgc_notify_request_ar(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "44000000"), @@ -6491,13 +6556,13 @@ mtraaap_mgc_notify_reply_ar(Cid, TermId) -> CR = cre_cmdReply(NR), cre_actionReply(Cid, [CR]). -mtraaap_mgc_notify_reply(Mid, TransId, Cid, TermId) -> - AR = mtraaap_mgc_notify_reply_ar(Cid, TermId), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtraaap_mgc_notify_reply(Mid, TransId, Cid, TermId) -> +%% AR = mtraaap_mgc_notify_reply_ar(Cid, TermId), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -6639,38 +6704,38 @@ mtraaap_mg_verify_service_change_reply(Else) -> "~n Else: ~p~n", [Else]), {error, Else, ok}. -mtraaap_mg_verify_notify_request_fun() -> - fun(Ev) -> - mtraaap_mg_verify_notify_request(Ev) - end. - -mtraaap_mg_verify_notify_request( - {handle_trans_request, _, ?VERSION, [AR]}) -> - io:format("mtraaap_mg_verify_notify_request -> ok" - "~n AR: ~p~n", [AR]), - case AR of - #'ActionRequest'{contextId = 1 = Cid, - commandRequests = [CR]} -> - #'CommandRequest'{command = Cmd} = CR, - {notifyReq, NR} = Cmd, - #'NotifyRequest'{terminationID = [Tid], - observedEventsDescriptor = OED, - errorDescriptor = asn1_NOVALUE} = NR, - #'ObservedEventsDescriptor'{observedEventLst = [OE]} = OED, - #'ObservedEvent'{eventName = "al/of"} = OE, - Reply = {discard_ack, [mtraaap_mg_notify_reply_ar(Cid, Tid)]}, - {ok, 3000, AR, Reply}; - _ -> - ED = mtraaap_err_desc(AR), - ErrReply = {discard_ack, ED}, - {error, AR, ErrReply} - end; -mtraaap_mg_verify_notify_request(Else) -> - io:format("mtraaap_mg_verify_notify_request:fun -> unknown" - "~n Else: ~p~n", [Else]), - ED = mtraaap_err_desc(Else), - ErrReply = {discard_ack, ED}, - {error, Else, ErrReply}. +%% mtraaap_mg_verify_notify_request_fun() -> +%% fun(Ev) -> +%% mtraaap_mg_verify_notify_request(Ev) +%% end. + +%% mtraaap_mg_verify_notify_request( +%% {handle_trans_request, _, ?VERSION, [AR]}) -> +%% io:format("mtraaap_mg_verify_notify_request -> ok" +%% "~n AR: ~p~n", [AR]), +%% case AR of +%% #'ActionRequest'{contextId = 1 = Cid, +%% commandRequests = [CR]} -> +%% #'CommandRequest'{command = Cmd} = CR, +%% {notifyReq, NR} = Cmd, +%% #'NotifyRequest'{terminationID = [Tid], +%% observedEventsDescriptor = OED, +%% errorDescriptor = asn1_NOVALUE} = NR, +%% #'ObservedEventsDescriptor'{observedEventLst = [OE]} = OED, +%% #'ObservedEvent'{eventName = "al/of"} = OE, +%% Reply = {discard_ack, [mtraaap_mg_notify_reply_ar(Cid, Tid)]}, +%% {ok, 3000, AR, Reply}; +%% _ -> +%% ED = mtraaap_err_desc(AR), +%% ErrReply = {discard_ack, ED}, +%% {error, AR, ErrReply} +%% end; +%% mtraaap_mg_verify_notify_request(Else) -> +%% io:format("mtraaap_mg_verify_notify_request:fun -> unknown" +%% "~n Else: ~p~n", [Else]), +%% ED = mtraaap_err_desc(Else), +%% ErrReply = {discard_ack, ED}, +%% {error, Else, ErrReply}. mtraaap_mg_verify_notify_reply({handle_trans_reply, _CH, ?VERSION, {ok, [AR]}, _}) -> @@ -6691,17 +6756,17 @@ mtraaap_mg_service_change_request_ar(_Mid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -mtraaap_mg_service_change_request_msg(Mid, TransId, Cid) -> - AR = mtraaap_mg_service_change_request_ar(Mid, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtraaap_mg_service_change_request_msg(Mid, TransId, Cid) -> +%% AR = mtraaap_mg_service_change_request_ar(Mid, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). -mtraaap_mg_notify_reply_ar(Cid, TermId) -> - NR = cre_notifyReply([TermId]), - CR = cre_cmdReply(NR), - cre_actionReply(Cid, [CR]). +%% mtraaap_mg_notify_reply_ar(Cid, TermId) -> +%% NR = cre_notifyReply([TermId]), +%% CR = cre_cmdReply(NR), +%% cre_actionReply(Cid, [CR]). mtraaap_mg_notify_request_ar(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "22000000"), @@ -6712,12 +6777,12 @@ mtraaap_mg_notify_request_ar(Rid, Tid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -mtraaap_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> - AR = mtraaap_mg_notify_request_ar(Rid, TermId, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtraaap_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> +%% AR = mtraaap_mg_notify_request_ar(Rid, TermId, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -6725,9 +6790,7 @@ mtraaap_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> %% mtraaap_err_desc(T) -> - EC = ?megaco_internal_gateway_error, - ET = lists:flatten(io_lib:format("~w",[T])), - #'ErrorDescriptor'{errorCode = EC, errorText = ET}. + cre_ErrDesc(T). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -6765,8 +6828,8 @@ multi_trans_req_and_ack_and_reply(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MGC Node: ~p" + "~n MG Node: ~p", [MgcNode, MgNode]), ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), @@ -7061,13 +7124,13 @@ mtraaar_mgc_service_change_reply_ar(Mid, Cid) -> CR = cre_cmdReply(SCR), cre_actionReply(Cid, [CR]). -mtraaar_mgc_service_change_reply_msg(Mid, TransId, Cid) -> - AR = mtraaar_mgc_service_change_reply_ar(Mid, Cid), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtraaar_mgc_service_change_reply_msg(Mid, TransId, Cid) -> +%% AR = mtraaar_mgc_service_change_reply_ar(Mid, Cid), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtraaar_mgc_notify_request_ar(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "44000000"), @@ -7083,13 +7146,13 @@ mtraaar_mgc_notify_reply_ar(Cid, TermId) -> CR = cre_cmdReply(NR), cre_actionReply(Cid, [CR]). -mtraaar_mgc_notify_reply(Mid, TransId, Cid, TermId) -> - AR = mtraaar_mgc_notify_reply_ar(Cid, TermId), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtraaar_mgc_notify_reply(Mid, TransId, Cid, TermId) -> +%% AR = mtraaar_mgc_notify_reply_ar(Cid, TermId), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -7232,38 +7295,38 @@ mtraaar_mg_verify_service_change_reply(Else) -> "~n Else: ~p~n", [Else]), {error, Else, ok}. -mtraaar_mg_verify_notify_request_fun() -> - fun(Ev) -> - mtraaar_mg_verify_notify_request(Ev) - end. - -mtraaar_mg_verify_notify_request( - {handle_trans_request, _, ?VERSION, [AR]}) -> - io:format("mtraaar_mg_verify_notify_request -> ok" - "~n AR: ~p~n", [AR]), - case AR of - #'ActionRequest'{contextId = 1 = Cid, - commandRequests = [CR]} -> - #'CommandRequest'{command = Cmd} = CR, - {notifyReq, NR} = Cmd, - #'NotifyRequest'{terminationID = [Tid], - observedEventsDescriptor = OED, - errorDescriptor = asn1_NOVALUE} = NR, - #'ObservedEventsDescriptor'{observedEventLst = [OE]} = OED, - #'ObservedEvent'{eventName = "al/of"} = OE, - Reply = {discard_ack, [mtraaar_mg_notify_reply_ar(Cid, Tid)]}, - {ok, AR, Reply}; - _ -> - ED = mtraaar_err_desc(AR), - ErrReply = {discard_ack, ED}, - {error, AR, ErrReply} - end; -mtraaar_mg_verify_notify_request(Else) -> - io:format("mtraaar_mg_verify_notify_request -> unknown" - "~n Else: ~p~n", [Else]), - ED = mtraaar_err_desc(Else), - ErrReply = {discard_ack, ED}, - {error, Else, ErrReply}. +%% mtraaar_mg_verify_notify_request_fun() -> +%% fun(Ev) -> +%% mtraaar_mg_verify_notify_request(Ev) +%% end. + +%% mtraaar_mg_verify_notify_request( +%% {handle_trans_request, _, ?VERSION, [AR]}) -> +%% io:format("mtraaar_mg_verify_notify_request -> ok" +%% "~n AR: ~p~n", [AR]), +%% case AR of +%% #'ActionRequest'{contextId = 1 = Cid, +%% commandRequests = [CR]} -> +%% #'CommandRequest'{command = Cmd} = CR, +%% {notifyReq, NR} = Cmd, +%% #'NotifyRequest'{terminationID = [Tid], +%% observedEventsDescriptor = OED, +%% errorDescriptor = asn1_NOVALUE} = NR, +%% #'ObservedEventsDescriptor'{observedEventLst = [OE]} = OED, +%% #'ObservedEvent'{eventName = "al/of"} = OE, +%% Reply = {discard_ack, [mtraaar_mg_notify_reply_ar(Cid, Tid)]}, +%% {ok, AR, Reply}; +%% _ -> +%% ED = mtraaar_err_desc(AR), +%% ErrReply = {discard_ack, ED}, +%% {error, AR, ErrReply} +%% end; +%% mtraaar_mg_verify_notify_request(Else) -> +%% io:format("mtraaar_mg_verify_notify_request -> unknown" +%% "~n Else: ~p~n", [Else]), +%% ED = mtraaar_err_desc(Else), +%% ErrReply = {discard_ack, ED}, +%% {error, Else, ErrReply}. mtraaar_mg_verify_notify_reply({handle_trans_reply, _CH, ?VERSION, {ok, [AR]}, _}) -> @@ -7284,17 +7347,17 @@ mtraaar_mg_service_change_request_ar(_Mid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -mtraaar_mg_service_change_request_msg(Mid, TransId, Cid) -> - AR = mtraaar_mg_service_change_request_ar(Mid, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtraaar_mg_service_change_request_msg(Mid, TransId, Cid) -> +%% AR = mtraaar_mg_service_change_request_ar(Mid, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). -mtraaar_mg_notify_reply_ar(Cid, TermId) -> - NR = cre_notifyReply([TermId]), - CR = cre_cmdReply(NR), - cre_actionReply(Cid, [CR]). +%% mtraaar_mg_notify_reply_ar(Cid, TermId) -> +%% NR = cre_notifyReply([TermId]), +%% CR = cre_cmdReply(NR), +%% cre_actionReply(Cid, [CR]). mtraaar_mg_notify_request_ar(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "22000000"), @@ -7305,12 +7368,12 @@ mtraaar_mg_notify_request_ar(Rid, Tid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -mtraaar_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> - AR = mtraaar_mg_notify_request_ar(Rid, TermId, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtraaar_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> +%% AR = mtraaar_mg_notify_request_ar(Rid, TermId, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -7318,9 +7381,7 @@ mtraaar_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> %% mtraaar_err_desc(T) -> - EC = ?megaco_internal_gateway_error, - ET = lists:flatten(io_lib:format("~w",[T])), - #'ErrorDescriptor'{errorCode = EC, errorText = ET}. + cre_ErrDesc(T). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -7348,8 +7409,8 @@ otp_7192_1(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MGC Node: ~p" + "~n MG Node: ~p", [MgcNode, MgNode]), MgMid = {deviceName,"mg"}, @@ -7643,13 +7704,13 @@ otp71921_mgc_service_change_reply_ar(Mid, Cid) -> CR = cre_cmdReply(SCR), cre_actionReply(Cid, [CR]). -otp71921_mgc_service_change_reply_msg(Mid, TransId, Cid) -> - AR = otp71921_mgc_service_change_reply_ar(Mid, Cid), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% otp71921_mgc_service_change_reply_msg(Mid, TransId, Cid) -> +%% AR = otp71921_mgc_service_change_reply_ar(Mid, Cid), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). otp71921_mgc_notify_request_ar(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "44000000"), @@ -7665,13 +7726,13 @@ otp71921_mgc_notify_reply_ar(Cid, TermId) -> CR = cre_cmdReply(NR), cre_actionReply(Cid, [CR]). -otp71921_mgc_notify_reply(Mid, TransId, Cid, TermId) -> - AR = otp71921_mgc_notify_reply_ar(Cid, TermId), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% otp71921_mgc_notify_reply(Mid, TransId, Cid, TermId) -> +%% AR = otp71921_mgc_notify_reply_ar(Cid, TermId), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -7813,38 +7874,38 @@ otp71921_mg_verify_service_change_reply(Else) -> "~n Else: ~p~n", [Else]), {error, Else, ok}. -otp71921_mg_verify_notify_request_fun() -> - fun(Ev) -> - otp71921_mg_verify_notify_request(Ev) - end. - -otp71921_mg_verify_notify_request( - {handle_trans_request, _, ?VERSION, [AR]}) -> - io:format("otp71921_mg_verify_notify_request -> ok" - "~n AR: ~p~n", [AR]), - case AR of - #'ActionRequest'{contextId = 1 = Cid, - commandRequests = [CR]} -> - #'CommandRequest'{command = Cmd} = CR, - {notifyReq, NR} = Cmd, - #'NotifyRequest'{terminationID = [Tid], - observedEventsDescriptor = OED, - errorDescriptor = asn1_NOVALUE} = NR, - #'ObservedEventsDescriptor'{observedEventLst = [OE]} = OED, - #'ObservedEvent'{eventName = "al/of"} = OE, - Reply = {discard_ack, [otp71921_mg_notify_reply_ar(Cid, Tid)]}, - {ok, AR, Reply}; - _ -> - ED = otp71921_err_desc(AR), - ErrReply = {discard_ack, ED}, - {error, AR, ErrReply} - end; -otp71921_mg_verify_notify_request(Else) -> - io:format("otp71921_mg_verify_notify_request -> unknown" - "~n Else: ~p~n", [Else]), - ED = otp71921_err_desc(Else), - ErrReply = {discard_ack, ED}, - {error, Else, ErrReply}. +%% otp71921_mg_verify_notify_request_fun() -> +%% fun(Ev) -> +%% otp71921_mg_verify_notify_request(Ev) +%% end. + +%% otp71921_mg_verify_notify_request( +%% {handle_trans_request, _, ?VERSION, [AR]}) -> +%% io:format("otp71921_mg_verify_notify_request -> ok" +%% "~n AR: ~p~n", [AR]), +%% case AR of +%% #'ActionRequest'{contextId = 1 = Cid, +%% commandRequests = [CR]} -> +%% #'CommandRequest'{command = Cmd} = CR, +%% {notifyReq, NR} = Cmd, +%% #'NotifyRequest'{terminationID = [Tid], +%% observedEventsDescriptor = OED, +%% errorDescriptor = asn1_NOVALUE} = NR, +%% #'ObservedEventsDescriptor'{observedEventLst = [OE]} = OED, +%% #'ObservedEvent'{eventName = "al/of"} = OE, +%% Reply = {discard_ack, [otp71921_mg_notify_reply_ar(Cid, Tid)]}, +%% {ok, AR, Reply}; +%% _ -> +%% ED = otp71921_err_desc(AR), +%% ErrReply = {discard_ack, ED}, +%% {error, AR, ErrReply} +%% end; +%% otp71921_mg_verify_notify_request(Else) -> +%% io:format("otp71921_mg_verify_notify_request -> unknown" +%% "~n Else: ~p~n", [Else]), +%% ED = otp71921_err_desc(Else), +%% ErrReply = {discard_ack, ED}, +%% {error, Else, ErrReply}. otp71921_mg_verify_notify_reply({handle_trans_reply, _CH, ?VERSION, {ok, [AR]}, _}) -> @@ -7865,17 +7926,17 @@ otp71921_mg_service_change_request_ar(_Mid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -otp71921_mg_service_change_request_msg(Mid, TransId, Cid) -> - AR = otp71921_mg_service_change_request_ar(Mid, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% otp71921_mg_service_change_request_msg(Mid, TransId, Cid) -> +%% AR = otp71921_mg_service_change_request_ar(Mid, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). -otp71921_mg_notify_reply_ar(Cid, TermId) -> - NR = cre_notifyReply([TermId]), - CR = cre_cmdReply(NR), - cre_actionReply(Cid, [CR]). +%% otp71921_mg_notify_reply_ar(Cid, TermId) -> +%% NR = cre_notifyReply([TermId]), +%% CR = cre_cmdReply(NR), +%% cre_actionReply(Cid, [CR]). otp71921_mg_notify_request_ar(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "22000000"), @@ -7886,12 +7947,12 @@ otp71921_mg_notify_request_ar(Rid, Tid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -otp71921_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> - AR = otp71921_mg_notify_request_ar(Rid, TermId, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% otp71921_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> +%% AR = otp71921_mg_notify_request_ar(Rid, TermId, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -7919,8 +7980,8 @@ otp_7192_2(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MGC Node: ~p" + "~n MG Node: ~p", [MgcNode, MgNode]), MgMid = {deviceName,"mg"}, @@ -8213,13 +8274,13 @@ otp71922_mgc_service_change_reply_ar(Mid, Cid) -> CR = cre_cmdReply(SCR), cre_actionReply(Cid, [CR]). -otp71922_mgc_service_change_reply_msg(Mid, TransId, Cid) -> - AR = otp71922_mgc_service_change_reply_ar(Mid, Cid), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% otp71922_mgc_service_change_reply_msg(Mid, TransId, Cid) -> +%% AR = otp71922_mgc_service_change_reply_ar(Mid, Cid), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). otp71922_mgc_notify_request_ar(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "44000000"), @@ -8235,13 +8296,13 @@ otp71922_mgc_notify_reply_ar(Cid, TermId) -> CR = cre_cmdReply(NR), cre_actionReply(Cid, [CR]). -otp71922_mgc_notify_reply(Mid, TransId, Cid, TermId) -> - AR = otp71922_mgc_notify_reply_ar(Cid, TermId), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% otp71922_mgc_notify_reply(Mid, TransId, Cid, TermId) -> +%% AR = otp71922_mgc_notify_reply_ar(Cid, TermId), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -8379,38 +8440,38 @@ otp71922_mg_verify_service_change_reply(Else) -> "~n Else: ~p~n", [Else]), {error, Else, ok}. -otp71922_mg_verify_notify_request_fun() -> - fun(Ev) -> - otp71922_mg_verify_notify_request(Ev) - end. - -otp71922_mg_verify_notify_request( - {handle_trans_request, _, ?VERSION, [AR]}) -> - io:format("otp71922_mg_verify_notify_request -> ok" - "~n AR: ~p~n", [AR]), - case AR of - #'ActionRequest'{contextId = 1 = Cid, - commandRequests = [CR]} -> - #'CommandRequest'{command = Cmd} = CR, - {notifyReq, NR} = Cmd, - #'NotifyRequest'{terminationID = [Tid], - observedEventsDescriptor = OED, - errorDescriptor = asn1_NOVALUE} = NR, - #'ObservedEventsDescriptor'{observedEventLst = [OE]} = OED, - #'ObservedEvent'{eventName = "al/of"} = OE, - Reply = {discard_ack, [otp71922_mg_notify_reply_ar(Cid, Tid)]}, - {ok, AR, Reply}; - _ -> - ED = otp71922_err_desc(AR), - ErrReply = {discard_ack, ED}, - {error, AR, ErrReply} - end; -otp71922_mg_verify_notify_request(Else) -> - io:format("otp71922_mg_verify_notify_request -> unknown" - "~n Else: ~p~n", [Else]), - ED = otp71922_err_desc(Else), - ErrReply = {discard_ack, ED}, - {error, Else, ErrReply}. +%% otp71922_mg_verify_notify_request_fun() -> +%% fun(Ev) -> +%% otp71922_mg_verify_notify_request(Ev) +%% end. + +%% otp71922_mg_verify_notify_request( +%% {handle_trans_request, _, ?VERSION, [AR]}) -> +%% io:format("otp71922_mg_verify_notify_request -> ok" +%% "~n AR: ~p~n", [AR]), +%% case AR of +%% #'ActionRequest'{contextId = 1 = Cid, +%% commandRequests = [CR]} -> +%% #'CommandRequest'{command = Cmd} = CR, +%% {notifyReq, NR} = Cmd, +%% #'NotifyRequest'{terminationID = [Tid], +%% observedEventsDescriptor = OED, +%% errorDescriptor = asn1_NOVALUE} = NR, +%% #'ObservedEventsDescriptor'{observedEventLst = [OE]} = OED, +%% #'ObservedEvent'{eventName = "al/of"} = OE, +%% Reply = {discard_ack, [otp71922_mg_notify_reply_ar(Cid, Tid)]}, +%% {ok, AR, Reply}; +%% _ -> +%% ED = otp71922_err_desc(AR), +%% ErrReply = {discard_ack, ED}, +%% {error, AR, ErrReply} +%% end; +%% otp71922_mg_verify_notify_request(Else) -> +%% io:format("otp71922_mg_verify_notify_request -> unknown" +%% "~n Else: ~p~n", [Else]), +%% ED = otp71922_err_desc(Else), +%% ErrReply = {discard_ack, ED}, +%% {error, Else, ErrReply}. otp71922_mg_verify_notify_reply({handle_trans_reply, _CH, ?VERSION, {ok, [AR]}, _}) -> @@ -8431,17 +8492,17 @@ otp71922_mg_service_change_request_ar(_Mid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -otp71922_mg_service_change_request_msg(Mid, TransId, Cid) -> - AR = otp71922_mg_service_change_request_ar(Mid, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% otp71922_mg_service_change_request_msg(Mid, TransId, Cid) -> +%% AR = otp71922_mg_service_change_request_ar(Mid, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). -otp71922_mg_notify_reply_ar(Cid, TermId) -> - NR = cre_notifyReply([TermId]), - CR = cre_cmdReply(NR), - cre_actionReply(Cid, [CR]). +%% otp71922_mg_notify_reply_ar(Cid, TermId) -> +%% NR = cre_notifyReply([TermId]), +%% CR = cre_cmdReply(NR), +%% cre_actionReply(Cid, [CR]). otp71922_mg_notify_request_ar(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "22000000"), @@ -8452,12 +8513,12 @@ otp71922_mg_notify_request_ar(Rid, Tid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -otp71922_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> - AR = otp71922_mg_notify_request_ar(Rid, TermId, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% otp71922_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> +%% AR = otp71922_mg_notify_request_ar(Rid, TermId, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -8465,9 +8526,7 @@ otp71922_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> %% otp71922_err_desc(T) -> - EC = ?megaco_internal_gateway_error, - ET = lists:flatten(io_lib:format("~w",[T])), - #'ErrorDescriptor'{errorCode = EC, errorText = ET}. + cre_ErrDesc(T). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -8485,8 +8544,8 @@ otp_7192_3(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MGC Node: ~p" + "~n MG Node: ~p", [MgcNode, MgNode]), MgMid = {deviceName,"mg"}, @@ -8779,13 +8838,13 @@ otp72923_mgc_service_change_reply_ar(Mid, Cid) -> CR = cre_cmdReply(SCR), cre_actionReply(Cid, [CR]). -otp72923_mgc_service_change_reply_msg(Mid, TransId, Cid) -> - AR = otp72923_mgc_service_change_reply_ar(Mid, Cid), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% otp72923_mgc_service_change_reply_msg(Mid, TransId, Cid) -> +%% AR = otp72923_mgc_service_change_reply_ar(Mid, Cid), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). otp72923_mgc_notify_request_ar(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "44000000"), @@ -8801,13 +8860,13 @@ otp72923_mgc_notify_reply_ar(Cid, TermId) -> CR = cre_cmdReply(NR), cre_actionReply(Cid, [CR]). -otp72923_mgc_notify_reply(Mid, TransId, Cid, TermId) -> - AR = otp72923_mgc_notify_reply_ar(Cid, TermId), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% otp72923_mgc_notify_reply(Mid, TransId, Cid, TermId) -> +%% AR = otp72923_mgc_notify_reply_ar(Cid, TermId), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -8946,38 +9005,38 @@ otp72923_mg_verify_service_change_reply(Else) -> "~n Else: ~p~n", [Else]), {error, Else, ok}. -otp72923_mg_verify_notify_request_fun() -> - fun(Ev) -> - otp72923_mg_verify_notify_request(Ev) - end. - -otp72923_mg_verify_notify_request( - {handle_trans_request, _, ?VERSION, [AR]}) -> - io:format("otp72923_mg_verify_notify_request -> ok" - "~n AR: ~p~n", [AR]), - case AR of - #'ActionRequest'{contextId = 1 = Cid, - commandRequests = [CR]} -> - #'CommandRequest'{command = Cmd} = CR, - {notifyReq, NR} = Cmd, - #'NotifyRequest'{terminationID = [Tid], - observedEventsDescriptor = OED, - errorDescriptor = asn1_NOVALUE} = NR, - #'ObservedEventsDescriptor'{observedEventLst = [OE]} = OED, - #'ObservedEvent'{eventName = "al/of"} = OE, - Reply = {discard_ack, [otp72923_mg_notify_reply_ar(Cid, Tid)]}, - {ok, AR, Reply}; - _ -> - ED = otp72923_err_desc(AR), - ErrReply = {discard_ack, ED}, - {error, AR, ErrReply} - end; -otp72923_mg_verify_notify_request(Else) -> - io:format("otp72923_mg_verify_notify_request -> unknown" - "~n Else: ~p~n", [Else]), - ED = otp72923_err_desc(Else), - ErrReply = {discard_ack, ED}, - {error, Else, ErrReply}. +%% otp72923_mg_verify_notify_request_fun() -> +%% fun(Ev) -> +%% otp72923_mg_verify_notify_request(Ev) +%% end. + +%% otp72923_mg_verify_notify_request( +%% {handle_trans_request, _, ?VERSION, [AR]}) -> +%% io:format("otp72923_mg_verify_notify_request -> ok" +%% "~n AR: ~p~n", [AR]), +%% case AR of +%% #'ActionRequest'{contextId = 1 = Cid, +%% commandRequests = [CR]} -> +%% #'CommandRequest'{command = Cmd} = CR, +%% {notifyReq, NR} = Cmd, +%% #'NotifyRequest'{terminationID = [Tid], +%% observedEventsDescriptor = OED, +%% errorDescriptor = asn1_NOVALUE} = NR, +%% #'ObservedEventsDescriptor'{observedEventLst = [OE]} = OED, +%% #'ObservedEvent'{eventName = "al/of"} = OE, +%% Reply = {discard_ack, [otp72923_mg_notify_reply_ar(Cid, Tid)]}, +%% {ok, AR, Reply}; +%% _ -> +%% ED = otp72923_err_desc(AR), +%% ErrReply = {discard_ack, ED}, +%% {error, AR, ErrReply} +%% end; +%% otp72923_mg_verify_notify_request(Else) -> +%% io:format("otp72923_mg_verify_notify_request -> unknown" +%% "~n Else: ~p~n", [Else]), +%% ED = otp72923_err_desc(Else), +%% ErrReply = {discard_ack, ED}, +%% {error, Else, ErrReply}. otp72923_mg_verify_notify_reply({handle_trans_reply, _CH, ?VERSION, {ok, [AR]}, _}) -> @@ -8998,17 +9057,17 @@ otp72923_mg_service_change_request_ar(_Mid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -otp72923_mg_service_change_request_msg(Mid, TransId, Cid) -> - AR = otp72923_mg_service_change_request_ar(Mid, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% otp72923_mg_service_change_request_msg(Mid, TransId, Cid) -> +%% AR = otp72923_mg_service_change_request_ar(Mid, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). -otp72923_mg_notify_reply_ar(Cid, TermId) -> - NR = cre_notifyReply([TermId]), - CR = cre_cmdReply(NR), - cre_actionReply(Cid, [CR]). +%% otp72923_mg_notify_reply_ar(Cid, TermId) -> +%% NR = cre_notifyReply([TermId]), +%% CR = cre_cmdReply(NR), +%% cre_actionReply(Cid, [CR]). otp72923_mg_notify_request_ar(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "22000000"), @@ -9019,12 +9078,12 @@ otp72923_mg_notify_request_ar(Rid, Tid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -otp72923_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> - AR = otp72923_mg_notify_request_ar(Rid, TermId, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% otp72923_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> +%% AR = otp72923_mg_notify_request_ar(Rid, TermId, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -9032,9 +9091,7 @@ otp72923_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> %% otp72923_err_desc(T) -> - EC = ?megaco_internal_gateway_error, - ET = lists:flatten(io_lib:format("~w",[T])), - #'ErrorDescriptor'{errorCode = EC, errorText = ET}. + cre_ErrDesc(T). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -9065,10 +9122,10 @@ cre_timeNotation(D,T) -> cre_obsEvent(Name, Not) -> #'ObservedEvent'{eventName = Name, timeNotation = Not}. -cre_obsEvent(Name, Not, Par) -> - #'ObservedEvent'{eventName = Name, - timeNotation = Not, - eventParList = Par}. +%% cre_obsEvent(Name, Not, Par) -> +%% #'ObservedEvent'{eventName = Name, +%% timeNotation = Not, +%% eventParList = Par}. cre_obsEvsDesc(Id, EvList) -> #'ObservedEventsDescriptor'{requestId = Id, @@ -9090,9 +9147,9 @@ cre_actionReq(CtxId, CmdReqs) when is_list(CmdReqs) -> #'ActionRequest'{contextId = CtxId, commandRequests = CmdReqs}. -cre_transReq(TransId, ARs) when is_list(ARs) -> - #'TransactionRequest'{transactionId = TransId, - actions = ARs}. +%% cre_transReq(TransId, ARs) when is_list(ARs) -> +%% #'TransactionRequest'{transactionId = TransId, +%% actions = ARs}. %% -- @@ -9120,14 +9177,14 @@ cre_actionReply(CtxId, CmdRep) -> #'ActionReply'{contextId = CtxId, commandReply = CmdRep}. -cre_transResult(ED) when is_record(ED, 'ErrorDescriptor') -> - {transactionError, ED}; -cre_transResult([AR|_] = ARs) when is_record(AR, 'ActionReply') -> - {actionReplies, ARs}. +%% cre_transResult(ED) when is_record(ED, 'ErrorDescriptor') -> +%% {transactionError, ED}; +%% cre_transResult([AR|_] = ARs) when is_record(AR, 'ActionReply') -> +%% {actionReplies, ARs}. -cre_transReply(TransId, Res) -> - #'TransactionReply'{transactionId = TransId, - transactionResult = Res}. +%% cre_transReply(TransId, Res) -> +%% #'TransactionReply'{transactionId = TransId, +%% transactionResult = Res}. %% -- @@ -9136,48 +9193,48 @@ cre_serviceChangeProf(Name, Ver) when is_list(Name) andalso is_integer(Ver) -> #'ServiceChangeProfile'{profileName = Name, version = Ver}. -cre_transaction(Trans) when is_record(Trans, 'TransactionRequest') -> - {transactionRequest, Trans}; -cre_transaction(Trans) when is_record(Trans, 'TransactionPending') -> - {transactionPending, Trans}; -cre_transaction(Trans) when is_record(Trans, 'TransactionReply') -> - {transactionReply, Trans}; -cre_transaction(Trans) when is_record(Trans, 'TransactionAck') -> - {transactionResponseAck, Trans}. - -cre_transactions(Trans) when is_list(Trans) -> - {transactions, Trans}. - -cre_message(Version, Mid, Body) -> - #'Message'{version = Version, - mId = Mid, - messageBody = Body}. - -cre_megacoMessage(Mess) -> - #'MegacoMessage'{mess = Mess}. +%% cre_transaction(Trans) when is_record(Trans, 'TransactionRequest') -> +%% {transactionRequest, Trans}; +%% cre_transaction(Trans) when is_record(Trans, 'TransactionPending') -> +%% {transactionPending, Trans}; +%% cre_transaction(Trans) when is_record(Trans, 'TransactionReply') -> +%% {transactionReply, Trans}; +%% cre_transaction(Trans) when is_record(Trans, 'TransactionAck') -> +%% {transactionResponseAck, Trans}. + +%% cre_transactions(Trans) when is_list(Trans) -> +%% {transactions, Trans}. + +%% cre_message(Version, Mid, Body) -> +%% #'Message'{version = Version, +%% mId = Mid, +%% messageBody = Body}. + +%% cre_megacoMessage(Mess) -> +%% #'MegacoMessage'{mess = Mess}. %% %% Common functions %% -encode_msg_fun(Mod, Conf) -> - fun(M) -> - Mod:encode_message(Conf, M) - end. -encode_msg_fun(Mod, Conf, Ver) -> - fun(M) -> - Mod:encode_message(Conf, Ver, M) - end. - -decode_msg_fun(Mod, Conf) -> - fun(M) -> - Mod:decode_message(Conf, M) - end. -decode_msg_fun(Mod, Conf, Ver) -> - fun(M) -> - Mod:decode_message(Conf, Ver, M) - end. +%% encode_msg_fun(Mod, Conf) -> +%% fun(M) -> +%% Mod:encode_message(Conf, M) +%% end. +%% encode_msg_fun(Mod, Conf, Ver) -> +%% fun(M) -> +%% Mod:encode_message(Conf, Ver, M) +%% end. + +%% decode_msg_fun(Mod, Conf) -> +%% fun(M) -> +%% Mod:decode_message(Conf, M) +%% end. +%% decode_msg_fun(Mod, Conf, Ver) -> +%% fun(M) -> +%% Mod:decode_message(Conf, Ver, M) +%% end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -9193,7 +9250,7 @@ await_ack(User, N, Timeout, Expected) when (N > 0) andalso is_integer(Timeout) - d("await_ack -> received another ack"), await_ack(User, N-1, Timeout - (tim() - T), Expected); {ack_received, User, UnExpected} -> - d("await_ack -> unexpected ack result: ~p", [UnExpected]), + e("await_ack -> unexpected ack result: ~p", [UnExpected]), exit({unexpected_ack_result, UnExpected, Expected}) after Timeout -> exit({await_ack_timeout, N}) @@ -9205,72 +9262,42 @@ await_ack(User, N, infinity, Expected) when N > 0 -> d("await_ack -> received another ack"), await_ack(User, N-1, infinity, Expected); {ack_received, User, UnExpected} -> - d("await_ack -> unexpected ack result: ~p", [UnExpected]), + e("await_ack -> unexpected ack result: ~p", [UnExpected]), exit({unexpected_ack_result, UnExpected, Expected}) end. -await_req(_User, 0, Timeout) -> - d("await_req -> done when Timeout = ~p", [Timeout]), - ok; -await_req(User, N, Timeout) when (N > 0) andalso is_integer(Timeout) -> - d("await_req -> entry with N: ~p, Timeout: ~p", [N,Timeout]), - T = tim(), - receive - {req_received, User, ARs} -> - d("await_req -> received req(s) when N = ~w", [N]), - N1 = await_req1(N, ARs), - await_req(User, N1, Timeout - (tim() - T)) - after Timeout -> - exit({await_req_timeout, N}) - end; -await_req(User, N, infinity) when N > 0 -> - d("await_req -> entry with N: ~p", [N]), - receive - {req_received, User, ARs} -> - d("await_req -> received req(s) when N = ~2",[N]), - N1 = await_req1(N, ARs), - await_req(User, N1, infinity) - end. - -await_req1(N, []) when N >= 0 -> - N; -await_req1(N, [AR|ARs]) when (N > 0) andalso is_record(AR, 'ActionRequest') -> - await_req1(N-1, ARs); -await_req1(N, ARs) -> - exit({unexpected_req_result, N, ARs}). - -% await_rep(_User, 0, Timeout) -> -% d("await_rep -> done when Timeout = ~p", [Timeout]), -% ok; -% await_rep(User, N, Timeout) when N > 0, integer(Timeout) -> -% d("await_rep -> entry with N: ~p, Timeout: ~p", [N,Timeout]), -% T = tim(), -% receive -% {rep_received, User, ARs} -> -% d("await_rep -> received rep(s)"), -% N1 = await_rep1(N, ARs), -% await_rep(User, N1, Timeout - (tim() - T)) -% after Timeout -> -% exit({await_rep_timeout, N}) -% end; -% await_rep(User, N, infinity) when N > 0 -> -% d("await_rep -> entry with N: ~p", [N]), -% receive -% {rep_received, User, ARs} -> -% d("await_rep -> received rep(s)"), -% N1 = await_rep1(N, ARs), -% await_rep(User, N1, infinity) -% end. - -% await_rep1(N, []) when N >= 0 -> -% N; -% await_rep1(N, [AR|ARs]) when N > 0, record(AR, 'ActionReply') -> -% await_rep1(N-1, ARs); -% await_rep1(N, ARs) -> -% exit({unexpected_rep_result, N, ARs}). +%% await_req(_User, 0, Timeout) -> +%% d("await_req -> done when Timeout = ~p", [Timeout]), +%% ok; +%% await_req(User, N, Timeout) when (N > 0) andalso is_integer(Timeout) -> +%% d("await_req -> entry with N: ~p, Timeout: ~p", [N,Timeout]), +%% T = tim(), +%% receive +%% {req_received, User, ARs} -> +%% d("await_req -> received req(s) when N = ~w", [N]), +%% N1 = await_req1(N, ARs), +%% await_req(User, N1, Timeout - (tim() - T)) +%% after Timeout -> +%% exit({await_req_timeout, N}) +%% end; +%% await_req(User, N, infinity) when N > 0 -> +%% d("await_req -> entry with N: ~p", [N]), +%% receive +%% {req_received, User, ARs} -> +%% d("await_req -> received req(s) when N = ~2",[N]), +%% N1 = await_req1(N, ARs), +%% await_req(User, N1, infinity) +%% end. + +%% await_req1(N, []) when N >= 0 -> +%% N; +%% await_req1(N, [AR|ARs]) when (N > 0) andalso is_record(AR, 'ActionRequest') -> +%% await_req1(N-1, ARs); +%% await_req1(N, ARs) -> +%% exit({unexpected_req_result, N, ARs}). tim() -> - {A,B,C} = erlang:now(), + {A,B,C} = erlang:timestamp(), A*1000000000+B*1000+(C div 1000). @@ -9291,7 +9318,8 @@ await_completion(Ids) -> d("OK => Reply: ~n~p", [Reply]), ok; {error, Reply} -> - d("ERROR => Reply: ~n~p", [Reply]), + e("await completion failed: " + "~n ~p", [Reply]), ?ERROR({failed, Reply}) end. @@ -9301,7 +9329,9 @@ await_completion(Ids, Timeout) -> d("OK => Reply: ~n~p", [Reply]), ok; {error, Reply} -> - d("ERROR => Reply: ~n~p", [Reply]), + e("await completion failed: " + "~n ~p" + "~n ~p", [Timeout, Reply]), ?ERROR({failed, Reply}) end. @@ -9310,64 +9340,71 @@ await_completion(Ids, Timeout) -> sleep(X) -> receive after X -> ok end. -error_msg(F,A) -> error_logger:error_msg(F ++ "~n",A). +%% error_msg(F,A) -> error_logger:error_msg(F ++ "~n",A). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% e(F) -> +%% e(F, []). + +e(F, A) -> + print(error, "ERR", F, A). + + i(F) -> i(F, []). i(F, A) -> - print(info, get(verbosity), now(), get(tc), "INF", F, A). + print(info, "INF", F, A). d(F) -> d(F, []). d(F, A) -> - print(debug, get(verbosity), now(), get(tc), "DBG", F, A). + print(debug, "DBG", F, A). -printable(_, debug) -> true; -printable(info, info) -> true; -printable(_,_) -> false. +print(Severity, P, F, A) -> + print2(printable(Severity), P, F, A). -print(Severity, Verbosity, Ts, Tc, P, F, A) -> - print(printable(Severity,Verbosity), Ts, Tc, P, F, A). +printable(Sev) -> + printable(Sev, get(verbosity)). -print(true, Ts, Tc, P, F, A) -> - io:format("*** [~s] ~s ~p ~s:~w ***" - "~n " ++ F ++ "~n", - [format_timestamp(Ts), P, self(), get(tc), Tc | A]); -print(_, _, _, _, _, _) -> +printable(_, debug) -> true; +printable(info, info) -> true; +printable(error, _) -> true; +printable(_,_) -> false. + + +print2(true, P, F, A) -> + TS = erlang:timestamp(), + TC = get(tc), + S = ?F("*** [~s] ~s ~p ~w ***" + "~n " ++ F ++ "~n" + "~n", [megaco:format_timestamp(TS), P, self(), TC | A]), + io:format("~s", [S]), + io:format(user, "~s", [S]); +print2(_, _, _, _) -> ok. p(F, A) -> io:format("*** [~s] ***" "~n " ++ F ++ "~n", - [format_timestamp(now()) | A]). + [megaco:format_timestamp(erlang:timestamp()) | A]). -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -random_init() -> - {A,B,C} = now(), - random:seed(A,B,C). -random() -> - 10 * random:uniform(50). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -apply_load_timer() -> - erlang:send_after(random(), self(), apply_load_timeout). +%% random_init() -> +%% {A,B,C} = erlang:timestamp(), +%% random:seed(A,B,C). +%% random() -> +%% 10 * random:uniform(50). -format_timestamp({_N1, _N2, N3} = Now) -> - {Date, Time} = calendar:now_to_datetime(Now), - {YYYY,MM,DD} = Date, - {Hour,Min,Sec} = Time, - FormatDate = - io_lib:format("~.4w:~.2.0w:~.2.0w ~.2.0w:~.2.0w:~.2.0w 4~w", - [YYYY,MM,DD,Hour,Min,Sec,round(N3/1000)]), - lists:flatten(FormatDate). +%% apply_load_timer() -> +%% erlang:send_after(random(), self(), apply_load_timeout). diff --git a/lib/os_mon/src/disksup.erl b/lib/os_mon/src/disksup.erl index 5118d807e1..4253067c90 100644 --- a/lib/os_mon/src/disksup.erl +++ b/lib/os_mon/src/disksup.erl @@ -264,7 +264,7 @@ check_disk_space({unix, irix}, Port, Threshold) -> Result = my_cmd("/usr/sbin/df -lk",Port), check_disks_irix(skip_to_eol(Result), Threshold); check_disk_space({unix, linux}, Port, Threshold) -> - Result = my_cmd("/bin/df -lk", Port), + Result = my_cmd("/bin/df -lk -x squashfs", Port), check_disks_solaris(skip_to_eol(Result), Threshold); check_disk_space({unix, posix}, Port, Threshold) -> Result = my_cmd("df -k -P", Port), diff --git a/lib/public_key/asn1/CMSAesRsaesOaep.asn1 b/lib/public_key/asn1/CMSAesRsaesOaep.asn1 new file mode 100644 index 0000000000..ca8c7b7f92 --- /dev/null +++ b/lib/public_key/asn1/CMSAesRsaesOaep.asn1 @@ -0,0 +1,39 @@ +CMSAesRsaesOaep {iso(1) member-body(2) us(840) rsadsi(113549) + pkcs(1) pkcs-9(9) smime(16) modules(0) id-mod-cms-aes(19) } + + +DEFINITIONS IMPLICIT TAGS ::= +BEGIN + +-- EXPORTS ALL -- +IMPORTS + -- PKIX + AlgorithmIdentifier + FROM PKIX1Explicit88 {iso(1) identified-organization(3) dod(6) + internet(1) security(5) mechanisms(5) pkix(7) id-mod(0) + id-pkix1-explicit(18)}; + +-- AES information object identifiers -- + +aes OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) + organization(1) gov(101) csor(3) nistAlgorithms(4) 1 } + +-- AES using CBC-chaining mode for key sizes of 128, 192, 256 + +id-aes128-CBC OBJECT IDENTIFIER ::= { aes 2 } +id-aes192-CBC OBJECT IDENTIFIER ::= { aes 22 } +id-aes256-CBC OBJECT IDENTIFIER ::= { aes 42 } + +-- AES-IV is a the parameter for all the above object identifiers. + +AES-IV ::= OCTET STRING (SIZE(16)) + + +-- AES Key Wrap Algorithm Identifiers - Parameter is absent + +id-aes128-wrap OBJECT IDENTIFIER ::= { aes 5 } +id-aes192-wrap OBJECT IDENTIFIER ::= { aes 25 } +id-aes256-wrap OBJECT IDENTIFIER ::= { aes 45 } + + +END diff --git a/lib/public_key/asn1/Makefile b/lib/public_key/asn1/Makefile index a920ea87ea..10952106c6 100644 --- a/lib/public_key/asn1/Makefile +++ b/lib/public_key/asn1/Makefile @@ -42,7 +42,7 @@ RELSYSDIR = $(RELEASE_PATH)/lib/public_key-$(VSN) ASN_TOP = OTP-PUB-KEY PKCS-FRAME ASN_MODULES = PKIX1Explicit88 PKIX1Implicit88 PKIX1Algorithms88 \ PKIXAttributeCertificate PKCS-1 PKCS-3 PKCS-7 PKCS-8 PKCS-10 PKCS5v2-0 OTP-PKIX \ - InformationFramework RFC5639 + InformationFramework RFC5639 CMSAesRsaesOaep ASN_ASNS = $(ASN_MODULES:%=%.asn1) ASN_ERLS = $(ASN_TOP:%=%.erl) ASN_HRLS = $(ASN_TOP:%=%.hrl) diff --git a/lib/public_key/asn1/OTP-PUB-KEY.set.asn b/lib/public_key/asn1/OTP-PUB-KEY.set.asn index b3f3ccdb77..7ab1684ff3 100644 --- a/lib/public_key/asn1/OTP-PUB-KEY.set.asn +++ b/lib/public_key/asn1/OTP-PUB-KEY.set.asn @@ -10,3 +10,5 @@ ECPrivateKey.asn1 PKCS-7.asn1 PKCS-10.asn1 RFC5639.asn1 +CMSAesRsaesOaep.asn1 + diff --git a/lib/public_key/doc/src/notes.xml b/lib/public_key/doc/src/notes.xml index d13c9a520a..57d9898661 100644 --- a/lib/public_key/doc/src/notes.xml +++ b/lib/public_key/doc/src/notes.xml @@ -87,6 +87,21 @@ </section> +<section><title>Public_Key 1.6.6.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Support Pasword based encryption with AES</p> + <p> + Own Id: OTP-15870 Aux Id: ERL-952 </p> + </item> + </list> + </section> + +</section> + <section><title>Public_Key 1.6.6</title> <section><title>Improvements and New Features</title> @@ -1247,4 +1262,3 @@ </chapter> - diff --git a/lib/public_key/doc/src/public_key_app.xml b/lib/public_key/doc/src/public_key_app.xml index 923a9f1dfb..5f2c50711a 100644 --- a/lib/public_key/doc/src/public_key_app.xml +++ b/lib/public_key/doc/src/public_key_app.xml @@ -51,6 +51,9 @@ Diffie-Hellman Key Agreement Standard </item> <item>Supports <url href="http://www.ietf.org/rfc/rfc2898.txt"> PKCS-5</url> - Password-Based Cryptography Standard </item> + <item>Supports <url href="http://www.ietf.org/rfc/fc3565.txt"> AES </url> - + Use of the Advanced Encryption Standard (AES) Algorithm in Cryptographic Message Syntax (CMS) + </item> <item>Supports <url href="http://www.ietf.org/rfc/rfc5208.txt"> PKCS-8</url> - Private-Key Information Syntax Standard</item> <item>Supports <url href="http://www.ietf.org/rfc/rfc5967.txt"> PKCS-10</url> - diff --git a/lib/public_key/src/pubkey_pbe.erl b/lib/public_key/src/pubkey_pbe.erl index e6bcedd1b1..6003bf21d0 100644 --- a/lib/public_key/src/pubkey_pbe.erl +++ b/lib/public_key/src/pubkey_pbe.erl @@ -26,9 +26,7 @@ -export([encode/4, decode/4, decrypt_parameters/1, encrypt_parameters/1]). -export([pbdkdf1/4, pbdkdf2/7]). --define(DEFAULT_SHA_MAC_KEYLEN, 20). -define(ASN1_OCTET_STR_TAG, 4). --define(IV_LEN, 8). %%==================================================================== %% Internal application API @@ -41,14 +39,23 @@ %%-------------------------------------------------------------------- encode(Data, Password, "DES-CBC" = Cipher, KeyDevParams) -> {Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams), - crypto:block_encrypt(des_cbc, Key, IV, pbe_pad(Data, KeyDevParams)); + crypto:block_encrypt(des_cbc, Key, IV, pbe_pad(Data, block_size(des_cbc))); encode(Data, Password, "DES-EDE3-CBC" = Cipher, KeyDevParams) -> {Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams), <<Key1:8/binary, Key2:8/binary, Key3:8/binary>> = Key, - crypto:block_encrypt(des3_cbc, [Key1, Key2, Key3], IV, pbe_pad(Data)); + crypto:block_encrypt(des3_cbc, [Key1, Key2, Key3], IV, pbe_pad(Data, block_size(des_3ede))); encode(Data, Password, "RC2-CBC" = Cipher, KeyDevParams) -> {Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams), - crypto:block_encrypt(rc2_cbc, Key, IV, pbe_pad(Data, KeyDevParams)). + crypto:block_encrypt(rc2_cbc, Key, IV, pbe_pad(Data, block_size(rc2_cbc))); +encode(Data, Password, "AES-128-CBC" = Cipher, KeyDevParams) -> + {Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams), + crypto:block_encrypt(aes_128_cbc, Key, IV, pbe_pad(Data, block_size(aes_128_cbc))); +encode(Data, Password, "AES-192-CBC" = Cipher, KeyDevParams) -> + {Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams), + crypto:block_encrypt(aes_192_cbc, Key, IV, pbe_pad(Data, block_size(aes_192_cbc))); +encode(Data, Password, "AES-256-CBC"= Cipher, KeyDevParams) -> + {Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams), + crypto:block_encrypt(aes_256_cbc, Key, IV, pbe_pad(Data, block_size(aes_256_cbc))). %%-------------------------------------------------------------------- -spec decode(binary(), string(), string(), term()) -> binary(). @@ -67,14 +74,16 @@ decode(Data, Password,"RC2-CBC"= Cipher, KeyDevParams) -> crypto:block_decrypt(rc2_cbc, Key, IV, Data); decode(Data, Password,"AES-128-CBC"= Cipher, KeyDevParams) -> {Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams), - crypto:block_decrypt(aes_cbc128, Key, IV, Data); -decode(Data, Password,"AES-256-CBC"= Cipher, KeyDevParams) -> + crypto:block_decrypt(aes_128_cbc, Key, IV, Data); +decode(Data, Password,"AES-192-CBC"= Cipher, KeyDevParams) -> + {Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams), + crypto:block_decrypt(aes_192_cbc, Key, IV, Data); +decode(Data, Password,"AES-256-CBC"= Cipher, KeyDevParams) -> {Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams), - crypto:block_decrypt(aes_cbc256, Key, IV, Data). - + crypto:block_decrypt(aes_256_cbc, Key, IV, Data). %%-------------------------------------------------------------------- --spec pbdkdf1(string(), iodata(), integer(), atom()) -> binary(). +-spec pbdkdf1(iodata(), iodata(), integer(), atom()) -> binary(). %% %% Description: Implements password based decryption key derive function 1. %% Exported mainly for testing purposes. @@ -86,7 +95,7 @@ pbdkdf1(Password, Salt, Count, Hash) -> do_pbdkdf1(Result, Count-1, Result, Hash). %%-------------------------------------------------------------------- --spec pbdkdf2(string(), iodata(), integer(), integer(), fun(), atom(), integer()) +-spec pbdkdf2(iodata(), iodata(), integer(), integer(), fun(), atom(), integer()) -> binary(). %% %% Description: Implements password based decryption key derive function 2. @@ -150,17 +159,15 @@ do_pbdkdf1(Prev, Count, Acc, Hash) -> Result = crypto:hash(Hash, Prev), do_pbdkdf1(Result, Count-1 , <<Result/binary, Acc/binary>>, Hash). -iv(#'PBES2-params_encryptionScheme'{algorithm = Algo, - parameters = ASN1IV}) - when (Algo == ?'desCBC') or - (Algo == ?'des-EDE3-CBC') -> - <<?ASN1_OCTET_STR_TAG, ?IV_LEN, IV:?IV_LEN/binary>> = decode_handle_open_type_wrapper(ASN1IV), - IV; iv(#'PBES2-params_encryptionScheme'{algorithm = ?'rc2CBC', parameters = ASN1IV}) -> {ok, #'RC2-CBC-Parameter'{iv = IV}} = 'PKCS-FRAME':decode('RC2-CBC-Parameter', decode_handle_open_type_wrapper(ASN1IV)), - iolist_to_binary(IV). + iolist_to_binary(IV); +iv(#'PBES2-params_encryptionScheme'{algorithm = _Algo, + parameters = ASN1IV}) -> + <<?ASN1_OCTET_STR_TAG, Len:8/unsigned-big-integer, IV:Len/binary>> = decode_handle_open_type_wrapper(ASN1IV), + IV. blocks(1, N, Index, Password, Salt, Count, Prf, PrfHash, PrfLen, Acc) -> <<XorSum:N/binary, _/binary>> = xor_sum(Password, Salt, Count, Index, Prf, PrfHash, PrfLen), @@ -217,17 +224,9 @@ pbe1_oid("RC2-CBC", md5) -> pbe1_oid("DES-CBC", md5) -> ?'pbeWithMD5AndDES-CBC'. -pbe_pad(Data, {#'PBEParameter'{}, _}) -> - pbe_pad(Data); -pbe_pad(Data, #'PBES2-params'{}) -> - pbe_pad(Data); -pbe_pad(Data, _) -> -pbe_pad(Data).%% Data. - - -pbe_pad(Data) -> - N = 8 - (erlang:byte_size(Data) rem 8), - Pad = list_to_binary(lists:duplicate(N, N)), +pbe_pad(Data, BlockSize) -> + N = BlockSize - (erlang:byte_size(Data) rem BlockSize), + Pad = binary:copy(<<N>>, N), <<Data/binary, Pad/binary>>. key_derivation_params(#'PBES2-params'{keyDerivationFunc = KeyDerivationFunc, @@ -249,11 +248,27 @@ key_derivation_params(#'PBES2-params'{keyDerivationFunc = KeyDerivationFunc, pseudo_random_function(#'PBKDF2-params_prf'{algorithm = {_,_, _,'id-hmacWithSHA1'}}) -> {fun crypto:hmac/4, sha, pseudo_output_length(?'id-hmacWithSHA1')}; -pseudo_random_function(#'PBKDF2-params_prf'{algorithm = ?'id-hmacWithSHA1'}) -> - {fun crypto:hmac/4, sha, pseudo_output_length(?'id-hmacWithSHA1')}. +pseudo_random_function(#'PBKDF2-params_prf'{algorithm = ?'id-hmacWithSHA1' = Algo}) -> + {fun crypto:hmac/4, sha, pseudo_output_length(Algo)}; +pseudo_random_function(#'PBKDF2-params_prf'{algorithm = ?'id-hmacWithSHA224'= Algo}) -> + {fun crypto:hmac/4, sha224, pseudo_output_length(Algo)}; +pseudo_random_function(#'PBKDF2-params_prf'{algorithm = ?'id-hmacWithSHA256' = Algo}) -> + {fun crypto:hmac/4, sha256, pseudo_output_length(Algo)}; +pseudo_random_function(#'PBKDF2-params_prf'{algorithm = ?'id-hmacWithSHA384' = Algo}) -> + {fun crypto:hmac/4, sha384, pseudo_output_length(Algo)}; +pseudo_random_function(#'PBKDF2-params_prf'{algorithm = ?'id-hmacWithSHA512' = Algo}) -> + {fun crypto:hmac/4, sha512, pseudo_output_length(Algo)}. pseudo_output_length(?'id-hmacWithSHA1') -> - ?DEFAULT_SHA_MAC_KEYLEN. + 20; %%160/8 +pseudo_output_length(?'id-hmacWithSHA224') -> + 28; %%%224/8 +pseudo_output_length(?'id-hmacWithSHA256') -> + 32; %%256/8 +pseudo_output_length(?'id-hmacWithSHA384') -> + 48; %%384/8 +pseudo_output_length(?'id-hmacWithSHA512') -> + 64. %%512/8 derived_key_length(_, Len) when is_integer(Len) -> Len; @@ -266,11 +281,33 @@ derived_key_length(Cipher,_) when (Cipher == ?'rc2CBC') or derived_key_length(Cipher,_) when (Cipher == ?'des-EDE3-CBC') or (Cipher == "DES-EDE3-CBC") -> 24; -derived_key_length(Cipher,_) when (Cipher == "AES-128-CBC") -> + +derived_key_length(Cipher,_) when (Cipher == "AES-128-CBC"); + (Cipher == ?'id-aes128-CBC') -> 16; -derived_key_length(Cipher,_) when (Cipher == "AES-256-CBC") -> +derived_key_length(Cipher,_) when (Cipher == "AES-192-CBC"); + (Cipher == ?'id-aes192-CBC') -> + 24; + +derived_key_length(Cipher,_) when (Cipher == "AES-256-CBC"); + (Cipher == ?'id-aes256-CBC') -> 32. +block_size(Cipher) when Cipher == rc2_cbc; + Cipher == des_cbc; + Cipher == des_3ede -> + 8; +block_size(Cipher) when Cipher == aes_128_cbc; + Cipher == aes_192_cbc; + Cipher == aes_256_cbc -> + 16. + +cipher(#'PBES2-params_encryptionScheme'{algorithm = ?'id-aes128-CBC'}) -> + "AES-128-CBC"; +cipher(#'PBES2-params_encryptionScheme'{algorithm = ?'id-aes192-CBC'}) -> + "AES-192-CBC"; +cipher(#'PBES2-params_encryptionScheme'{algorithm = ?'id-aes256-CBC'}) -> + "AES-256-CBC"; cipher(#'PBES2-params_encryptionScheme'{algorithm = ?'desCBC'}) -> "DES-CBC"; cipher(#'PBES2-params_encryptionScheme'{algorithm = ?'des-EDE3-CBC'}) -> diff --git a/lib/public_key/test/pbe_SUITE.erl b/lib/public_key/test/pbe_SUITE.erl index 1136267411..61db282dfa 100644 --- a/lib/public_key/test/pbe_SUITE.erl +++ b/lib/public_key/test/pbe_SUITE.erl @@ -206,7 +206,10 @@ pbes2() -> [{doc,"Tests encode/decode EncryptedPrivateKeyInfo encrypted with different ciphers using PBES2"}]. pbes2(Config) when is_list(Config) -> decode_encode_key_file("pbes2_des_cbc_enc_key.pem", "password", "DES-CBC", Config), - decode_encode_key_file("pbes2_des_ede3_cbc_enc_key.pem", "password", "DES-EDE3-CBC", Config), + decode_encode_key_file("pbes2_des_ede3_cbc_enc_key.pem", "password", "DES-EDE3-CBC", Config), + decode_encode_key_file("pbes2_aes_128_enc_key.pem", "password", "AES-128-CBC", Config), + decode_encode_key_file("pbes2_aes_192_enc_key.pem", "password", "AES-192-CBC", Config), + decode_encode_key_file("pbes2_aes_256_enc_key.pem", "password", "AES-256-CBC", Config), case lists:member(rc2_cbc, proplists:get_value(ciphers, crypto:supports())) of true -> decode_encode_key_file("pbes2_rc2_cbc_enc_key.pem", "password", "RC2-CBC", Config); @@ -239,7 +242,6 @@ decode_encode_key_file(File, Password, Cipher, Config) -> {ok, PemKey} = file:read_file(filename:join(Datadir, File)), PemEntry = public_key:pem_decode(PemKey), - ct:pal("Pem entry: ~p" , [PemEntry]), [{Asn1Type, _, {Cipher,_} = CipherInfo} = PubEntry] = PemEntry, #'RSAPrivateKey'{} = KeyInfo = public_key:pem_entry_decode(PubEntry, Password), PemKey1 = public_key:pem_encode([public_key:pem_entry_encode(Asn1Type, KeyInfo, {CipherInfo, Password})]), diff --git a/lib/public_key/test/pbe_SUITE_data/pbes2_aes_128_enc_key.pem b/lib/public_key/test/pbe_SUITE_data/pbes2_aes_128_enc_key.pem new file mode 100644 index 0000000000..5702119ad6 --- /dev/null +++ b/lib/public_key/test/pbe_SUITE_data/pbes2_aes_128_enc_key.pem @@ -0,0 +1,30 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFLTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIWrPgmqJqNpICAggA +MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAECBBA/bbIMYqQMUDxMk9ifPR7ABIIE +0Drfqke1/ccFxk786hTh36yjVo48Xx7B3Scb92KtmyQpNaR6GbR+jhP9cxIcvmGN +YroCB896VJSIx8PraqGgIJ1hblZXyfLanB0mUnZvaaQ4xp3UJT53a0yOm5Lfd+fB +0TyaoEzca2jA5EVVh3yH6gzNsvQJRw6cQP5CAptLjiUv2jrwVGnO8x8X4egJDLZS +Sb8B5AW8h1sGsyKEEFto6gpBjVqnVn5veMoI/Cfs9qDr071+dhbps/m6pseKKp0z +8qeFM7+9Y4npD1VYg2gqOFi19QAI3gwq6tC8grOzRA8dPFUgpV9eMToVsI2OFQc1 +xnFZEV7NZVymh5HjKM1jwFy6es+5TFoMtRu6vDxKS6Y13lIlZ4oQSh8aXtG5Ylt2 +CqsKNHyDbZUpvKe/k19TBmVXQBCYFuN733jI9/4JBtpygnxwt1aXCvq/PFFGsTS4 +p1JOQvr/jaD7b4JO6IMXH1kSVxiMXKXNG7wPUNr6OWJvc7OqdclsZa7ibEx4L52x +DuFmsxQo4a3iibhbcjr436OmR5Uw2UAstB5qxWfMhkt+e7rRhCOh/3O7SAYEpt+f +Zr2VFXdGme4kR6uMCzgGiSh0qCseQXpJUZVufn/Go9r+601OJTJIQ9a2VoqlMR8o +Dd14D0gBXXaZkY60Mh8iXR/MjKDuv0KBUyBzfcpk3fLmv0PhGSkbn6j+q1jZbogm +EhI0AL5s2EoofuBdvgdusBhCrrwCMonprqR7BuaKPD0GEw5utnT5ovcUg/sjMJox +10100QwAzQScU4iG/xic/TsN+ZMumhUcYs003MsZkRLvCEFxZurEMx7819CqfhIc +NGd7ETTBSwoNf5pXRTHaTbW6pPiIeWunLUUVsRcNoBtL/cXmg+mu1zdsD7nD51mJ +vG9A7LPW7XVl2Jv2NgQoKkHYO7cVozmcz6AE2z1q+XN4LGto8JEZktb6E7UIyXXg +Ls4Tv0sn5TLgtaJ31w4+9iybNiGoVYOc4h0s5DoNR4ivcZ6n/Qnf8PTrNzejEJY6 +R/UnDbc24u0palGc1kei99d0BYodnq4OlAj7M7ML0GncftInhgA0Dp81YG5PujMa +irhvwtnD5Xysfh1YrroAEN7Qxc8+2JlpgNSFlFFkMgfibc6jvTX6/C6MaFz8hiOq +W43ZBEzjMIs23ZrJKOJGsuTdHSob+VbvqIMgS2PeGb/6g3/GjdipCbynNhX3zUOM +3j/lpZOiAwE/Bftr5FOSfTFpnyorIIeyWgROEZTTL4eSYvnBjzf+tUdXY7ltxJie +q0rpQ42X7+B4gTo8Qj/xC7LXSCldERK57cCwwITvjcHwxPyOiJ9BMI1HlRQ/Fo3C +lPYIst1xjJ67qrTm6mWkor2hUOZcg4MOOzXWuijWRGJ/Wz0H+GKWtoE2X536D6sy +a4Nwwj09oFY4Fph/SUNwy0MLpTSzikpUx6mxjbs3Odvo6tWWVcicp/dCWYCqLpGU +3axEb/qlsaRNtKJg9O3Fq7hh1BTyLNGB2ET5wSKtlSD0bDeF15bBvkHB3z2/lDls +YQ2hEHMjeSEZZyGTPqEHwtBuUwiWBBXwOIhT8nfYXbHWR0CLBLth2+E/JCaO9hD2 +V277arqNFa8nugZMwS+ragi6vbgIX4BiS/rnfYXgqaxD +-----END ENCRYPTED PRIVATE KEY----- diff --git a/lib/public_key/test/pbe_SUITE_data/pbes2_aes_192_enc_key.pem b/lib/public_key/test/pbe_SUITE_data/pbes2_aes_192_enc_key.pem new file mode 100644 index 0000000000..ee82e9f667 --- /dev/null +++ b/lib/public_key/test/pbe_SUITE_data/pbes2_aes_192_enc_key.pem @@ -0,0 +1,30 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFLTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIcqBCM7v+ZlkCAggA +MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEWBBD93r4IWBhvry+cdfwIDOKeBIIE +0DXM8S70sMsUmwxRZQtKwGfYddEWIc9lrEdsgEEuonF6NrseRq7QdXnBSPwq5f0O +ofMZ/0OCun3Qg1ls1EdsyKdijSOq27ZhHCnmWi1Rw1ApJIAq5i/jY8U17+lUakvG +VtcsuRzlKmFxbBW44kLK7vK6xiA76HPx0I4ZXcdywR0pbLT1ubbhbQ9djLnBiYkT +odszGTyxNceEse1Hu/RhFK17tnwov0fdioKY2i9F7qfq8lYLPrusEKTY7tOVjFOh +bXeCry1BL0KTt65JVGR9xQCI0qokEU0QrCgD6skq7Vx2C/Ho1sW6h8FBFVIm6ozO +bEUtVk3Xgs5yieetha1GxJAang1VxAPemnXfOmVapoSgSv1BQyDdnk3067Sfkh64 +A5yf44BUjvJsSd/ViCVmCryoXU7KOMAdFkyRSiDDLQus6bZGEhc6f+VEikG+TZ2L +xxY4OucE2Bz67S6ycyOUpXKo0+FW0juE6NTJdlYSXWOvfciZKA83h6yAej6MfUEu +4orIvnCTVO7i3+hHybnSgftj42jrqqZzeXll8rkGHg4syrKRVaDD6qfJjgAHBJkJ +pZT4zZwuJ1puWfBykI25S4mKUnk0erq4N5jpGqdm7U14fWBWCjZN85jY4WgZZOJx +kBNO2NbmZKzZEzRGyMJ563z4l7MNfzZBHv+FeBNkX146J4ZhMbT8IXPGV9peNWqu +mY2B9RhN4hlDrd3Hfz5uiiF3UGrFkDcsPRBHWGqQ20YpuOQNno7iL8N0FWauERw1 +dvxAGVwFfUznR3wc/eyGcnRhqQhlYPspukh0IVIyEbre3yVFSG/41GQYQfg08XYd +LYiiDUu1i515/GeDvYN5VcnZ4nMhPgqfxW4rEUZjI86p++bqwqGy8eOCivkzGV3A +IFWQwlvKKzU7tSdi3uHUq5v7xQsJrALdf67JVjCCGfUZa17O41vmm58L/vKhhL2Y +mLz/H004DPsB+CtWoLwqZ8Jmb1EHwqNbna3tGHn3n63j2cV7gykZFa/zXeuBbbJ/ +t4ZIojIEzwAVKA9Xzcl3wyGCRr62WJPEcOqe4kBYREuKd22juPEm9RQgciIIj0tP +eJVpD0QarGGzERsaq7pheAiWisO+Q4cLjF8Mb3/r89abnd4AQk6meabFJIE2dXWp +LZy3I6FkNQ7L7LxNOILhnaWzWGdOBVwHeAAxfbLOzM22ewj7oUwBCRpsBJ8zl2PL +VhUjX6N26YoiR9gE1RBaVrwRkYLmkyGvrowCDoZVPxvJqbfIESQE42zGB9DbEPNp +WXCnzAg5cIjNC31We274yLE7dpNPVRXPJCRhtp7noorWVzDdKB+dFvg08bIir6Vj +1gxy8DvuZE1Gq9vqx38V7Cy2MrSpsgapw5mli4n5cMafE7Ty3j5pBJFF2f3jUn6B +7MjCrKp1d8v6MEy18J/Ugu1Lytb92LMcNtWBKmqyCSxekrUB9/FC2hWqOpdwRI6q +QMWkwshjyEhmlr2PAkBPM4uVzUFc9lBw1GzOUChkr9jiINdbsUSRJrwZ32Nc3gRY +yKzWbEELPSgRcXwXgH3QqZukvmk2tBMTIxilXqKTLmd7t/AEnIhkbqC0pfnyChyU +YlFkme0RpAXpgbDJgv+Vk+1/1s6gyaNSzT4s2Q340WIO +-----END ENCRYPTED PRIVATE KEY----- diff --git a/lib/public_key/test/pbe_SUITE_data/pbes2_aes_256_enc_key.pem b/lib/public_key/test/pbe_SUITE_data/pbes2_aes_256_enc_key.pem new file mode 100644 index 0000000000..050337aead --- /dev/null +++ b/lib/public_key/test/pbe_SUITE_data/pbes2_aes_256_enc_key.pem @@ -0,0 +1,30 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFLTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQI4MxgpDiHxQcCAggA +MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBA2g/L8XmlK2axDkeYJCltnBIIE +0C3+NQ93DzEK/9qicy1sj0Vag1M7AeJjTGGpatETCxM+eHjk4kNNeDeMV5+EmCSu +Db4P48uvHOBGGCcqdjnQovfQsAh81GWxgF3yqpd4OKn2RubMLO4/Qu+zGtt/XRKz +T0pyHHBu6hyPSOhad2SIjKWuaHepwxGYaejLP83sy6yhm0sEmyBUn4nGSTOROcqR +wd7EbwU2PYUcrRGGxtChU7MUNt48wBO50Xmri1ssPPtZV6MHio4IoIz4hqzCjvAc +VE1BqAvNIJ7icpdnL8Jqq0lfwEmGjFCkAjgov5fNW9I1b44jE2Tv5LM2urMH8InQ +9qNjTHozYQhHAk9nX4cmMgHsIhkOd7Z2M+nz8Hd1tj9DmBNOr5XbfyctgVntaMB4 +GGnThuNlX8d5giOKOcaNPMpLU1jtfDcb73mEhwCYcdo1PM0rjrYZ7qetjXJW/oHs +Nl/hIZIRpMuCRVuXHml4G+ziKbMnXUN8sbtvgkQatYFHFQOhAqZeyzWp8SlDcfqb +Zt0LlZVJEhKUYzZgKoe7SmR1rXTTCfYeB75PddyYwVgf/IkT6HJ/y1apGOP6/UJ8 +7UV6zssQA35gMsYDT36sH2hAQvA/cOFxSxrip0gm0xXOeFF0gbyZWbFqk0aULaeF +rbBoMe28akxdE4eD06b+TP2NguUGP72l3TPOlG4PQVScweMw9L3oPXOVj4Vbbd0y +DenNvRHlWIwOh/y7ADTHSWq9CE45QDBvFaTcn43JQWD8xCmhAhI/9H+fhAQUhABm +P5QoJLE2IGo8A+Gi7rfgYQb3fCgqcn8azsRJzozhE+oXxMvxEESejYTtm26FNmLg +ONTWysF9BiaKHt2IXwRX97691wZqv5wJEaxeeJxfVQ6MlAHoEDXe49VxGN4zFXuq +Yb71JdQDgM94jwc/PoUwFH2ALSkIciiKwU0xfFpptycl4qWpy9m7QTIKw0DjgCfg +MuySPRGM5jn3yVg72ux2Qf9MKNEybWjZ+Se9MJ1IZmZK5eOo6L2JsFCc0nRn908E +vn4gAgUfMxyCZ1ygXfxINVAixR+6KPHsz1QTIxTZkrlnXRsuEu1ZfBSHzmXESvJo +3I9PkP/Iekg1FBpB5xxd7mXwCj17EWqYXWsLnfd8SblMjRYd64q7hfx0oU/MJ1wi +KadkGcyAGVRyleJRBR0LleYj/2sDihrRQY4zu5UtzSMFMH0XWjSWk5+ZQb+z3iDc +Ud4GHcHiuTMH+i03ApZGWLN9v93za/15fsnZogstgJkaHxizTz5JuCkRf15xd8+O +EH77Tsfizjp+h2NF/wcr4OSD0i+H0mwZWajpZ3UmSeJ0BFK6ODEbmVycrInpHo3n +zyMJnEDTJXL3HUwZSLjO5e5cNaB+75tdHrj2yJtRLuaJFr02b0EO1MUYfuUuqlK4 +7mg7FkBsimW+CXkoLRjHYK88ibT3G+rZ/STf4S/jxiRjBi06FAql3H02K5i1umgB +0BaaQei0Z8wQxMeTEnGzL+OcJeqDA1ZRFeXe7DNGsX1jeTYKPHA/Dr2IdZqyiCr2 +xh6e7RJuUe4D2liXW8LlMdwhN/7xSinA031PgBmb8XzSRmfdHhytFkA8PiM5T2ew +NR3qXBJ/G7BuRa/t26RuKI3BMVoBQPhGx80ds10uJjxq +-----END ENCRYPTED PRIVATE KEY----- diff --git a/lib/snmp/src/agent/snmp_community_mib.erl b/lib/snmp/src/agent/snmp_community_mib.erl index 9fd7b30f9f..984b0bcee1 100644 --- a/lib/snmp/src/agent/snmp_community_mib.erl +++ b/lib/snmp/src/agent/snmp_community_mib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2016. All Rights Reserved. +%% Copyright Ericsson AB 1999-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -206,10 +206,10 @@ do_add_community(Community) -> {error, create_failed} end catch - {error, Reason} -> - {error, Reason}; - Class:Reason -> - {error, {Class, Reason, erlang:get_stacktrace()}} + throw:{error, _} = ERROR -> + ERROR; + C:E:S -> + {error, {C, E, S}} end. %% FIXME: does not work with mnesia diff --git a/lib/snmp/src/agent/snmp_generic.erl b/lib/snmp/src/agent/snmp_generic.erl index e67a1b3c80..26a0dd0648 100644 --- a/lib/snmp/src/agent/snmp_generic.erl +++ b/lib/snmp/src/agent/snmp_generic.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2017. All Rights Reserved. +%% Copyright Ericsson AB 1996-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -421,12 +421,12 @@ table_check_status(NameDb, Col, ?'RowStatus_createAndGo', RowIndex, Cols) -> _Found -> {inconsistentValue, Col} end catch - _:_Reason -> + _:_E:_S -> ?vtrace( "failed construct row (createAndGo): " - " n Reason: ~p" - " n Stack: ~p", - [_Reason, erlang:get_stacktrace()]), + " n Error: ~p" + " n Stack: ~p", + [_E, _S]), {noCreation, Col} % Bad RowIndex end; true -> {inconsistentValue, Col} @@ -441,12 +441,12 @@ table_check_status(NameDb, Col, ?'RowStatus_createAndWait', RowIndex, Cols) -> _Row -> {noError, 0} catch - _:_Reason -> + _:_E:_S -> ?vtrace( "failed construct row (createAndWait): " - " n Reason: ~p" - " n Stack: ~p", - [_Reason, erlang:get_stacktrace()]), + " n Error: ~p" + " n Stack: ~p", + [_E, _S]), {noCreation, Col} % Bad RowIndex end; true -> {inconsistentValue, Col} diff --git a/lib/snmp/src/agent/snmp_standard_mib.erl b/lib/snmp/src/agent/snmp_standard_mib.erl index bfe471178d..679d2657c6 100644 --- a/lib/snmp/src/agent/snmp_standard_mib.erl +++ b/lib/snmp/src/agent/snmp_standard_mib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2015. All Rights Reserved. +%% Copyright Ericsson AB 1996-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -27,6 +27,7 @@ -include("snmp_types.hrl"). -include("STANDARD-MIB.hrl"). +-include("snmpa_internal.hrl"). -define(VMODULE,"STANDARD-MIB"). -include("snmp_verbosity.hrl"). @@ -547,10 +548,12 @@ dummy(_Op) -> ok. %%----------------------------------------------------------------- snmp_set_serial_no(new) -> snmp_generic:variable_func(new, {snmpSetSerialNo, volatile}), - random:seed(erlang:phash2([node()]), - erlang:monotonic_time(), - erlang:unique_integer()), - Val = random:uniform(2147483648) - 1, + ?SNMP_RAND_SEED(), + %% rand:seed(exrop, + %% {erlang:phash2([node()]), + %% erlang:monotonic_time(), + %% erlang:unique_integer()}), + Val = rand:uniform(2147483648) - 1, snmp_generic:variable_func(set, Val, {snmpSetSerialNo, volatile}); snmp_set_serial_no(delete) -> diff --git a/lib/snmp/src/agent/snmp_target_mib.erl b/lib/snmp/src/agent/snmp_target_mib.erl index e65fa7f340..22fd3acb84 100644 --- a/lib/snmp/src/agent/snmp_target_mib.erl +++ b/lib/snmp/src/agent/snmp_target_mib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2015. All Rights Reserved. +%% Copyright Ericsson AB 1998-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -42,6 +42,7 @@ -define(VMODULE,"TARGET-MIB"). -include("snmp_verbosity.hrl"). +-include("snmpa_internal.hrl"). %% Column not accessible via SNMP - needed when the agent sends informs @@ -673,10 +674,12 @@ snmpTargetSpinLock(print) -> snmpTargetSpinLock(new) -> snmp_generic:variable_func(new, {snmpTargetSpinLock, volatile}), - random:seed(erlang:phash2([node()]), - erlang:monotonic_time(), - erlang:unique_integer()), - Val = random:uniform(2147483648) - 1, + ?SNMP_RAND_SEED(), + %% rand:seed(exrop, + %% {erlang:phash2([node()]), + %% erlang:monotonic_time(), + %% erlang:unique_integer()}), + Val = rand:uniform(2147483648) - 1, snmp_generic:variable_func(set, Val, {snmpTargetSpinLock, volatile}); snmpTargetSpinLock(delete) -> diff --git a/lib/snmp/src/agent/snmp_user_based_sm_mib.erl b/lib/snmp/src/agent/snmp_user_based_sm_mib.erl index f6e4fd3951..4842669fa4 100644 --- a/lib/snmp/src/agent/snmp_user_based_sm_mib.erl +++ b/lib/snmp/src/agent/snmp_user_based_sm_mib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2015. All Rights Reserved. +%% Copyright Ericsson AB 1999-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -440,10 +440,12 @@ usmUserSpinLock(print) -> usmUserSpinLock(new) -> snmp_generic:variable_func(new, {usmUserSpinLock, volatile}), - random:seed(erlang:phash2([node()]), - erlang:monotonic_time(), - erlang:unique_integer()), - Val = random:uniform(2147483648) - 1, + ?SNMP_RAND_SEED(), + %% rand:seed(exrop, + %% {erlang:phash2([node()]), + %% erlang:monotonic_time(), + %% erlang:unique_integer()}), + Val = rand:uniform(2147483648) - 1, snmp_generic:variable_func(set, Val, {usmUserSpinLock, volatile}); usmUserSpinLock(delete) -> diff --git a/lib/snmp/src/agent/snmp_view_based_acm_mib.erl b/lib/snmp/src/agent/snmp_view_based_acm_mib.erl index c6eeb7cea2..56b5d96142 100644 --- a/lib/snmp/src/agent/snmp_view_based_acm_mib.erl +++ b/lib/snmp/src/agent/snmp_view_based_acm_mib.erl @@ -48,6 +48,7 @@ -include("SNMPv2-TC.hrl"). -include("SNMP-VIEW-BASED-ACM-MIB.hrl"). -include("snmpa_vacm.hrl"). +-include("snmpa_internal.hrl"). -define(VMODULE,"VACM-MIB"). @@ -860,10 +861,12 @@ vacmViewSpinLock(print) -> vacmViewSpinLock(new) -> snmp_generic:variable_func(new, volatile_db(vacmViewSpinLock)), - random:seed(erlang:phash2([node()]), - erlang:monotonic_time(), - erlang:unique_integer()), - Val = random:uniform(2147483648) - 1, + ?SNMP_RAND_SEED(), + %% rand:seed(exrop, + %% {erlang:phash2([node()]), + %% erlang:monotonic_time(), + %% erlang:unique_integer()}), + Val = rand:uniform(2147483648) - 1, snmp_generic:variable_func(set, Val, volatile_db(vacmViewSpinLock)); vacmViewSpinLock(delete) -> diff --git a/lib/snmp/src/agent/snmpa_mpd.erl b/lib/snmp/src/agent/snmpa_mpd.erl index b440d57d03..2ec5dcb5e6 100644 --- a/lib/snmp/src/agent/snmpa_mpd.erl +++ b/lib/snmp/src/agent/snmpa_mpd.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2015. All Rights Reserved. +%% Copyright Ericsson AB 1997-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -76,11 +76,9 @@ init(Vsns) -> ?vlog("init -> entry with" "~n Vsns: ~p", [Vsns]), - random:seed(erlang:phash2([node()]), - erlang:monotonic_time(), - erlang:unique_integer()), - ets:insert(snmp_agent_table, {msg_id, random:uniform(2147483647)}), - ets:insert(snmp_agent_table, {req_id, random:uniform(2147483647)}), + ?SNMP_RAND_SEED(), + ets:insert(snmp_agent_table, {msg_id, rand:uniform(2147483647)}), + ets:insert(snmp_agent_table, {req_id, rand:uniform(2147483647)}), init_counters(), init_versions(Vsns, #state{}). diff --git a/lib/snmp/src/agent/snmpa_set.erl b/lib/snmp/src/agent/snmpa_set.erl index 9833d6fdcc..b3a3bf0ab0 100644 --- a/lib/snmp/src/agent/snmpa_set.erl +++ b/lib/snmp/src/agent/snmpa_set.erl @@ -163,10 +163,14 @@ set_phase_two(MyVarbinds, SubagentVarbinds) -> [MyVarbinds, SubagentVarbinds]), case snmpa_set_lib:try_set(MyVarbinds) of {noError, 0} -> + ?vtrace("set phase two: (local) varbinds set ok", []), set_phase_two_subagents(SubagentVarbinds); - {ErrorStatus, Index} -> + {ErrorStatus, ErrorIndex} -> + ?vlog("set phase two: (local) varbinds set failed" + "~n ErrorStatus: ~p" + "~n ErrorIndex: ~p", [ErrorStatus, ErrorIndex]), set_phase_two_undo_subagents(SubagentVarbinds), - {ErrorStatus, Index} + {ErrorStatus, ErrorIndex} end. %%----------------------------------------------------------------- @@ -188,6 +192,7 @@ set_phase_two_subagents([{SubAgentPid, SAVbs} | SubagentVarbinds]) -> {_SAOids, Vbs} = sa_split(SAVbs), case catch snmpa_agent:subagent_set(SubAgentPid, [phase_two, set, Vbs]) of {noError, 0} -> + ?vtrace("set phase two: subagent ~p varbinds set ok", [SubAgentPid]), set_phase_two_subagents(SubagentVarbinds); {'EXIT', Reason} -> user_err("Lost contact with subagent (set)~n~w. Using genErr", @@ -195,10 +200,14 @@ set_phase_two_subagents([{SubAgentPid, SAVbs} | SubagentVarbinds]) -> set_phase_two_undo_subagents(SubagentVarbinds), {genErr, 0}; {ErrorStatus, ErrorIndex} -> + ?vlog("set phase two: subagent ~p varbinds set failed" + "~n ErrorStatus: ~p" + "~n ErrorIndex: ~p", [SubAgentPid, ErrorStatus, ErrorIndex]), set_phase_two_undo_subagents(SubagentVarbinds), {ErrorStatus, ErrorIndex} end; set_phase_two_subagents([]) -> + ?vtrace("set phase two: subagent(s) set ok", []), {noError, 0}. %%----------------------------------------------------------------- diff --git a/lib/snmp/src/agent/snmpa_trap.erl b/lib/snmp/src/agent/snmpa_trap.erl index d04b6a206e..f741c3aaa9 100644 --- a/lib/snmp/src/agent/snmpa_trap.erl +++ b/lib/snmp/src/agent/snmpa_trap.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% Copyright Ericsson AB 1996-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -364,13 +364,14 @@ send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs, LocalEngineID, LocalEngineID, ExtraInfo, NetIf) end catch - T:E -> - Info = [{args, [TrapRec, NotifyName, ContextName, - Recv, Vbs, LocalEngineID, ExtraInfo, NetIf]}, - {tag, T}, - {err, E}, - {stacktrace, erlang:get_stacktrace()}], - ?vlog("snmpa_trap:send_trap exception: ~p", [Info]), + C:E:S -> + Info = [{args, [TrapRec, NotifyName, ContextName, + Recv, Vbs, LocalEngineID, ExtraInfo, NetIf]}, + {class, C}, + {err, E}, + {stacktrace, S}], + ?vlog("snmpa_trap:send_trap exception: " + "~n ~p", [Info]), {error, {failed_sending_trap, Info}} end. diff --git a/lib/snmp/src/agent/snmpa_usm.erl b/lib/snmp/src/agent/snmpa_usm.erl index fb616cd9ef..1debceae98 100644 --- a/lib/snmp/src/agent/snmpa_usm.erl +++ b/lib/snmp/src/agent/snmpa_usm.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2015. All Rights Reserved. +%% Copyright Ericsson AB 1999-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -646,10 +646,12 @@ get_des_salt() -> ets:insert(snmp_agent_table, {usm_des_salt, 0}), 0; _ -> % it doesn't exist, initialize - random:seed(erlang:phash2([node()]), - erlang:monotonic_time(), - erlang:unique_integer()), - R = random:uniform(4294967295), + ?SNMP_RAND_SEED(), + %% rand:seed(exrop, + %% {erlang:phash2([node()]), + %% erlang:monotonic_time(), + %% erlang:unique_integer()}), + R = rand:uniform(4294967295), ets:insert(snmp_agent_table, {usm_des_salt, R}), R end, @@ -679,10 +681,12 @@ get_aes_salt() -> ets:insert(snmp_agent_table, {usm_aes_salt, 0}), 0; _ -> % it doesn't exist, initialize - random:seed(erlang:phash2([node()]), - erlang:monotonic_time(), - erlang:unique_integer()), - R = random:uniform(36893488147419103231), + ?SNMP_RAND_SEED(), + %% rand:seed(exrop, + %% {erlang:phash2([node()]), + %% erlang:monotonic_time(), + %% erlang:unique_integer()}), + R = rand:uniform(36893488147419103231), ets:insert(snmp_agent_table, {usm_aes_salt, R}), R end, diff --git a/lib/snmp/src/app/snmp_internal.hrl b/lib/snmp/src/app/snmp_internal.hrl index 374767df15..f9a758ab7b 100644 --- a/lib/snmp/src/app/snmp_internal.hrl +++ b/lib/snmp/src/app/snmp_internal.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2016. All Rights Reserved. +%% Copyright Ericsson AB 2006-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -25,7 +25,12 @@ -define(APPLICATION, snmp). -endif. --define(STACK(), erlang:get_stacktrace()). + +-define(SNMP_RAND_SEED_ALG, exrop). +-define(SNMP_RAND_SEED(), rand:seed(?SNMP_RAND_SEED_ALG, + {erlang:phash2([node()]), + erlang:monotonic_time(), + erlang:unique_integer()})). -define(snmp_info(C, F, A), ?snmp_msg(info_msg, C, F, A)). -define(snmp_warning(C, F, A), ?snmp_msg(warning_msg, C, F, A)). @@ -39,5 +44,3 @@ -endif. % -ifdef(snmp_internal). - - diff --git a/lib/snmp/src/compile/Makefile b/lib/snmp/src/compile/Makefile index 4093ffa9ca..d9678669a5 100644 --- a/lib/snmp/src/compile/Makefile +++ b/lib/snmp/src/compile/Makefile @@ -2,7 +2,7 @@ # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2016. All Rights Reserved. +# Copyright Ericsson AB 1997-2019. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -59,6 +59,8 @@ PARSER_TARGET = $(PARSER_MODULE).$(EMULATOR) # ---------------------------------------------------- # FLAGS # ---------------------------------------------------- +ERL_COMPILE_FLAGS += -pa $(ERL_TOP)/lib/snmp/ebin + ifeq ($(WARN_UNUSED_VARS),true) ERL_COMPILE_FLAGS += +warn_unused_vars endif diff --git a/lib/snmp/src/compile/snmpc.erl b/lib/snmp/src/compile/snmpc.erl index c810bfcd41..4249799195 100644 --- a/lib/snmp/src/compile/snmpc.erl +++ b/lib/snmp/src/compile/snmpc.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2018. All Rights Reserved. +%% Copyright Ericsson AB 1997-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -31,6 +31,7 @@ -export([init/3]). -include_lib("stdlib/include/erl_compile.hrl"). +-include_lib("snmp/src/app/snmp_internal.hrl"). -include("snmp_types.hrl"). -include("snmpc.hrl"). -include("snmpc_lib.hrl"). @@ -413,9 +414,11 @@ get_verbosity(Options) -> %%---------------------------------------------------------------------- init(From, MibFileName, Options) -> - random:seed(erlang:phash2([node()]), - erlang:monotonic_time(), - erlang:unique_integer()), + ?SNMP_RAND_SEED(), + %% rand:seed(exrop, + %% {erlang:phash2([node()]), + %% erlang:monotonic_time(), + %% erlang:unique_integer()}), put(options, Options), put(verbosity, get_verbosity(Options)), put(description, get_description(Options)), diff --git a/lib/snmp/src/manager/snmpm_config.erl b/lib/snmp/src/manager/snmpm_config.erl index 118cdcd1df..cd9fecd4d4 100644 --- a/lib/snmp/src/manager/snmpm_config.erl +++ b/lib/snmp/src/manager/snmpm_config.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2016. All Rights Reserved. +%% Copyright Ericsson AB 2004-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -479,10 +479,7 @@ agent_info(Domain, Address, Item) when is_atom(Domain) -> NAddress -> do_agent_info(Domain, NAddress, Item) catch - _Thrown -> - %% p(?MODULE_STRING":agent_info(~p, ~p, ~p) throwed ~p at.~n" - %% " ~p", - %% [Domain, Address, Item, _Thrown, erlang:get_stacktrace()]), + _C:_E:_S -> {error, not_found} end; agent_info(Ip, Port, Item) when is_integer(Port) -> @@ -493,10 +490,7 @@ agent_info(Ip, Port, Item) when is_integer(Port) -> Address -> do_agent_info(Domain, Address, Item) catch - _Thrown -> - %% p(?MODULE_STRING":agent_info(~p, ~p, ~p) throwed ~p at.~n" - %% " ~p", - %% [Ip, Port, Item, _Thrown, erlang:get_stacktrace()]), + _C:_E:_S -> {error, not_found} end. @@ -1688,9 +1682,10 @@ read_agents_config_file(Dir) -> Check = fun check_agent_config/2, try read_file(Dir, "agents.conf", Order, Check, []) catch - throw:Error -> - ?vlog("agent config error: ~p", [Error]), - erlang:raise(throw, Error, erlang:get_stacktrace()) + throw:E:S -> + ?vlog("agent config error: " + "~n ~p", [E]), + erlang:raise(throw, E, S) end. check_agent_config(Agent, State) -> @@ -1935,9 +1930,10 @@ read_users_config_file(Dir) -> Check = fun (User, State) -> {check_user_config(User), State} end, try read_file(Dir, "users.conf", Order, Check, []) catch - throw:Error -> - ?vlog("failure reading users config file: ~n ~p", [Error]), - erlang:raise(throw, Error, erlang:get_stacktrace()) + throw:E:S -> + ?vlog("failure reading users config file: " + "~n ~p", [E]), + erlang:raise(throw, E, S) end. check_user_config({Id, Mod, Data}) -> @@ -2351,10 +2347,11 @@ read_file(Dir, FileName, Order, Check, Default) -> read_file(Dir, FileName, Order, Check) -> try snmp_conf:read(filename:join(Dir, FileName), Order, Check) catch - throw:{error, Reason} = Error + throw:{error, Reason} = E:S when element(1, Reason) =:= failed_open -> - error_msg("failed reading config from ~s: ~p", [FileName, Reason]), - erlang:raise(throw, Error, erlang:get_stacktrace()) + error_msg("failed reading config from ~s: " + "~n ~p", [FileName, Reason]), + erlang:raise(throw, E, S) end. %%-------------------------------------------------------------------- diff --git a/lib/snmp/src/manager/snmpm_mpd.erl b/lib/snmp/src/manager/snmpm_mpd.erl index 191dc2c281..8d0a7918a6 100644 --- a/lib/snmp/src/manager/snmpm_mpd.erl +++ b/lib/snmp/src/manager/snmpm_mpd.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2015. All Rights Reserved. +%% Copyright Ericsson AB 2004-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -68,11 +68,13 @@ %%%----------------------------------------------------------------- init(Vsns) -> ?vdebug("init -> entry with ~p", [Vsns]), - random:seed(erlang:phash2([node()]), - erlang:monotonic_time(), - erlang:unique_integer()), - snmpm_config:cre_counter(msg_id, random:uniform(2147483647)), - snmpm_config:cre_counter(req_id, random:uniform(2147483647)), + ?SNMP_RAND_SEED(), + %% rand:seed(exrop, + %% {erlang:phash2([node()]), + %% erlang:monotonic_time(), + %% erlang:unique_integer()}), + snmpm_config:cre_counter(msg_id, rand:uniform(2147483647)), + snmpm_config:cre_counter(req_id, rand:uniform(2147483647)), init_counters(), State = init_versions(Vsns, #state{}), init_usm(State#state.v3), diff --git a/lib/snmp/src/manager/snmpm_net_if.erl b/lib/snmp/src/manager/snmpm_net_if.erl index 29216f9d6a..184f782860 100644 --- a/lib/snmp/src/manager/snmpm_net_if.erl +++ b/lib/snmp/src/manager/snmpm_net_if.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2017. All Rights Reserved. +%% Copyright Ericsson AB 2004-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -182,11 +182,9 @@ worker(Worker, Failer, #state{log = Log} = State) -> %% Winds up in handle_info {'DOWN', ...} erlang:exit({net_if_worker, Result}) catch - Class:Reason -> + C:E:S -> %% Winds up in handle_info {'DOWN', ...} - erlang:exit( - {net_if_worker, Failer, - Class, Reason, erlang:get_stacktrace()}) + erlang:exit({net_if_worker, Failer, C, E, S}) end end, [monitor]). @@ -983,11 +981,10 @@ udp_send(Sock, To, Msg) -> error_msg("failed sending message to ~p:~p:~n" " ~p",[IpAddr, IpPort, Reason]) catch - error:Error -> - error_msg("failed sending message to ~p:~p:~n" - " error:~p~n" - " ~p", - [IpAddr, IpPort, Error, erlang:get_stacktrace()]) + error:E:S -> + error_msg("failed sending message to ~p:~p:" + "~n ~p" + "~n ~p", [IpAddr, IpPort, E, S]) end. sz(B) when is_binary(B) -> diff --git a/lib/snmp/src/manager/snmpm_server.erl b/lib/snmp/src/manager/snmpm_server.erl index c8d7fa1e8b..a6ca2b2b14 100644 --- a/lib/snmp/src/manager/snmpm_server.erl +++ b/lib/snmp/src/manager/snmpm_server.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2015. All Rights Reserved. +%% Copyright Ericsson AB 2004-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -1755,9 +1755,10 @@ handle_error(_UserId, Mod, Reason, ReqId, Data, _State) -> Mod:handle_error(ReqId, Reason, Data) end catch - T:E -> + C:E:S -> CallbackArgs = [ReqId, Reason, Data], - handle_invalid_result(handle_error, CallbackArgs, T, E) + handle_invalid_result(handle_error, CallbackArgs, + C, E, S) end end, handle_callback(F), @@ -1948,9 +1949,10 @@ handle_pdu( Mod:handle_pdu(TargetName, ReqId, SnmpResponse, Data) end catch - T:E -> + C:E:S -> CallbackArgs = [TargetName, ReqId, SnmpResponse, Data], - handle_invalid_result(handle_pdu, CallbackArgs, T, E) + handle_invalid_result(handle_pdu, CallbackArgs, + C, E, S) end end, handle_callback(F), @@ -2119,10 +2121,10 @@ do_handle_agent(DefUserId, DefMod, "<~p,~p>: ~n~w", [Type, Domain, Addr, SnmpInfo]) end; - T:E -> + C:E:S -> CallbackArgs = [Domain_or_Ip, Addr_or_Port, Type, SnmpInfo, DefData], - handle_invalid_result(handle_agent, CallbackArgs, T, E) + handle_invalid_result(handle_agent, CallbackArgs, C, E, S) end. @@ -2331,8 +2333,8 @@ do_handle_trap( handle_invalid_result(handle_trap, CallbackArgs, InvalidResult) catch - T:E -> - handle_invalid_result(handle_trap, CallbackArgs, T, E) + C:E:S -> + handle_invalid_result(handle_trap, CallbackArgs, C, E, S) end. @@ -2523,8 +2525,8 @@ do_handle_inform( reply catch - T:E -> - handle_invalid_result(handle_inform, CallbackArgs, T, E), + C:E:S -> + handle_invalid_result(handle_inform, CallbackArgs, C, E, S), reply end, @@ -2837,8 +2839,8 @@ do_handle_report( reply catch - T:E -> - handle_invalid_result(handle_report, CallbackArgs, T, E), + C:E:S -> + handle_invalid_result(handle_report, CallbackArgs, C, E, S), reply end. @@ -2855,15 +2857,14 @@ handle_callback(F) -> -handle_invalid_result(Func, Args, T, E) -> - Stacktrace = ?STACK(), +handle_invalid_result(Func, Args, C, E, S) -> error_msg("Callback function failed: " "~n Function: ~p" "~n Args: ~p" - "~n Error Type: ~p" + "~n Class: ~p" "~n Error: ~p" "~n Stacktrace: ~p", - [Func, Args, T, E, Stacktrace]). + [Func, Args, C, E, S]). handle_invalid_result(Func, Args, InvalidResult) -> error_msg("Callback function returned invalid result: " diff --git a/lib/snmp/src/misc/snmp_conf.erl b/lib/snmp/src/misc/snmp_conf.erl index 513616a285..d73291764d 100644 --- a/lib/snmp/src/misc/snmp_conf.erl +++ b/lib/snmp/src/misc/snmp_conf.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% Copyright Ericsson AB 1996-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -236,15 +236,16 @@ read_check(File, Check, [{StartLine, Row, EndLine}|Lines], State, Res) -> " NewRow: ~p~n", [NewRow]), read_check(File, Check, Lines, NewState, [NewRow | Res]) catch - {error, Reason} -> - ?vtrace("read_check -> error:~n" - " Reason: ~p", [Reason]), + throw:{error, Reason} -> + ?vtrace("read_check -> error:" + "~n Reason: ~p", [Reason]), error({failed_check, File, StartLine, EndLine, Reason}); - Class:Reason -> - Error = {Class,Reason,erlang:get_stacktrace()}, - ?vtrace("read_check -> failure:~n" - " Error: ~p", [Error]), - error({failed_check, File, StartLine, EndLine, Error}) + C:E:S -> + ?vtrace("read_check -> failure:" + "~n Class: ~p" + "~n Error: ~p" + "~n Stack: ~p", [C, E, S]), + error({failed_check, File, StartLine, EndLine, {C, E, S}}) end. open_file(File) -> diff --git a/lib/snmp/src/misc/snmp_config.erl b/lib/snmp/src/misc/snmp_config.erl index 45661b71a7..26e85897f4 100644 --- a/lib/snmp/src/misc/snmp_config.erl +++ b/lib/snmp/src/misc/snmp_config.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% Copyright Ericsson AB 1996-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -2573,15 +2573,17 @@ write_config_file(Dir, FileName, Order, Check, Write, Entries) Error end catch - Error -> - S = erlang:get_stacktrace(), - d("File write of ~s throwed: ~p~n ~p~n", - [FileName, Error, S]), - Error; - C:E -> - S = erlang:get_stacktrace(), - d("File write of ~s exception: ~p:~p~n ~p~n", - [FileName,C,E,S]), + throw:E:S -> + d("File write of ~s throwed: " + "~n ~p" + "~n ~p" + "~n", [FileName, E, S]), + E; + C:E:S -> + d("File write of ~s exception: " + "~n ~p:~p" + "~n ~p" + "~n", [FileName, C, E, S]), {error, {failed_write, Dir, FileName, {C, E, S}}} end. @@ -2590,16 +2592,18 @@ write_config_file(Dir, FileName, Write, Entries, Fd) -> ok -> close_config_file(Dir, FileName, Fd) catch - Error -> - S = erlang:get_stacktrace(), - d("File write of ~s throwed: ~p~n ~p~n", - [FileName, Error, S]), + throw:E:S -> + d("File write of ~s throwed: " + "~n ~p" + "~n ~p" + "~n", [FileName, E, S]), close_config_file(Dir, FileName, Fd), - Error; - C:E -> - S = erlang:get_stacktrace(), - d("File write of ~s exception: ~p:~p~n ~p~n", - [FileName,C,E,S]), + E; + C:E:S -> + d("File write of ~s exception: " + "~n ~p:~p" + "~n ~p" + "~n", [FileName, C, E, S]), close_config_file(Dir, FileName, Fd), {error, {failed_write, Dir, FileName, {C, E, S}}} end. @@ -2661,16 +2665,18 @@ append_config_file(Dir, FileName, Order, Check, Write, Entries, Fd) -> ok -> close_config_file(Dir, FileName, Fd) catch - Error -> - S = erlang:get_stacktrace(), - d("File append of ~s throwed: ~p~n ~p~n", - [FileName, Error, S]), + throw:E:S -> + d("File append of ~s throwed: " + "~n ~p" + "~n ~p" + "~n", [FileName, E, S]), close_config_file(Dir, FileName, Fd), - Error; - C:E -> - S = erlang:get_stacktrace(), - d("File append of ~s exception: ~p:~p~n ~p~n", - [FileName,C,E,S]), + E; + C:E:S -> + d("File append of ~s exception: " + "~n ~p:~p" + "~n ~p" + "~n", [FileName, C, E, S]), close_config_file(Dir, FileName, Fd), {error, {failed_append, Dir, FileName, {C, E, S}}} end. @@ -2702,16 +2708,18 @@ read_config_file(Dir, FileName, Order, Check) SortedLines = sort_lines(Lines, Order), {ok, verify_lines(SortedLines, Check, undefined, [])} catch - Error -> - S = erlang:get_stacktrace(), - d("File read of ~s throwed: ~p~n ~p~n", - [FileName, Error, S]), - {error, Error}; - T:E -> - S = erlang:get_stacktrace(), - d("File read of ~s exception: ~p:~p~n ~p~n", - [FileName,T,E,S]), - {error, {failed_read, Dir, FileName, {T, E, S}}} + throw:E:S -> + d("File read of ~s throwed: " + "~n ~p" + "~n ~p" + "~n", [FileName, E, S]), + {error, E}; + C:E:S -> + d("File read of ~s exception: " + "~n ~p:~p" + "~n ~p" + "~n", [FileName, C, E, S]), + {error, {failed_read, Dir, FileName, {C, E, S}}} after file:close(Fd) end; @@ -2760,11 +2768,10 @@ verify_lines( {{ok, NewTerm}, NewState} -> verify_lines(Lines, Check, NewState, [NewTerm|Acc]) catch - {error, Reason} -> + throw:{error, Reason}:_ -> throw({failed_check, StartLine, EndLine, Reason}); - C:R -> - S = erlang:get_stacktrace(), - throw({failed_check, StartLine, EndLine, {C, R, S}}) + C:E:S -> + throw({failed_check, StartLine, EndLine, {C, E, S}}) end. diff --git a/lib/snmp/test/snmp_agent_test.erl b/lib/snmp/test/snmp_agent_test.erl index 71e3fa3b9a..860ca17cdb 100644 --- a/lib/snmp/test/snmp_agent_test.erl +++ b/lib/snmp/test/snmp_agent_test.erl @@ -667,22 +667,39 @@ init_per_group(GroupName, Config) -> snmp_test_lib:init_group_top_dir(GroupName, Config). init_per_group_ipv6(GroupName, Config, Init) -> - {ok, Hostname0} = inet:gethostname(), - case ct:require(ipv6_hosts) of - ok -> - case lists:member(list_to_atom(Hostname0), ct:get_config(ipv6_hosts)) of - true -> - Init( - snmp_test_lib:init_group_top_dir( - GroupName, - [{ipfamily, inet6}, - {ip, ?LOCALHOST(inet6)} - | lists:keydelete(ip, 1, Config)])); - false -> - {skip, "Host does not support IPV6"} - end; - _ -> - {skip, "Test config ipv6_hosts is missing"} + %% <OS-CONDITIONAL-SKIP> + %% This is a higly questionable test. + %% But until we have time to figure out what IPv6 issues + %% are actually causing the failures... + OSSkipable = [{unix, + [ + {darwin, fun(V) when (V > {9, 8, 0}) -> + %% This version is OK: No Skip + false; + (_) -> + %% This version is *not* ok: Skip + true + end} + ] + }], + %% </OS-CONDITIONAL-SKIP> + case ?OS_BASED_SKIP(OSSkipable) of + true -> + {skip, "Host *may* not *properly* support IPV6"}; + false -> + %% Even if this host supports IPv6 we don't use it unless its + %% one of the configured/supported IPv6 hosts... + case (?HAS_SUPPORT_IPV6() andalso ?IS_IPV6_HOST()) of + true -> + Init( + snmp_test_lib:init_group_top_dir( + GroupName, + [{ipfamily, inet6}, + {ip, ?LOCALHOST(inet6)} + | lists:keydelete(ip, 1, Config)])); + false -> + {skip, "Host does not support IPv6"} + end end. end_per_group(all_tcs, Config) -> diff --git a/lib/snmp/test/snmp_agent_test_lib.erl b/lib/snmp/test/snmp_agent_test_lib.erl index 6defdadb5a..1128fc8a8c 100644 --- a/lib/snmp/test/snmp_agent_test_lib.erl +++ b/lib/snmp/test/snmp_agent_test_lib.erl @@ -66,7 +66,7 @@ ]). %% Internal exports --export([wait/5, run/4]). +-export([tc_wait/5, tc_run/4]). -include_lib("kernel/include/file.hrl"). -include_lib("common_test/include/ct.hrl"). @@ -276,36 +276,94 @@ init_case(Config) when is_list(Config) -> %%% configuration. %%%-------------------------------------------------- -try_test(Mod, Func) -> - call(get(mgr_node), ?MODULE, run, [Mod, Func, [], []]). - -try_test(Mod, Func, A) -> - call(get(mgr_node), ?MODULE, run, [Mod, Func, A, []]). - -try_test(Mod, Func, A, Opts) -> - call(get(mgr_node), ?MODULE, run, [Mod, Func, A, Opts]). - -call(N,M,F,A) -> - ?DBG("call -> entry with~n" - " N: ~p~n" - " M: ~p~n" - " F: ~p~n" - " A: ~p~n" - " when~n" - " get(): ~p", - [N,M,F,A,get()]), - spawn(N, ?MODULE, wait, [self(),get(),M,F,A]), +try_test(TcRunMod, TcRunFunc) -> + try_test(TcRunMod, TcRunFunc, []). + +try_test(TcRunMod, TcRunFunc, TcRunArgs) -> + try_test(TcRunMod, TcRunFunc, TcRunArgs, []). + +try_test(TcRunMod, TcRunFunc, TcRunArgs, TcRunOpts) -> + Node = get(mgr_node), + Mod = ?MODULE, + Func = tc_run, + Args = [TcRunMod, TcRunFunc, TcRunArgs, TcRunOpts], + tc_try(Node, Mod, Func, Args). + +%% We spawn a test case runner process on the manager node. +%% The assumption is that the manager shall do something, but +%% not all test cases have the manager perform actions. +%% In some cases we make a rpc call back to the agent node directly +%% and call something in the agent... (for example the info_test +%% test case). +%% We should use link (instead of monitor) in order for the test case +%% timeout cleanup (kills) should have effect on the test case runner +%% process as well. + +tc_try(N, M, F, A) -> + ?PRINT2("tc_try -> entry with" + "~n N: ~p" + "~n M: ~p" + "~n F: ~p" + "~n A: ~p" + "~n when" + "~n get(): ~p" + "~n", [N, + M, F, A, + get()]), + case net_adm:ping(N) of + pong -> + ?PRINT2("tc_try -> ~p still running - start runner~n", [N]), + OldFlag = trap_exit(true), % Make sure we catch it + Runner = spawn_link(N, ?MODULE, tc_wait, [self(), get(), M, F, A]), + await_tc_runner_started(Runner, OldFlag), + await_tc_runner_done(Runner, OldFlag); + pang -> + ?EPRINT2("tc_try -> ~p *not* running~n", [N]), + exit({node_not_running, N}) + end. + +await_tc_runner_started(Runner, OldFlag) -> + ?PRINT2("await tc-runner (~p) start ack~n", [Runner]), + receive + {'EXIT', Runner, Reason} -> + ?EPRINT2("TC runner start failed: " + "~n ~p~n", [Reason]), + exit({tx_runner_start_failed, Reason}); + {tc_runner_started, Runner} -> + ?PRINT2("TC runner start acknowledged~n"), + ok + after 10000 -> + trap_exit(OldFlag), + unlink_and_flush_exit(Runner), + RunnerInfo = process_info(Runner), + ?EPRINT2("TC runner start timeout: " + "~n ~p", [RunnerInfo]), + %% If we don't get a start ack within 10 seconds, we are f*ed + exit(Runner, kill), + exit({tc_runner_start, timeout, RunnerInfo}) + end. + +await_tc_runner_done(Runner, OldFlag) -> receive - {done, {'EXIT', Rn}, Loc} -> - ?DBG("call -> done with exit: " - "~n Rn: ~p" - "~n Loc: ~p", [Rn, Loc]), + {'EXIT', Runner, Reason} -> + ?EPRINT2("TC runner failed: " + "~n ~p~n", [Reason]), + exit({tx_runner_failed, Reason}); + {tc_runner_done, Runner, {'EXIT', Rn}, Loc} -> + ?PRINT2("call -> done with exit: " + "~n Rn: ~p" + "~n Loc: ~p" + "~n", [Rn, Loc]), + trap_exit(OldFlag), + unlink_and_flush_exit(Runner), put(test_server_loc, Loc), exit(Rn); - {done, Ret, _Zed} -> + {tc_runner_done, Runner, Ret, _Zed} -> ?DBG("call -> done:" "~n Ret: ~p" "~n Zed: ~p", [Ret, _Zed]), + trap_exit(OldFlag), + unlink_and_flush_exit(Runner), case Ret of {error, Reason} -> exit(Reason); @@ -314,49 +372,66 @@ call(N,M,F,A) -> end end. -wait(From, Env, M, F, A) -> - ?DBG("wait -> entry with" - "~n From: ~p" - "~n Env: ~p" - "~n M: ~p" - "~n F: ~p" - "~n A: ~p", [From, Env, M, F, A]), +trap_exit(Flag) when is_boolean(Flag) -> + erlang:process_flag(trap_exit, Flag). + +unlink_and_flush_exit(Pid) -> + unlink(Pid), + receive + {'EXIT', Pid, _} -> + ok + after 0 -> + ok + end. + +tc_wait(From, Env, M, F, A) -> + ?PRINT2("tc_wait -> entry with" + "~n From: ~p" + "~n Env: ~p" + "~n M: ~p" + "~n F: ~p" + "~n A: ~p", [From, Env, M, F, A]), + From ! {tc_runner_started, self()}, lists:foreach(fun({K,V}) -> put(K,V) end, Env), - Rn = (catch apply(M, F, A)), - ?DBG("wait -> Rn: ~n~p", [Rn]), - From ! {done, Rn, get(test_server_loc)}, - exit(Rn). - -run(Mod, Func, Args, Opts) -> - ?DBG("run -> entry with" - "~n Mod: ~p" - "~n Func: ~p" - "~n Args: ~p" - "~n Opts: ~p", [Mod, Func, Args, Opts]), - M = get(mib_dir), - Dir = get(mgr_dir), - User = snmp_misc:get_option(user, Opts, "all-rights"), - SecLevel = snmp_misc:get_option(sec_level, Opts, noAuthNoPriv), - EngineID = snmp_misc:get_option(engine_id, Opts, "agentEngine"), + ?PRINT2("tc_wait -> env set - now run tc~n"), + Res = (catch apply(M, F, A)), + ?PRINT2("tc_wait -> tc run done: " + "~n ~p" + "~n", [Res]), + From ! {tc_runner_done, self(), Res, get(test_server_loc)}, + exit(Res). + +tc_run(Mod, Func, Args, Opts) -> + ?PRINT2("tc_run -> entry with" + "~n Mod: ~p" + "~n Func: ~p" + "~n Args: ~p" + "~n Opts: ~p" + "~n", [Mod, Func, Args, Opts]), + (catch snmp_test_mgr:stop()), % If we had a running mgr from a failed case + M = get(mib_dir), + Dir = get(mgr_dir), + User = snmp_misc:get_option(user, Opts, "all-rights"), + SecLevel = snmp_misc:get_option(sec_level, Opts, noAuthNoPriv), + EngineID = snmp_misc:get_option(engine_id, Opts, "agentEngine"), CtxEngineID = snmp_misc:get_option(context_engine_id, Opts, EngineID), - Community = snmp_misc:get_option(community, Opts, "all-rights"), - ?DBG("run -> start crypto app",[]), - _CryptoRes = ?CRYPTO_START(), - ?DBG("run -> Crypto: ~p", [_CryptoRes]), - catch snmp_test_mgr:stop(), % If we had a running mgr from a failed case - StdM = join(code:priv_dir(snmp), "mibs") ++ "/", - Vsn = get(vsn), - ?DBG("run -> config:" - "~n M: ~p" - "~n Vsn: ~p" - "~n Dir: ~p" - "~n User: ~p" - "~n SecLevel: ~p" - "~n EngineID: ~p" - "~n CtxEngineID: ~p" - "~n Community: ~p" - "~n StdM: ~p", - [M,Vsn,Dir,User,SecLevel,EngineID,CtxEngineID,Community,StdM]), + Community = snmp_misc:get_option(community, Opts, "all-rights"), + ?DBG("tc_run -> start crypto app",[]), + _CryptoRes = ?CRYPTO_START(), + ?DBG("tc_run -> Crypto: ~p", [_CryptoRes]), + StdM = join(code:priv_dir(snmp), "mibs") ++ "/", + Vsn = get(vsn), + ?PRINT2("tc_run -> config:" + "~n M: ~p" + "~n Vsn: ~p" + "~n Dir: ~p" + "~n User: ~p" + "~n SecLevel: ~p" + "~n EngineID: ~p" + "~n CtxEngineID: ~p" + "~n Community: ~p" + "~n StdM: ~p" + "~n", [M,Vsn,Dir,User,SecLevel,EngineID,CtxEngineID,Community,StdM]), case snmp_test_mgr:start([%% {agent, snmp_test_lib:hostname()}, {packet_server_debug, true}, {debug, true}, @@ -377,23 +452,23 @@ run(Mod, Func, Args, Opts) -> {ok, _Pid} -> case (catch apply(Mod, Func, Args)) of {'EXIT', Reason} -> - catch snmp_test_mgr:stop(), + (catch snmp_test_mgr:stop()), ?FAIL({apply_failed, {Mod, Func, Args}, Reason}); Res -> - catch snmp_test_mgr:stop(), + (catch snmp_test_mgr:stop()), Res end; {error, Reason} -> ?EPRINT2("Failed starting (test) manager: " "~n ~p", [Reason]), - catch snmp_test_mgr:stop(), + (catch snmp_test_mgr:stop()), ?line ?FAIL({mgr_start_error, Reason}); Err -> ?EPRINT2("Failed starting (test) manager: " "~n ~p", [Err]), - catch snmp_test_mgr:stop(), + (catch snmp_test_mgr:stop()), ?line ?FAIL({mgr_start_failure, Err}) end. diff --git a/lib/snmp/test/snmp_manager_config_test.erl b/lib/snmp/test/snmp_manager_config_test.erl index 64d3134055..ccbdd77629 100644 --- a/lib/snmp/test/snmp_manager_config_test.erl +++ b/lib/snmp/test/snmp_manager_config_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2016. All Rights Reserved. +%% Copyright Ericsson AB 2004-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -35,6 +35,7 @@ -include_lib("common_test/include/ct.hrl"). -include("snmp_test_lib.hrl"). -include_lib("snmp/src/manager/snmpm_usm.hrl"). +-include_lib("snmp/src/app/snmp_internal.hrl"). %%---------------------------------------------------------------------- @@ -2259,11 +2260,13 @@ create_and_increment(Conf) when is_list(Conf) -> ?line {ok, _Pid} = snmpm_config:start_link(Opts), %% Random init - random:seed(erlang:phash2([node()]), - erlang:monotonic_time(), - erlang:unique_integer()), + ?SNMP_RAND_SEED(), + %% rand:seed(exrop, + %% {erlang:phash2([node()]), + %% erlang:monotonic_time(), + %% erlang:unique_integer()}), - StartVal = random:uniform(2147483647), + StartVal = rand:uniform(2147483647), IncVal = 42, EndVal = StartVal + IncVal, diff --git a/lib/snmp/test/snmp_manager_test.erl b/lib/snmp/test/snmp_manager_test.erl index 5b0ebf8647..d959d9e09b 100644 --- a/lib/snmp/test/snmp_manager_test.erl +++ b/lib/snmp/test/snmp_manager_test.erl @@ -619,38 +619,47 @@ init_per_group(event_tests_mt = GroupName, Config) -> GroupName, [{manager_net_if_module, snmpm_net_if_mt} | Config]); init_per_group(ipv6_mt = GroupName, Config) -> - {ok, Hostname0} = inet:gethostname(), - case ct:require(ipv6_hosts) of - ok -> - case lists:member(list_to_atom(Hostname0), ct:get_config(ipv6_hosts)) of - true -> - ipv6_init( - snmp_test_lib:init_group_top_dir( - GroupName, - [{manager_net_if_module, snmpm_net_if_mt} - | Config])); - false -> - {skip, "Host does not support IPv6"} - end; - _ -> - {skip, "Test config ipv6_hosts is missing"} - end; + init_per_group_ipv6(GroupName, + [{manager_net_if_module, snmpm_net_if_mt} | Config]); init_per_group(ipv6 = GroupName, Config) -> - {ok, Hostname0} = inet:gethostname(), - case ct:require(ipv6_hosts) of - ok -> - case lists:member(list_to_atom(Hostname0), ct:get_config(ipv6_hosts)) of - true -> - ipv6_init(snmp_test_lib:init_group_top_dir(GroupName, Config)); - false -> - {skip, "Host does not support IPv6"} - end; - _ -> - {skip, "Test config ipv6_hosts is missing"} - end; + init_per_group_ipv6(GroupName, Config); init_per_group(GroupName, Config) -> snmp_test_lib:init_group_top_dir(GroupName, Config). + +init_per_group_ipv6(GroupName, Config) -> + %% <OS-CONDITIONAL-SKIP> + OSSkipable = [{unix, + [ + {darwin, fun(V) when (V > {9, 8, 0}) -> + %% This version is OK: No Skip + false; + (_) -> + %% This version is *not* ok: Skip + %% We need a fully qualified hostname + %% to get a proper IPv6 address (in this + %% version), but its just to messy, so + %% instead we skip this **OLD** darwin... + true + end} + ] + }], + %% </OS-CONDITIONAL-SKIP> + case ?OS_BASED_SKIP(OSSkipable) of + true -> + {skip, "Host *may* not *properly* support IPV6"}; + false -> + %% Even if this host supports IPv6 we don't use it unless its + %% one of the configures/supported IPv6 hosts... + case (?HAS_SUPPORT_IPV6() andalso ?IS_IPV6_HOST()) of + true -> + ipv6_init(snmp_test_lib:init_group_top_dir(GroupName, Config)); + false -> + {skip, "Host does not support IPv6"} + end + end. + + end_per_group(_GroupName, Config) -> %% Do we really need to do this? lists:keydelete(snmp_group_top_dir, 1, Config). diff --git a/lib/snmp/test/snmp_test_lib.erl b/lib/snmp/test/snmp_test_lib.erl index a483690653..42f710e4cd 100644 --- a/lib/snmp/test/snmp_test_lib.erl +++ b/lib/snmp/test/snmp_test_lib.erl @@ -25,7 +25,9 @@ -export([hostname/0, hostname/1, localhost/0, localhost/1, os_type/0, sz/1, display_suite_info/1]). --export([non_pc_tc_maybe_skip/4, os_based_skip/1]). +-export([non_pc_tc_maybe_skip/4, os_based_skip/1, + has_support_ipv6/0, has_support_ipv6/1, + is_ipv6_host/0, is_ipv6_host/1]). -export([fix_data_dir/1, init_suite_top_dir/2, init_group_top_dir/2, init_testcase_top_dir/2, lookup/2, @@ -52,11 +54,12 @@ hostname() -> hostname(node()). hostname(Node) -> - from($@, atom_to_list(Node)). - -from(H, [H | T]) -> T; -from(H, [_ | T]) -> from(H, T); -from(_H, []) -> []. + case string:tokens(atom_to_list(Node), [$@]) of + [_, Host] -> + Host; + _ -> + [] + end. %% localhost() -> %% {ok, Ip} = snmp_misc:ip(net_adm:localhost()), @@ -78,7 +81,9 @@ localhost(Family) -> {error, Reason1} -> fail({getifaddrs, Reason1}, ?MODULE, ?LINE) end; - {ok, {0, _, _, _, _, _, _, _}} when (Family =:= inet6) -> + {ok, {A1, _, _, _, _, _, _, _}} when (Family =:= inet6) andalso + ((A1 =:= 0) orelse + (A1 =:= 16#fe80)) -> %% Ouch, we need to use something else case inet:getifaddrs() of {ok, IfList} -> @@ -207,53 +212,108 @@ non_pc_tc_maybe_skip(Config, Condition, File, Line) end. +%% The type and spec'ing is just to increase readability +-type os_family() :: win32 | unix. +-type os_name() :: atom(). +-type os_version() :: string() | {non_neg_integer(), + non_neg_integer(), + non_neg_integer()}. +-type os_skip_check() :: fun(() -> boolean()) | + fun((os_version()) -> boolean()). +-type skippable() :: any | [os_family() | + {os_family(), os_name() | + [os_name() | {os_name(), + os_skip_check()}]}]. + +-spec os_based_skip(skippable()) -> boolean(). + os_based_skip(any) -> - io:format("os_based_skip(any) -> entry" - "~n", []), true; os_based_skip(Skippable) when is_list(Skippable) -> - io:format("os_based_skip -> entry with" - "~n Skippable: ~p" - "~n", [Skippable]), - {OsFam, OsName} = - case os:type() of - {_Fam, _Name} = FamAndName -> - FamAndName; - Fam -> - {Fam, undefined} - end, - io:format("os_based_skip -> os-type: " - "~n OsFam: ~p" - "~n OsName: ~p" - "~n", [OsFam, OsName]), + os_base_skip(Skippable, os:type()); +os_based_skip(_Crap) -> + false. + +os_base_skip(Skippable, {OsFam, OsName}) -> + os_base_skip(Skippable, OsFam, OsName); +os_base_skip(Skippable, OsFam) -> + os_base_skip(Skippable, OsFam, undefined). + +os_base_skip(Skippable, OsFam, OsName) -> + %% Check if the entire family is to be skipped + %% Example: [win32, unix] case lists:member(OsFam, Skippable) of true -> true; false -> - case lists:keysearch(OsFam, 1, Skippable) of - {value, {OsFam, OsName}} -> - true; - {value, {OsFam, OsNames}} when is_list(OsNames) -> + %% Example: [{unix, freebsd}] | [{unix, [freebsd, darwin]}] + case lists:keysearch(OsFam, 1, Skippable) of + {value, {OsFam, OsName}} -> + true; + {value, {OsFam, OsNames}} when is_list(OsNames) -> + %% OsNames is a list of: + %% [atom()|{atom(), function/0 | function/1}] case lists:member(OsName, OsNames) of true -> true; false -> - case lists:keymember(OsName, 1, OsNames) of - {value, {OsName, Check}} when is_function(Check) -> - Check(); - _ -> - false - end + os_based_skip_check(OsName, OsNames) end; - _ -> - false - end - end; -os_based_skip(_Crap) -> - io:format("os_based_skip -> entry with" - "~n _Crap: ~p" - "~n", [_Crap]), - false. + _ -> + false + end + end. + +%% Performs a check via a provided fun with arity 0 or 1. +%% The argument is the result of os:version(). +os_based_skip_check(OsName, OsNames) -> + case lists:keysearch(OsName, 1, OsNames) of + {value, {OsName, Check}} when is_function(Check, 0) -> + Check(); + {value, {OsName, Check}} when is_function(Check, 1) -> + Check(os:version()); + _ -> + false + end. + + +%% A basic test to check if current host supports IPv6 +has_support_ipv6() -> + case inet:gethostname() of + {ok, Hostname} -> + has_support_ipv6(Hostname); + _ -> + false + end. + +has_support_ipv6(Hostname) -> + case inet:getaddr(Hostname, inet6) of + {ok, Addr} when (size(Addr) =:= 8) andalso + (element(1, Addr) =/= 0) andalso + (element(1, Addr) =/= 16#fe80) -> + true; + {ok, _} -> + false; + {error, _} -> + false + end. + + +is_ipv6_host() -> + case inet:gethostname() of + {ok, Hostname} -> + is_ipv6_host(Hostname); + {error, _} -> + false + end. + +is_ipv6_host(Hostname) -> + case ct:require(ipv6_hosts) of + ok -> + lists:member(list_to_atom(Hostname), ct:get_config(ipv6_hosts)); + _ -> + false + end. %% ---------------------------------------------------------------- diff --git a/lib/snmp/test/snmp_test_lib.hrl b/lib/snmp/test/snmp_test_lib.hrl index 335f3fff3c..c66602b779 100644 --- a/lib/snmp/test/snmp_test_lib.hrl +++ b/lib/snmp/test/snmp_test_lib.hrl @@ -49,8 +49,12 @@ snmp_test_lib:os_based_skip(Skippable)). -define(NON_PC_TC_MAYBE_SKIP(Config, Condition), snmp_test_lib:non_pc_tc_maybe_skip(Config, Condition, ?MODULE, ?LINE)). --define(SKIP(Reason), snmp_test_lib:skip(Reason, ?MODULE, ?LINE)). --define(FAIL(Reason), snmp_test_lib:fail(Reason, ?MODULE, ?LINE)). +-define(SKIP(Reason), snmp_test_lib:skip(Reason, ?MODULE, ?LINE)). +-define(FAIL(Reason), snmp_test_lib:fail(Reason, ?MODULE, ?LINE)). +-define(IS_IPV6_HOST(), snmp_test_lib:is_ipv6_host()). +-define(IS_IPV6_HOST(H), snmp_test_lib:is_ipv6_host(H)). +-define(HAS_SUPPORT_IPV6(), snmp_test_lib:has_support_ipv6()). +-define(HAS_SUPPORT_IPV6(H), snmp_test_lib:has_support_ipv6(H)). %% - Time macros - @@ -150,8 +154,10 @@ snmp_test_lib:print(P, ?MODULE, ?LINE, F, A)). -define(PRINT1(F, A), snmp_test_lib:print1(F, A)). +-define(PRINT1(F), ?PRINT1(F, [])). -define(EPRINT1(F, A), ?PRINT1("<ERROR> " ++ F, A)). -define(PRINT2(F, A), snmp_test_lib:print2(F, A)). +-define(PRINT2(F), ?PRINT2(F, [])). -define(EPRINT2(F, A), ?PRINT2("<ERROR> " ++ F, A)). diff --git a/lib/snmp/test/snmp_test_mgr.erl b/lib/snmp/test/snmp_test_mgr.erl index 73a4d56084..9d6be65088 100644 --- a/lib/snmp/test/snmp_test_mgr.erl +++ b/lib/snmp/test/snmp_test_mgr.erl @@ -52,6 +52,7 @@ -include_lib("snmp/include/snmp_types.hrl"). -include_lib("snmp/include/STANDARD-MIB.hrl"). -include("snmp_test_lib.hrl"). +-include_lib("snmp/src/app/snmp_internal.hrl"). -record(state, {dbg = true, quiet, @@ -192,9 +193,11 @@ receive_trap(Timeout) -> init({Options, CallerPid}) -> put(sname, mgr), put(verbosity, debug), - random:seed(erlang:phash2([node()]), - erlang:monotonic_time(), - erlang:unique_integer()), + ?SNMP_RAND_SEED(), + %% rand:seed(exrop, + %% {erlang:phash2([node()]), + %% erlang:monotonic_time(), + %% erlang:unique_integer()}), case (catch is_options_ok(Options)) of true -> put(debug, get_value(debug, Options, false)), @@ -244,10 +247,21 @@ init({Options, CallerPid}) -> IpFamily = get_value(ipfamily, Options, inet), print("[~w] ~p -> IpFamily: ~p~n", [?MODULE, self(), IpFamily]), AgIp = case snmp_misc:assq(agent, Options) of - {value, Tuple4} when is_tuple(Tuple4) andalso - (size(Tuple4) =:= 4) -> - Tuple4; + {value, Addr} when is_tuple(Addr) andalso + (size(Addr) =:= 4) andalso + (IpFamily =:= inet) -> + print("[~w] ~p -> Addr: ~p~n", + [?MODULE, self(), Addr]), + Addr; + {value, Addr} when is_tuple(Addr) andalso + (size(Addr) =:= 8) andalso + (IpFamily =:= inet6) -> + print("[~w] ~p -> Addr: ~p~n", + [?MODULE, self(), Addr]), + Addr; {value, Host} when is_list(Host) -> + print("[~w] ~p -> Host: ~p~n", + [?MODULE, self(), Host]), {ok, Ip} = snmp_misc:ip(Host, IpFamily), Ip end, @@ -668,7 +682,6 @@ make_vb(Oid) -> #varbind{oid = Oid, variabletype = 'NULL', value = 'NULL'}. make_request_id() -> - %% random:uniform(16#FFFFFFF-1). snmp_test_mgr_counter_server:increment(mgr_request_id, 1, 1, 2147483647). echo_pdu(PDU, MiniMIB) -> @@ -1141,5 +1154,5 @@ d(_,_F,_A) -> print(F, A) -> ?PRINT2("MGR " ++ F, A). -formated_timestamp() -> - snmp_test_lib:formated_timestamp(). +%% formated_timestamp() -> +%% snmp_test_lib:formated_timestamp(). diff --git a/lib/snmp/test/snmp_test_mgr_misc.erl b/lib/snmp/test/snmp_test_mgr_misc.erl index 315e3ebd9e..6608a88c00 100644 --- a/lib/snmp/test/snmp_test_mgr_misc.erl +++ b/lib/snmp/test/snmp_test_mgr_misc.erl @@ -576,6 +576,7 @@ vsn('version-2') -> v2c. udp_send(UdpId, AgentIp, UdpPort, B) -> + ?vlog("attempt send message (~w bytes) to ~p", [sz(B), {AgentIp, UdpPort}]), case (catch gen_udp:send(UdpId, AgentIp, UdpPort, B)) of {error,ErrorReason} -> error("failed (error) sending message to ~p:~p: " @@ -880,7 +881,7 @@ display_prop_hdr(S) -> %%---------------------------------------------------------------------- sz(L) when is_list(L) -> - length(lists:flatten(L)); + iolist_size(L); sz(B) when is_binary(B) -> size(B); sz(O) -> diff --git a/lib/snmp/test/snmp_to_snmpnet_SUITE.erl b/lib/snmp/test/snmp_to_snmpnet_SUITE.erl index b83c7461d1..b8bdb30271 100644 --- a/lib/snmp/test/snmp_to_snmpnet_SUITE.erl +++ b/lib/snmp/test/snmp_to_snmpnet_SUITE.erl @@ -247,9 +247,17 @@ erlang_agent_netsnmp_get(Config) when is_list(Config) -> start_agent(Config), Oid = ?sysDescr_instance, Expected = expected(Oid, get), - [Expected = snmpget(Oid, Transport, Config) - || Transport <- Transports], - ok. + try + begin + [Expected = snmpget(Oid, Transport, Config) + || Transport <- Transports], + ok + end + catch + throw:{skip, _} = SKIP -> + SKIP + end. + %%-------------------------------------------------------------------- erlang_manager_netsnmp_get() -> @@ -260,29 +268,34 @@ erlang_manager_netsnmp_get(Config) when is_list(Config) -> SysDescr = "Net-SNMP agent", TargetName = "Target Net-SNMP agent", Transports = ?config(transports, Config), - ProgHandle = start_snmpd(Community, SysDescr, Config), - start_manager(Config), - snmp_manager_user:start_link(self(), test_user), - [snmp_manager_user:register_agent( - TargetName++domain_suffix(Domain), - [{reg_type, target_name}, - {tdomain, Domain}, {taddress, Addr}, - {community, Community}, {engine_id, "EngineId"}, - {version, v2}, {sec_model, v2c}, {sec_level, noAuthNoPriv}]) - || {Domain, Addr} <- Transports], - Results = - [snmp_manager_user:sync_get( - TargetName++domain_suffix(Domain), - [?sysDescr_instance]) - || {Domain, _} <- Transports], - ct:pal("sync_get -> ~p", [Results]), - snmp_manager_user:stop(), - stop_program(ProgHandle), - [{ok, - {noError, 0, - [{varbind, ?sysDescr_instance, 'OCTET STRING', SysDescr,1}] }, - _} = R || R <- Results], - ok. + case start_snmpd(Community, SysDescr, Config) of + {skip, _} = SKIP -> + SKIP; + ProgHandle -> + start_manager(Config), + snmp_manager_user:start_link(self(), test_user), + [snmp_manager_user:register_agent( + TargetName++domain_suffix(Domain), + [{reg_type, target_name}, + {tdomain, Domain}, {taddress, Addr}, + {community, Community}, {engine_id, "EngineId"}, + {version, v2}, {sec_model, v2c}, {sec_level, noAuthNoPriv}]) + || {Domain, Addr} <- Transports], + Results = + [snmp_manager_user:sync_get( + TargetName++domain_suffix(Domain), + [?sysDescr_instance]) + || {Domain, _} <- Transports], + ct:pal("sync_get -> ~p", [Results]), + snmp_manager_user:stop(), + stop_program(ProgHandle), + [{ok, + {noError, 0, + [{varbind, ?sysDescr_instance, 'OCTET STRING', SysDescr,1}] }, + _} = R || R <- Results], + ok + end. + %%-------------------------------------------------------------------- erlang_agent_netsnmp_inform(Config) when is_list(Config) -> @@ -292,17 +305,19 @@ erlang_agent_netsnmp_inform(Config) when is_list(Config) -> start_agent(Config), ok = snmpa:load_mib(snmp_master_agent, filename:join(DataDir, Mib)), - ProgHandle = start_snmptrapd(Mib, Config), - - snmpa:send_notification( - snmp_master_agent, testTrapv22, {erlang_agent_test, self()}), - - receive - {snmp_targets, erlang_agent_test, Addresses} -> - ct:pal("Notification sent to: ~p~n", [Addresses]), - erlang_agent_netsnmp_inform_responses(Addresses) - end, - stop_program(ProgHandle). + case start_snmptrapd(Mib, Config) of + {skip, _} = SKIP -> + SKIP; + ProgHandle -> + snmpa:send_notification( + snmp_master_agent, testTrapv22, {erlang_agent_test, self()}), + receive + {snmp_targets, erlang_agent_test, Addresses} -> + ct:pal("Notification sent to: ~p~n", [Addresses]), + erlang_agent_netsnmp_inform_responses(Addresses) + end, + stop_program(ProgHandle) + end. erlang_agent_netsnmp_inform_responses([]) -> receive @@ -326,6 +341,7 @@ erlang_agent_netsnmp_inform_responses([Address | Addresses]) -> %%-------------------------------------------------------------------- %% Internal functions ------------------------------------------------ %%-------------------------------------------------------------------- + snmpget(Oid, Transport, Config) -> Versions = ?config(snmp_versions, Config), @@ -335,10 +351,14 @@ snmpget(Oid, Transport, Config) -> "-Cf", net_snmp_addr_str(Transport), oid_str(Oid)], - ProgHandle = start_program(snmpget, Args, none, Config), - {_, line, Line} = get_program_output(ProgHandle), - stop_program(ProgHandle), - Line. + case start_program(snmpget, Args, none, Config) of + {skip, _} = SKIP -> + throw(SKIP); + ProgHandle -> + {_, line, Line} = get_program_output(ProgHandle), + stop_program(ProgHandle), + Line + end. start_snmptrapd(Mibs, Config) -> DataDir = ?config(data_dir, Config), @@ -382,12 +402,13 @@ start_program(Prog, Args, StartCheckMP, Config) -> DataDir = ?config(data_dir, Config), StartWrapper = filename:join(DataDir, "start_stop_wrapper"), Parent = self(), - Pid = - spawn_link( + %% process_flag(trap_exit, true), + {Pid, Mon} = + spawn_monitor( fun () -> run_program(Parent, StartWrapper, [Path | Args]) end), - start_check(Pid, erlang:monitor(process, Pid), StartCheckMP). + start_check(Pid, Mon, StartCheckMP). start_check(Pid, Mon, none) -> {Pid, Mon}; @@ -400,6 +421,10 @@ start_check(Pid, Mon, StartCheckMP) -> nomatch -> start_check(Pid, Mon, StartCheckMP) end; + {'DOWN', Mon, _, _Pid, {skip, Reason} = SKIP} -> + ct:pal("Received DOWN from ~p" + "~n Skip Reason: ~p", [_Pid, Reason]), + SKIP; {'DOWN', Mon, _, _, Reason} -> ct:fail("Prog ~p start failed: ~p", [Pid, Reason]) end. @@ -446,14 +471,34 @@ run_program_loop(Parent, Port, Buf) -> {Port, {data, {Flag, Data}}} -> case Flag of eol -> - Line = iolist_to_binary(lists:reverse(Buf, Data)), - ct:pal("Prog ~p output: ~s", [Port, Line]), - Parent ! {self(), line, Line}, - run_program_loop(Parent, Port, []); + Line = iolist_to_binary(lists:reverse(Buf, Data)), + ct:pal("Prog ~p output: ~s", [Port, Line]), + %% There are potentially many different fail outputs, + %% but for now we test for just this one: illegal option + IOpt = "illegal option", + case string:find(binary_to_list(Line), IOpt) of + nomatch -> + Parent ! {self(), line, Line}, + run_program_loop(Parent, Port, []); + Line2 -> + %% Try to extract the actual illegal option string + IOpt2 = + case string:take( + string:prefix(Line2, IOpt), [$-, $ ]) of + {_, Str} when length(Str) > 0 -> + Str; + _X -> + Line2 + end, + ct:pal("Force program ~p stop", [Port]), + true = port_command(Port, <<"stop\n">>), + (catch port_close(Port)), + exit({skip, {illegal_option, IOpt2}}) + end; noeol -> run_program_loop(Parent, Port, [Data | Buf]) end; - {Port, {exit_status,ExitStatus}} -> + {Port, {exit_status, ExitStatus}} -> ct:pal("Prog ~p exit: ~p", [Port, ExitStatus]), catch port_close(Port), Parent ! {self(), exit, ExitStatus}; diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml index 3fd6eae423..8b7cb4dcd4 100644 --- a/lib/ssh/doc/src/ssh.xml +++ b/lib/ssh/doc/src/ssh.xml @@ -347,6 +347,7 @@ <datatype> <name name="subsystem_daemon_option"/> + <name name="subsystem_specs"/> <name name="subsystem_spec"/> <desc> <p>Defines a subsystem in the daemon.</p> diff --git a/lib/ssh/src/Makefile b/lib/ssh/src/Makefile index 6d64a45112..9627b70eeb 100644 --- a/lib/ssh/src/Makefile +++ b/lib/ssh/src/Makefile @@ -99,7 +99,7 @@ APP_TARGET= $(EBIN)/$(APP_FILE) APPUP_SRC= $(APPUP_FILE).src APPUP_TARGET= $(EBIN)/$(APPUP_FILE) -INTERNAL_HRL_FILES = ssh_auth.hrl ssh_connect.hrl ssh_transport.hrl ssh.hrl ssh_userauth.hrl ssh_xfer.hrl +INTERNAL_HRL_FILES = ssh_auth.hrl ssh_connect.hrl ssh_transport.hrl ssh.hrl ssh_xfer.hrl # ---------------------------------------------------- # FLAGS diff --git a/lib/ssh/src/ssh.hrl b/lib/ssh/src/ssh.hrl index 04453e6ef0..a991f72cf2 100644 --- a/lib/ssh/src/ssh.hrl +++ b/lib/ssh/src/ssh.hrl @@ -68,6 +68,25 @@ -define(string(X), ?string_utf8(X)). -define(binary(X), << ?STRING(X) >>). +-define('2bin'(X), (if is_binary(X) -> X; + is_list(X) -> list_to_binary(X); + X==undefined -> <<>> + end) ). + +%% encoding macros +-define('E...'(X), ?'2bin'(X)/binary ). +-define(Eboolean(X), ?BOOLEAN(case X of + true -> ?TRUE; + false -> ?FALSE + end) ). +-define(Ebyte(X), ?BYTE(X) ). +-define(Euint32(X), ?UINT32(X) ). +-define(Estring(X), ?STRING(?'2bin'(X)) ). +-define(Estring_utf8(X), ?string_utf8(X)/binary ). +-define(Ename_list(X), ?STRING(ssh_bits:name_list(X)) ). +-define(Empint(X), (ssh_bits:mpint(X))/binary ). +-define(Ebinary(X), ?STRING(X) ). + %% Cipher details -define(SSH_CIPHER_NONE, 0). -define(SSH_CIPHER_3DES, 3). @@ -293,7 +312,8 @@ | gen_tcp:listen_option() | ?COMMON_OPTION . --type subsystem_daemon_option() :: {subsystems, subsystem_spec()}. +-type subsystem_daemon_option() :: {subsystems, subsystem_specs()}. +-type subsystem_specs() :: [ subsystem_spec() ]. -type shell_daemon_option() :: {shell, mod_fun_args() | 'shell_fun/1'() | 'shell_fun/2'() }. -type 'shell_fun/1'() :: fun((User::string()) -> pid()) . @@ -459,14 +479,6 @@ recv_ext_info }). --record(ssh_key, - { - type, - public, - private, - comment = "" - }). - -record(ssh_pty, {term = "", % e.g. "xterm" width = 80, height = 25, @@ -474,13 +486,6 @@ pixel_height = 768, modes = <<>>}). -%% assertion macro --define(ssh_assert(Expr, Reason), - case Expr of - true -> ok; - _ -> exit(Reason) - end). - %% dbg help macros -define(wr_record(N,BlackList), diff --git a/lib/ssh/src/ssh_message.erl b/lib/ssh/src/ssh_message.erl index d95e58c1bb..7c86a81108 100644 --- a/lib/ssh/src/ssh_message.erl +++ b/lib/ssh/src/ssh_message.erl @@ -34,24 +34,6 @@ -export([dbg_trace/3]). --define('2bin'(X), (if is_binary(X) -> X; - is_list(X) -> list_to_binary(X); - X==undefined -> <<>> - end) ). - --define('E...'(X), ?'2bin'(X)/binary ). --define(Eboolean(X), ?BOOLEAN(case X of - true -> ?TRUE; - false -> ?FALSE - end) ). --define(Ebyte(X), ?BYTE(X) ). --define(Euint32(X), ?UINT32(X) ). --define(Estring(X), ?STRING(?'2bin'(X)) ). --define(Estring_utf8(X), ?string_utf8(X)/binary ). --define(Ename_list(X), ?STRING(ssh_bits:name_list(X)) ). --define(Empint(X), (ssh_bits:mpint(X))/binary ). --define(Ebinary(X), ?STRING(X) ). - ucl(B) -> try unicode:characters_to_list(B) of L when is_list(L) -> L; diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl index eaab13433a..a85926354e 100644 --- a/lib/ssh/src/ssh_transport.erl +++ b/lib/ssh/src/ssh_transport.erl @@ -61,14 +61,6 @@ -export([pack/3, adjust_algs_for_peer_version/2]). -export([decompress/2, decrypt_blocks/3, is_valid_mac/3 ]). % FIXME: remove --define(Estring(X), ?STRING((if is_binary(X) -> X; - is_list(X) -> list_to_binary(X); - X==undefined -> <<>> - end))). --define(Empint(X), (ssh_bits:mpint(X))/binary ). --define(Ebinary(X), ?STRING(X) ). --define(Euint32(X), ?UINT32(X) ). - %%%---------------------------------------------------------------------------- %%% %%% There is a difference between supported and default algorithms. The diff --git a/lib/ssh/src/ssh_userauth.hrl b/lib/ssh/src/ssh_userauth.hrl deleted file mode 100644 index 2cfc1f0f83..0000000000 --- a/lib/ssh/src/ssh_userauth.hrl +++ /dev/null @@ -1,78 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2005-2016. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%% -%% %CopyrightEnd% -%% - -%% - -%%% Description: user authentication protocol - --define(SSH_MSG_USERAUTH_REQUEST, 50). --define(SSH_MSG_USERAUTH_FAILURE, 51). --define(SSH_MSG_USERAUTH_SUCCESS, 52). --define(SSH_MSG_USERAUTH_BANNER, 53). --define(SSH_MSG_USERAUTH_PK_OK, 60). --define(SSH_MSG_USERAUTH_PASSWD_CHANGEREQ, 60). --define(SSH_MSG_USERAUTH_INFO_REQUEST, 60). --define(SSH_MSG_USERAUTH_INFO_RESPONSE, 61). - --record(ssh_msg_userauth_request, - { - user, %% string - service, %% string - method, %% string "publickey", "password" - data %% opaque - }). - --record(ssh_msg_userauth_failure, - { - authentications, %% string - partial_success %% boolean - }). - --record(ssh_msg_userauth_success, - { - }). - --record(ssh_msg_userauth_banner, - { - message, %% string - language %% string - }). - --record(ssh_msg_userauth_passwd_changereq, - { - prompt, %% string - languge %% string - }). - --record(ssh_msg_userauth_pk_ok, - { - algorithm_name, % string - key_blob % string - }). - --record(ssh_msg_userauth_info_request, - {name, - instruction, - language_tag, - num_prompts, - data}). --record(ssh_msg_userauth_info_response, - {num_responses, - data}). diff --git a/lib/ssh/test/ssh_bench_SUITE.erl b/lib/ssh/test/ssh_bench_SUITE.erl index 880c519a5e..5ff7a71c45 100644 --- a/lib/ssh/test/ssh_bench_SUITE.erl +++ b/lib/ssh/test/ssh_bench_SUITE.erl @@ -26,7 +26,7 @@ -include_lib("ssh/src/ssh.hrl"). -include_lib("ssh/src/ssh_transport.hrl"). -include_lib("ssh/src/ssh_connect.hrl"). --include_lib("ssh/src/ssh_userauth.hrl"). +-include_lib("ssh/src/ssh_auth.hrl"). %%%================================================================ %%% diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml index 5fdcf15b5f..c7a0942932 100644 --- a/lib/ssl/doc/src/notes.xml +++ b/lib/ssl/doc/src/notes.xml @@ -27,6 +27,114 @@ </header> <p>This document describes the changes made to the SSL application.</p> +<section><title>SSL 9.3.4</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fix handling of certificate decoding problems in TLS 1.3 + similarly as in TLS 1.2.</p> + <p> + Own Id: OTP-15900</p> + </item> + <item> + <p> + Hibernation now works as expected in all cases, was + accidently broken by optimization efforts.</p> + <p> + Own Id: OTP-15910</p> + </item> + <item> + <p> + Fix interoperability problems with openssl when the TLS + 1.3 server is configured wirh the option + signature_algs_cert.</p> + <p> + Own Id: OTP-15913</p> + </item> + </list> + </section> + +</section> + +<section><title>SSL 9.3.3</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Correct handshake handling, might cause strange symptoms + such as ASN.1 certificate decoding issues.</p> + <p> + Own Id: OTP-15879 Aux Id: ERL-968 </p> + </item> + <item> + <p> + Fix handling of the signature_algorithms_cert extension + in the ClientHello handshake message.</p> + <p> + Own Id: OTP-15887 Aux Id: ERL-973 </p> + </item> + <item> + <p> + Handle new ClientHello extensions when handshake is + paused by the {handshake, hello} ssl option.</p> + <p> + Own Id: OTP-15888 Aux Id: ERL-975 </p> + </item> + </list> + </section> + +</section> + +<section><title>SSL 9.3.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Returned "alert error string" is now same as logged alert + string</p> + <p> + Own Id: OTP-15844</p> + </item> + <item> + <p> + Fix returned extension map fields to follow the + documentation.</p> + <p> + Own Id: OTP-15862 Aux Id: ERL-951 </p> + </item> + <item> + <p> + Avoid DTLS crash due to missing gen_server return value + in DTLS packet demux process.</p> + <p> + Own Id: OTP-15864 Aux Id: ERL-962 </p> + </item> + </list> + </section> + +</section> + +<section><title>SSL 9.3.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Missing check of size of user_data_buffer made internal + socket behave as an active socket instead of active N. + This could cause memory problems.</p> + <p> + Own Id: OTP-15825 Aux Id: ERL-934, OTP-15823 </p> + </item> + </list> + </section> + +</section> + <section><title>SSL 9.3</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -102,6 +210,71 @@ </section> +<section><title>SSL 9.2.3.4</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Hibernation now works as expected in all cases, was + accidently broken by optimization efforts.</p> + <p> + Own Id: OTP-15910</p> + </item> + </list> + </section> + +</section> + +<section><title>SSL 9.2.3.3</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Correct handshake handling, might cause strange symptoms + such as ASN.1 certificate decoding issues.</p> + <p> + Own Id: OTP-15879 Aux Id: ERL-968 </p> + </item> + </list> + </section> + +</section> + +<section><title>SSL 9.2.3.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Returned "alert error string" is now same as logged alert + string</p> + <p> + Own Id: OTP-15844</p> + </item> + </list> + </section> + +</section> + +<section><title>SSL 9.2.3.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Correct solution for retaining tcp flow control OTP-15802 + (ERL-934) as to not break ssl:recv as reported in + (ERL-938)</p> + <p> + Own Id: OTP-15823 Aux Id: ERL-934, ERL-938 </p> + </item> + </list> + </section> + +</section> + <section><title>SSL 9.2.3</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/ssl/doc/src/standards_compliance.xml b/lib/ssl/doc/src/standards_compliance.xml index ca98385f85..9df48b99d3 100644 --- a/lib/ssl/doc/src/standards_compliance.xml +++ b/lib/ssl/doc/src/standards_compliance.xml @@ -126,29 +126,28 @@ <section> <title>TLS 1.3</title> - <p>OTP-22 introduces basic support for TLS 1.3 on the server side. Basic functionality + <p>OTP-22 introduces basic support for TLS 1.3. Basic functionality covers a simple TLS 1.3 handshake with support of the mandatory extensions (supported_groups, signature_algorithms, key_share, supported_versions and - signature_algorithms_cert). The server supports a selective set of cryptographic algorithms:</p> + signature_algorithms_cert). The current implementation supports a selective set of cryptographic algorithms:</p> <list type="bulleted"> <item>Key Exchange: ECDHE</item> <item>Groups: all standard groups supported for the Diffie-Hellman key exchange</item> <item>Ciphers: TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384, TLS_CHACHA20_POLY1305_SHA256 and TLS_AES_128_CCM_SHA256</item> - <item>Signature Algorithms: RSA and RSA PSS</item> - <item>Certificates: currently only certificates with RSA keys are supported</item> + <item>Signature Algorithms: rsa_pkcs1_sha256, rsa_pkcs1_sha384, rsa_pkcs1_sha512, + ecdsa_secp256r1_sha256, ecdsa_secp384r1_sha384, ecdsa_secp521r1_sha512, rsa_pss_rsae_sha256, + rsa_pss_rsae_sha384, rsa_pss_rsae_sha512, rsa_pkcs1_sha1 and ecdsa_sha1</item> + <item>Certificates: RSA (it MUST use the rsaEncryption OID) and ECDSA keys</item> </list> <p>Other notable features:</p> <list type="bulleted"> - <item>The server supports the HelloRetryRequest mechanism</item> <item>PSK and session resumption not supported</item> <item>Early data and 0-RTT not supported</item> <item>Key and Initialization Vector Update not supported</item> </list> <p>For more detailed information see the <seealso marker="#soc_table">Standards Compliance</seealso> below.</p> - <warning><p>Note that the client side is not yet functional. It is planned to be released - later in OTP-22.</p></warning> <p> The following table describes the current state of standards compliance for TLS 1.3.</p> <p>(<em>C</em> = Compliant, <em>NC</em> = Non-Compliant, <em>PC</em> = Partially-Compliant, @@ -176,25 +175,25 @@ <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">Version downgrade protection mechanism</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">RSASSA-PSS signature schemes</cell> <cell align="left" valign="middle"><em>PC</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">supported_versions (ClientHello) extension</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">signature_algorithms_cert extension</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> @@ -211,7 +210,7 @@ <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">(EC)DHE</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -295,8 +294,8 @@ </url> </cell> <cell align="left" valign="middle"><em>Client</em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>PC</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -319,14 +318,14 @@ <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">supported_groups (RFC7919)</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">signature_algorithms (RFC8446)</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -343,8 +342,8 @@ <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">application_layer_protocol_negotiation (RFC7301)</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -373,8 +372,8 @@ <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">key_share (RFC8446)</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -403,8 +402,8 @@ <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">supported_versions (RFC8446)</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -427,8 +426,8 @@ <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">signature_algorithms_cert (RFC8446)</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> @@ -459,13 +458,13 @@ <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">supported_groups (RFC7919)</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">signature_algorithms (RFC8446)</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -482,8 +481,8 @@ <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">application_layer_protocol_negotiation (RFC7301)</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -513,7 +512,7 @@ <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">key_share (RFC8446)</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -543,7 +542,7 @@ <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">supported_versions (RFC8446)</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -567,7 +566,7 @@ <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">signature_algorithms_cert (RFC8446)</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> @@ -577,20 +576,20 @@ </url> </cell> <cell align="left" valign="middle"><em>Client</em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>PC</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">Version downgrade protection</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">key_share (RFC8446)</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -601,8 +600,8 @@ <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">supported_versions (RFC8446)</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> @@ -615,13 +614,13 @@ <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">Version downgrade protection</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">key_share (RFC8446)</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -633,7 +632,7 @@ <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">supported_versions (RFC8446)</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> @@ -650,7 +649,7 @@ <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">key_share (RFC8446)</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -662,7 +661,7 @@ <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">supported_versions (RFC8446)</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> @@ -672,8 +671,8 @@ </url> </cell> <cell align="left" valign="middle"><em>Client</em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -706,62 +705,62 @@ </url> </cell> <cell align="left" valign="middle"><em>Client</em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>PC</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">rsa_pkcs1_sha256</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">rsa_pkcs1_sha384</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">rsa_pkcs1_sha512</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">ecdsa_secp256r1_sha256</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">ecdsa_secp384r1_sha384</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">ecdsa_secp521r1_sha512</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">rsa_pss_rsae_sha256</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">rsa_pss_rsae_sha384</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">rsa_pss_rsae_sha512</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -796,14 +795,14 @@ <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">rsa_pkcs1_sha1</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">ecdsa_sha1</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> @@ -816,55 +815,55 @@ <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">rsa_pkcs1_sha256</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">rsa_pkcs1_sha384</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">rsa_pkcs1_sha512</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">ecdsa_secp256r1_sha256</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">ecdsa_secp384r1_sha384</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">ecdsa_secp521r1_sha512</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">rsa_pss_rsae_sha256</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">rsa_pss_rsae_sha384</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">rsa_pss_rsae_sha512</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -900,13 +899,13 @@ <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">rsa_pkcs1_sha1</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">ecdsa_sha1</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> @@ -967,68 +966,68 @@ </url> </cell> <cell align="left" valign="middle"><em>Client</em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">secp256r1</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">secp384r1</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">secp521r1</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">x25519</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">x448</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">ffdhe2048</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">ffdhe3072</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">ffdhe4096</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">ffdhe6144</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">ffdhe8192</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> @@ -1105,8 +1104,8 @@ </url> </cell> <cell align="left" valign="middle"><em>Client</em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -1224,8 +1223,8 @@ </url> </cell> <cell align="left" valign="middle"><em>Client</em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>PC</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -1362,8 +1361,8 @@ </url> </cell> <cell align="left" valign="middle"><em>Client</em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>PC</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -1374,8 +1373,8 @@ <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">signature_algorithms (RFC8446)</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -1398,8 +1397,8 @@ <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">signature_algorithms_cert (RFC8446)</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> @@ -1417,8 +1416,8 @@ <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">signature_algorithms (RFC8446)</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -1441,8 +1440,8 @@ <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">signature_algorithms_cert (RFC8446)</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> @@ -1463,8 +1462,8 @@ </url> </cell> <cell align="left" valign="middle"><em>Client</em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>PC</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -1521,73 +1520,82 @@ 4.4.2.2. Server Certificate Selection </url> </cell> - <cell align="left" valign="middle"><em>Client</em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>PC</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> - <cell align="left" valign="middle">certificate type MUST be X.509v3</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle">The certificate type MUST be X.509v3, unless explicitly + negotiated otherwise</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> - <cell align="left" valign="middle">certificate's public key is compatible</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle">The server's end-entity certificate's public key (and associated + restrictions) MUST be compatible with the selected authentication + algorithm from the client's "signature_algorithms" extension + (currently RSA, ECDSA, or EdDSA).</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> - <cell align="left" valign="middle">The certificate MUST allow the key to be used for signing</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle">The certificate MUST allow the key to be used for signing + with a signature scheme indicated in the client's "signature_algorithms"/"signature_algorithms_cert" + extensions</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> - <cell align="left" valign="middle">server_name and certificate_authorities are used</cell> + <cell align="left" valign="middle">The "server_name" and "certificate_authorities" + extensions are used to guide certificate selection. As servers + MAY require the presence of the "server_name" extension, clients + SHOULD send this extension, when applicable.</cell> <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"></cell> </row> <row> - <cell align="left" valign="middle"></cell> - <cell align="left" valign="middle"><em>Server</em></cell> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-4.4.2.3"> + 4.4.2.3. Client Certificate Selection + </url> + </cell> + <cell align="left" valign="middle"><em></em></cell> <cell align="left" valign="middle"><em>PC</em></cell> - <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> - <cell align="left" valign="middle">certificate type MUST be X.509v3</cell> + <cell align="left" valign="middle">The certificate type MUST be X.509v3, unless explicitly + negotiated otherwise</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle"><em>22</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> - <cell align="left" valign="middle">certificate's public key is compatible</cell> - <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle"><em>22</em></cell> + <cell align="left" valign="middle">If the "certificate_authorities" extension in the + CertificateRequest message was present, at least one of the + certificates in the certificate chain SHOULD be issued by one of + the listed CAs.</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"><em></em></cell> </row> <row> <cell align="left" valign="middle"></cell> - <cell align="left" valign="middle">The certificate MUST allow the key to be used for signing</cell> + <cell align="left" valign="middle">The certificates MUST be signed using an acceptable signature + algorithm</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle"><em>22</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> - <cell align="left" valign="middle">server_name and certificate_authorities are used</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> - </row> - - <row> - <cell align="left" valign="middle"> - <url href="https://tools.ietf.org/html/rfc8446#section-4.4.2.3"> - 4.4.2.3. Client Certificate Selection - </url> - </cell> - <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle">If the CertificateRequest message contained a non-empty + "oid_filters" extension, the end-entity certificate MUST match the + extension OIDs that are recognized by the client</cell> <cell align="left" valign="middle"><em>NC</em></cell> <cell align="left" valign="middle"><em></em></cell> </row> @@ -1599,8 +1607,8 @@ </url> </cell> <cell align="left" valign="middle"><em>Client</em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -1616,8 +1624,8 @@ </url> </cell> <cell align="left" valign="middle"><em>Client</em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -1633,8 +1641,8 @@ </url> </cell> <cell align="left" valign="middle"><em>Client</em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -1738,25 +1746,25 @@ <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">MUST NOT be interleaved with other record types</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">MUST NOT span key changes</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">MUST NOT send zero-length fragments</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">Alert messages MUST NOT be fragmented</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> @@ -1807,7 +1815,7 @@ <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">The padding sent is automatically verified</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> @@ -1950,51 +1958,51 @@ </url> </cell> <cell align="left" valign="middle"><em></em></cell> - <cell align="left" valign="middle"><em>PC</em></cell> - <cell align="left" valign="middle"><em>22</em></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">MUST implement the TLS_AES_128_GCM_SHA256</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">SHOULD implement the TLS_AES_256_GCM_SHA384</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">SHOULD implement the TLS_CHACHA20_POLY1305_SHA256</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle"><em>Digital signatures</em></cell> - <cell align="left" valign="middle"><em>PC</em></cell> - <cell align="left" valign="middle"><em>22</em></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">MUST support rsa_pkcs1_sha256 (for certificates)</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">MUST support rsa_pss_rsae_sha256 (for CertificateVerify and certificates)</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">MUST support ecdsa_secp256r1_sha256</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> @@ -2007,13 +2015,13 @@ <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">MUST support key exchange with secp256r1</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">SHOULD support key exchange with X25519</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> @@ -2030,7 +2038,7 @@ <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">Supported Versions</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -2042,25 +2050,25 @@ <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">Signature Algorithms</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">Signature Algorithms Certificate</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">Negotiated Groups</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">Key Share</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -2072,32 +2080,32 @@ <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle"><em>MUST send and use these extensions</em></cell> - <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>PC</em></cell> <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">"supported_versions" is REQUIRED for ClientHello, ServerHello and HelloRetryRequest</cell> - <cell align="left" valign="middle"><em>PC</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">"signature_algorithms" is REQUIRED for certificate authentication</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">"supported_groups" is REQUIRED for ClientHello messages using (EC)DHE key exchange</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">"key_share" is REQUIRED for (EC)DHE key exchange</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -2115,20 +2123,20 @@ <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle"><em>TLS 1.3 ClientHello</em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">If not containing a "pre_shared_key" extension, it MUST contain both a "signature_algorithms" extension and a "supported_groups" extension.</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">If containing a "supported_groups" extension, it MUST also contain a "key_share" extension, and vice versa. An empty KeyShare.client_shares vector is permitted.</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> @@ -2151,30 +2159,44 @@ </url> </cell> <cell align="left" valign="middle"><em></em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle"><em>MUST correctly handle extensible fields</em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> - <cell align="left" valign="middle">A client sending a ClientHello MUST support all parameters advertised in it.</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">A client sending a ClientHello MUST support all parameters + advertised in it. Otherwise, the server may fail to interoperate by selecting one of those parameters.</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> - <cell align="left" valign="middle">A middlebox which terminates a TLS connection MUST behave as a compliant TLS server</cell> + <cell align="left" valign="middle">A server receiving a ClientHello MUST correctly ignore all + unrecognized cipher suites, extensions, and other parameters. Otherwise, it may fail to + interoperate with newer clients. In TLS 1.3, a client receiving a CertificateRequest or + NewSessionTicket MUST also ignore all unrecognized extensions.</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> + </row> + + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">A middlebox which terminates a TLS connection MUST behave as a + compliant TLS server</cell> <cell align="left" valign="middle"><em>NA</em></cell> <cell align="left" valign="middle"></cell> </row> <row> <cell align="left" valign="middle"></cell> - <cell align="left" valign="middle">A middlebox which forwards ClientHello parameters it does not understand MUST NOT process any messages beyond that ClientHello.</cell> + <cell align="left" valign="middle">A middlebox which forwards ClientHello parameters it does not + understand MUST NOT process any messages beyond that ClientHello. It MUST forward all subsequent + traffic unmodified. Otherwise, it may fail to interoperate with newer clients and servers.</cell> <cell align="left" valign="middle"><em>NA</em></cell> <cell align="left" valign="middle"></cell> </row> @@ -2193,25 +2215,25 @@ <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">TLS_AES_128_GCM_SHA256</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">TLS_AES_256_GCM_SHA384</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">TLS_CHACHA20_POLY1305_SHA256</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">TLS_AES_128_CCM_SHA256</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index e070006900..b220691e79 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -51,7 +51,7 @@ -export([encode_alert/3, send_alert/2, send_alert_in_connection/2, close/5, protocol_name/0]). %% Data handling --export([next_record/1, socket/4, setopts/3, getopts/3]). +-export([socket/4, setopts/3, getopts/3]). %% gen_statem state functions -export([init/3, error/3, downgrade/3, %% Initiation and take down states @@ -451,11 +451,11 @@ init({call, From}, {start, Timeout}, HelloVersion = dtls_record:hello_version(Version, SslOpts#ssl_options.versions), State1 = prepare_flight(State0#state{connection_env = CEnv#connection_env{negotiated_version = Version}}), {State2, Actions} = send_handshake(Hello, State1#state{connection_env = CEnv#connection_env{negotiated_version = HelloVersion}}), - State3 = State2#state{connection_env = CEnv#connection_env{negotiated_version = Version}, %% RequestedVersion + State = State2#state{connection_env = CEnv#connection_env{negotiated_version = Version}, %% RequestedVersion session = Session0#session{session_id = Hello#client_hello.session_id}, start_or_recv_from = From}, - next_event(hello, no_record, State3, [{{timeout, handshake}, Timeout, close} | Actions]); + next_event(hello, no_record, State, [{{timeout, handshake}, Timeout, close} | Actions]); init({call, _} = Type, Event, #state{static_env = #static_env{role = server}, protocol_specific = PS} = State) -> Result = gen_handshake(?FUNCTION_NAME, Type, Event, @@ -514,7 +514,7 @@ hello(internal, #client_hello{cookie = <<>>, VerifyRequest = dtls_handshake:hello_verify_request(Cookie, ?HELLO_VERIFY_REQUEST_VERSION), State1 = prepare_flight(State0#state{connection_env = CEnv#connection_env{negotiated_version = Version}}), {State, Actions} = send_handshake(VerifyRequest, State1), - next_event(?FUNCTION_NAME, no_record, + next_event(?FUNCTION_NAME, no_record, State#state{handshake_env = HsEnv#handshake_env{ tls_handshake_history = ssl_handshake:init_handshake_history()}}, @@ -836,9 +836,12 @@ initial_flight_state(_) -> next_dtls_record(Data, StateName, #state{protocol_buffers = #protocol_buffers{ dtls_record_buffer = Buf0, dtls_cipher_texts = CT0} = Buffers, + connection_env = #connection_env{negotiated_version = Version}, + static_env = #static_env{data_tag = DataTag}, ssl_options = SslOpts} = State0) -> case dtls_record:get_dtls_records(Data, - acceptable_record_versions(StateName, State0), + {DataTag, StateName, Version, + [dtls_record:protocol_version(Vsn) || Vsn <- ?ALL_AVAILABLE_DATAGRAM_VERSIONS]}, Buf0, SslOpts) of {Records, Buf1} -> CT1 = CT0 ++ Records, @@ -849,10 +852,6 @@ next_dtls_record(Data, StateName, #state{protocol_buffers = #protocol_buffers{ Alert end. -acceptable_record_versions(hello, _) -> - [dtls_record:protocol_version(Vsn) || Vsn <- ?ALL_AVAILABLE_DATAGRAM_VERSIONS]; -acceptable_record_versions(_, #state{connection_env = #connection_env{negotiated_version = Version}}) -> - [Version]. dtls_handshake_events(Packets) -> lists:map(fun(Packet) -> diff --git a/lib/ssl/src/dtls_packet_demux.erl b/lib/ssl/src/dtls_packet_demux.erl index c6431b55a9..94b350eaa5 100644 --- a/lib/ssl/src/dtls_packet_demux.erl +++ b/lib/ssl/src/dtls_packet_demux.erl @@ -154,9 +154,9 @@ handle_info({Transport, Socket, IP, InPortNo, _} = Msg, #state{listener = Socket handle_info({PassiveTag, Socket}, #state{active_n = N, listener = Socket, - transport = {_,_,_, udp_error, PassiveTag}}) -> - next_datagram(Socket, N); - + transport = {_, _, _, _, PassiveTag}} = State) -> + next_datagram(Socket, N), + {noreply, State}; %% UDP socket does not have a connection and should not receive an econnreset %% This does however happens on some windows versions. Just ignoring it %% appears to make things work as expected! diff --git a/lib/ssl/src/dtls_record.erl b/lib/ssl/src/dtls_record.erl index a4846f42c5..8b8db7b2de 100644 --- a/lib/ssl/src/dtls_record.erl +++ b/lib/ssl/src/dtls_record.erl @@ -162,26 +162,16 @@ current_connection_state_epoch(#{current_write := #{epoch := Epoch}}, Epoch. %%-------------------------------------------------------------------- --spec get_dtls_records(binary(), [ssl_record:ssl_version()], binary(), +-spec get_dtls_records(binary(), {atom(), atom(), ssl_record:ssl_version(), [ssl_record:ssl_version()]}, binary(), #ssl_options{}) -> {[binary()], binary()} | #alert{}. %% %% Description: Given old buffer and new data from UDP/SCTP, packs up a records %% and returns it as a list of tls_compressed binaries also returns leftover %% data %%-------------------------------------------------------------------- -get_dtls_records(Data, Versions, Buffer, SslOpts) -> +get_dtls_records(Data, Vinfo, Buffer, SslOpts) -> BinData = list_to_binary([Buffer, Data]), - case erlang:byte_size(BinData) of - N when N >= 3 -> - case assert_version(BinData, Versions) of - true -> - get_dtls_records_aux(BinData, [], SslOpts); - false -> - ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC) - end; - _ -> - get_dtls_records_aux(BinData, [], SslOpts) - end. + get_dtls_records_aux(Vinfo, BinData, [], SslOpts). %%==================================================================== %% Encoding DTLS records @@ -405,52 +395,49 @@ initial_connection_state(ConnectionEnd, BeastMitigation) -> client_verify_data => undefined, server_verify_data => undefined }. -assert_version(<<?BYTE(_), ?BYTE(MajVer), ?BYTE(MinVer), _/binary>>, Versions) -> - is_acceptable_version({MajVer, MinVer}, Versions). -get_dtls_records_aux(<<?BYTE(?APPLICATION_DATA),?BYTE(MajVer),?BYTE(MinVer), - ?UINT16(Epoch), ?UINT48(SequenceNumber), - ?UINT16(Length), Data:Length/binary, Rest/binary>> = RawDTLSRecord, - Acc, SslOpts) -> - ssl_logger:debug(SslOpts#ssl_options.log_level, inbound, 'record', [RawDTLSRecord]), - get_dtls_records_aux(Rest, [#ssl_tls{type = ?APPLICATION_DATA, - version = {MajVer, MinVer}, - epoch = Epoch, sequence_number = SequenceNumber, - fragment = Data} | Acc], SslOpts); -get_dtls_records_aux(<<?BYTE(?HANDSHAKE),?BYTE(MajVer),?BYTE(MinVer), - ?UINT16(Epoch), ?UINT48(SequenceNumber), - ?UINT16(Length), - Data:Length/binary, Rest/binary>> = RawDTLSRecord, - Acc, SslOpts) when MajVer >= 128 -> - ssl_logger:debug(SslOpts#ssl_options.log_level, inbound, 'record', [RawDTLSRecord]), - get_dtls_records_aux(Rest, [#ssl_tls{type = ?HANDSHAKE, - version = {MajVer, MinVer}, - epoch = Epoch, sequence_number = SequenceNumber, - fragment = Data} | Acc], SslOpts); -get_dtls_records_aux(<<?BYTE(?ALERT),?BYTE(MajVer),?BYTE(MinVer), - ?UINT16(Epoch), ?UINT48(SequenceNumber), - ?UINT16(Length), Data:Length/binary, - Rest/binary>> = RawDTLSRecord, Acc, SslOpts) -> +get_dtls_records_aux({DataTag, StateName, _, Versions} = Vinfo, <<?BYTE(Type),?BYTE(MajVer),?BYTE(MinVer), + ?UINT16(Epoch), ?UINT48(SequenceNumber), + ?UINT16(Length), Data:Length/binary, Rest/binary>> = RawDTLSRecord, + Acc, SslOpts) when ((StateName == hello) orelse + ((StateName == certify) andalso (DataTag == udp)) orelse + ((StateName == abbreviated) andalso(DataTag == udp))) + andalso + ((Type == ?HANDSHAKE) orelse + (Type == ?ALERT)) -> ssl_logger:debug(SslOpts#ssl_options.log_level, inbound, 'record', [RawDTLSRecord]), - get_dtls_records_aux(Rest, [#ssl_tls{type = ?ALERT, - version = {MajVer, MinVer}, - epoch = Epoch, sequence_number = SequenceNumber, - fragment = Data} | Acc], SslOpts); -get_dtls_records_aux(<<?BYTE(?CHANGE_CIPHER_SPEC),?BYTE(MajVer),?BYTE(MinVer), + case is_acceptable_version({MajVer, MinVer}, Versions) of + true -> + get_dtls_records_aux(Vinfo, Rest, [#ssl_tls{type = Type, + version = {MajVer, MinVer}, + epoch = Epoch, sequence_number = SequenceNumber, + fragment = Data} | Acc], SslOpts); + false -> + ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC) + end; +get_dtls_records_aux({_, _, Version, _} = Vinfo, <<?BYTE(Type),?BYTE(MajVer),?BYTE(MinVer), ?UINT16(Epoch), ?UINT48(SequenceNumber), ?UINT16(Length), Data:Length/binary, Rest/binary>> = RawDTLSRecord, - Acc, SslOpts) -> + Acc, SslOpts) when (Type == ?APPLICATION_DATA) orelse + (Type == ?HANDSHAKE) orelse + (Type == ?ALERT) orelse + (Type == ?CHANGE_CIPHER_SPEC) -> ssl_logger:debug(SslOpts#ssl_options.log_level, inbound, 'record', [RawDTLSRecord]), - get_dtls_records_aux(Rest, [#ssl_tls{type = ?CHANGE_CIPHER_SPEC, - version = {MajVer, MinVer}, - epoch = Epoch, sequence_number = SequenceNumber, - fragment = Data} | Acc], SslOpts); -get_dtls_records_aux(<<?BYTE(_), ?BYTE(_MajVer), ?BYTE(_MinVer), + case {MajVer, MinVer} of + Version -> + get_dtls_records_aux(Vinfo, Rest, [#ssl_tls{type = Type, + version = {MajVer, MinVer}, + epoch = Epoch, sequence_number = SequenceNumber, + fragment = Data} | Acc], SslOpts); + _ -> + ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC) + end; +get_dtls_records_aux(_, <<?BYTE(_), ?BYTE(_MajVer), ?BYTE(_MinVer), ?UINT16(Length), _/binary>>, _Acc, _) when Length > ?MAX_CIPHER_TEXT_LENGTH -> ?ALERT_REC(?FATAL, ?RECORD_OVERFLOW); -get_dtls_records_aux(Data, Acc, _) -> +get_dtls_records_aux(_, Data, Acc, _) -> case size(Data) =< ?MAX_CIPHER_TEXT_LENGTH + ?INITIAL_BYTES of true -> {lists:reverse(Acc), Data}; diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index 6af65e09f2..20b1e85ceb 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -125,7 +125,10 @@ protocol_extensions/0, session_id/0, error_alert/0, - srp_param_type/0]). + tls_alert/0, + srp_param_type/0, + named_curve/0, + sign_scheme/0]). %% ------------------------------------------------------------------------------------------------------- @@ -191,7 +194,8 @@ | rsa_pss_pss_sha384 | rsa_pss_pss_sha512 | rsa_pkcs1_sha1 - | ecdsa_sha1. + | ecdsa_sha1. % exported + -type kex_algo() :: rsa | dhe_rsa | dhe_dss | ecdhe_ecdsa | ecdh_ecdsa | ecdh_rsa | @@ -236,7 +240,7 @@ sect163r2 | secp160k1 | secp160r1 | - secp160r2. + secp160r2. % exported -type group() :: secp256r1 | secp384r1 | secp521r1 | ffdhe2048 | ffdhe3072 | ffdhe4096 | ffdhe6144 | ffdhe8192. @@ -279,7 +283,7 @@ bad_certificate_status_response | bad_certificate_hash_value | unknown_psk_identity | - no_application_protocol. + no_application_protocol. % exported %% ------------------------------------------------------------------------------------------------------- -type common_option() :: {protocol, protocol()} | @@ -1909,7 +1913,7 @@ validate_option(Opt, Value) -> throw({error, {options, {Opt, Value}}}). handle_cb_info({V1, V2, V3, V4}, {_,_,_,_,_}) -> - {V1,V2,V3,V4, list_to_atom(atom_to_list(V2) ++ "passive")}; + {V1,V2,V3,V4, list_to_atom(atom_to_list(V2) ++ "_passive")}; handle_cb_info(CbInfo, _) -> CbInfo. diff --git a/lib/ssl/src/ssl_alert.erl b/lib/ssl/src/ssl_alert.erl index 06b1b005a5..2d57b72f7b 100644 --- a/lib/ssl/src/ssl_alert.erl +++ b/lib/ssl/src/ssl_alert.erl @@ -32,7 +32,11 @@ -include("ssl_record.hrl"). -include("ssl_internal.hrl"). --export([decode/1, own_alert_txt/1, alert_txt/1, reason_code/2]). +-export([decode/1, + own_alert_txt/1, + alert_txt/1, + alert_txt/4, + reason_code/4]). %%==================================================================== %% Internal application API @@ -48,20 +52,29 @@ decode(Bin) -> decode(Bin, [], 0). %%-------------------------------------------------------------------- -%% -spec reason_code(#alert{}, client | server) -> -%% {tls_alert, unicode:chardata()} | closed. -%-spec reason_code(#alert{}, client | server) -> closed | {essl, string()}. +-spec reason_code(#alert{}, client | server, ProtocolName::string(), StateName::atom()) -> + {tls_alert, {atom(), unicode:chardata()}} | closed. %% %% Description: Returns the error reason that will be returned to the %% user. %%-------------------------------------------------------------------- -reason_code(#alert{description = ?CLOSE_NOTIFY}, _) -> +reason_code(#alert{description = ?CLOSE_NOTIFY}, _, _, _) -> closed; -reason_code(#alert{description = Description, role = Role} = Alert, Role) -> - {tls_alert, {description_atom(Description), own_alert_txt(Alert)}}; -reason_code(#alert{description = Description} = Alert, Role) -> - {tls_alert, {description_atom(Description), alert_txt(Alert#alert{role = Role})}}. +reason_code(#alert{description = Description, role = Role} = Alert, Role, ProtocolName, StateName) -> + Txt = lists:flatten(alert_txt(ProtocolName, Role, StateName, own_alert_txt(Alert))), + {tls_alert, {description_atom(Description), Txt}}; +reason_code(#alert{description = Description} = Alert, Role, ProtocolName, StateName) -> + Txt = lists:flatten(alert_txt(ProtocolName, Role, StateName, alert_txt(Alert))), + {tls_alert, {description_atom(Description), Txt}}. + +%%-------------------------------------------------------------------- +-spec alert_txt(string(), server | client, StateNam::atom(), string()) -> string(). +%% +%% Description: Generates alert text for log or string part of error return. +%%-------------------------------------------------------------------- +alert_txt(ProtocolName, Role, StateName, Txt) -> + io_lib:format("~s ~p: In state ~p ~s\n", [ProtocolName, Role, StateName, Txt]). %%-------------------------------------------------------------------- -spec own_alert_txt(#alert{}) -> string(). diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl index 21db887bb5..f4a91cac52 100644 --- a/lib/ssl/src/ssl_cipher.erl +++ b/lib/ssl/src/ssl_cipher.erl @@ -923,6 +923,12 @@ signature_scheme(rsa_pss_pss_sha384) -> ?RSA_PSS_PSS_SHA384; signature_scheme(rsa_pss_pss_sha512) -> ?RSA_PSS_PSS_SHA512; signature_scheme(rsa_pkcs1_sha1) -> ?RSA_PKCS1_SHA1; signature_scheme(ecdsa_sha1) -> ?ECDSA_SHA1; +%% Handling legacy signature algorithms +signature_scheme({Hash0, Sign0}) -> + Hash = hash_algorithm(Hash0), + Sign = sign_algorithm(Sign0), + <<?UINT16(SigAlg)>> = <<?BYTE(Hash),?BYTE(Sign)>>, + SigAlg; signature_scheme(?RSA_PKCS1_SHA256) -> rsa_pkcs1_sha256; signature_scheme(?RSA_PKCS1_SHA384) -> rsa_pkcs1_sha384; signature_scheme(?RSA_PKCS1_SHA512) -> rsa_pkcs1_sha512; @@ -962,18 +968,30 @@ scheme_to_components(rsa_pss_pss_sha256) -> {sha256, rsa_pss_pss, undefined}; scheme_to_components(rsa_pss_pss_sha384) -> {sha384, rsa_pss_pss, undefined}; scheme_to_components(rsa_pss_pss_sha512) -> {sha512, rsa_pss_pss, undefined}; scheme_to_components(rsa_pkcs1_sha1) -> {sha1, rsa_pkcs1, undefined}; -scheme_to_components(ecdsa_sha1) -> {sha1, ecdsa, undefined}. +scheme_to_components(ecdsa_sha1) -> {sha1, ecdsa, undefined}; +%% Handling legacy signature algorithms +scheme_to_components({Hash,Sign}) -> {Hash, Sign, undefined}. -%% TODO: Add support for EC and RSA-SSA signatures -signature_algorithm_to_scheme(#'SignatureAlgorithm'{algorithm = ?sha1WithRSAEncryption}) -> - rsa_pkcs1_sha1; +%% TODO: Add support for ed25519, ed448, rsa_pss* signature_algorithm_to_scheme(#'SignatureAlgorithm'{algorithm = ?sha256WithRSAEncryption}) -> rsa_pkcs1_sha256; signature_algorithm_to_scheme(#'SignatureAlgorithm'{algorithm = ?sha384WithRSAEncryption}) -> rsa_pkcs1_sha384; signature_algorithm_to_scheme(#'SignatureAlgorithm'{algorithm = ?sha512WithRSAEncryption}) -> - rsa_pkcs1_sha512. + rsa_pkcs1_sha512; +signature_algorithm_to_scheme(#'SignatureAlgorithm'{algorithm = ?'ecdsa-with-SHA256'}) -> + ecdsa_secp256r1_sha256; +signature_algorithm_to_scheme(#'SignatureAlgorithm'{algorithm = ?'ecdsa-with-SHA384'}) -> + ecdsa_secp384r1_sha384; +signature_algorithm_to_scheme(#'SignatureAlgorithm'{algorithm = ?'ecdsa-with-SHA512'}) -> + ecdsa_secp512r1_sha512; +signature_algorithm_to_scheme(#'SignatureAlgorithm'{algorithm = ?'sha-1WithRSAEncryption'}) -> + rsa_pkcs1_sha1; +signature_algorithm_to_scheme(#'SignatureAlgorithm'{algorithm = ?sha1WithRSAEncryption}) -> + rsa_pkcs1_sha1; +signature_algorithm_to_scheme(#'SignatureAlgorithm'{algorithm = ?'ecdsa-with-SHA1'}) -> + ecdsa_sha1. %% RFC 5246: 6.2.3.2. CBC Block Cipher diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index a5f754d2e3..cc4d60389e 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -124,7 +124,7 @@ handshake(#sslsocket{pid = [Pid|_]} = Socket, Timeout) -> connected -> {ok, Socket}; {ok, Ext} -> - {ok, Socket, Ext}; + {ok, Socket, no_records(Ext)}; Error -> Error end. @@ -328,34 +328,33 @@ prf(ConnectionPid, Secret, Label, Seed, WantedLength) -> %%==================================================================== %% Alert and close handling %%==================================================================== -handle_own_alert(Alert, _, StateName, +handle_own_alert(Alert0, _, StateName, #state{static_env = #static_env{role = Role, protocol_cb = Connection}, ssl_options = SslOpts} = State) -> try %% Try to tell the other side - send_alert(Alert, StateName, State) + send_alert(Alert0, StateName, State) catch _:_ -> %% Can crash if we are in a uninitialized state ignore end, try %% Try to tell the local user - log_alert(SslOpts#ssl_options.log_level, Role, - Connection:protocol_name(), StateName, - Alert#alert{role = Role}), + Alert = Alert0#alert{role = Role}, + log_alert(SslOpts#ssl_options.log_level, Role, Connection:protocol_name(), StateName, Alert), handle_normal_shutdown(Alert,StateName, State) catch _:_ -> ok end, {stop, {shutdown, own_alert}, State}. -handle_normal_shutdown(Alert, _, #state{static_env = #static_env{role = Role, - socket = Socket, - transport_cb = Transport, - protocol_cb = Connection, - tracker = Tracker}, - handshake_env = #handshake_env{renegotiation = {false, first}}, - start_or_recv_from = StartFrom} = State) -> +handle_normal_shutdown(Alert, StateName, #state{static_env = #static_env{role = Role, + socket = Socket, + transport_cb = Transport, + protocol_cb = Connection, + tracker = Tracker}, + handshake_env = #handshake_env{renegotiation = {false, first}}, + start_or_recv_from = StartFrom} = State) -> Pids = Connection:pids(State), - alert_user(Pids, Transport, Tracker,Socket, StartFrom, Alert, Role, Connection); + alert_user(Pids, Transport, Tracker,Socket, StartFrom, Alert, Role, StateName, Connection); handle_normal_shutdown(Alert, StateName, #state{static_env = #static_env{role = Role, socket = Socket, @@ -366,9 +365,9 @@ handle_normal_shutdown(Alert, StateName, #state{static_env = #static_env{role = socket_options = Opts, start_or_recv_from = RecvFrom} = State) -> Pids = Connection:pids(State), - alert_user(Pids, Transport, Tracker, Socket, StateName, Opts, Pid, RecvFrom, Alert, Role, Connection). + alert_user(Pids, Transport, Tracker, Socket, StateName, Opts, Pid, RecvFrom, Alert, Role, StateName, Connection). -handle_alert(#alert{level = ?FATAL} = Alert, StateName, +handle_alert(#alert{level = ?FATAL} = Alert0, StateName, #state{static_env = #static_env{role = Role, socket = Socket, host = Host, @@ -382,11 +381,11 @@ handle_alert(#alert{level = ?FATAL} = Alert, StateName, session = Session, socket_options = Opts} = State) -> invalidate_session(Role, Host, Port, Session), + Alert = Alert0#alert{role = opposite_role(Role)}, log_alert(SslOpts#ssl_options.log_level, Role, Connection:protocol_name(), - StateName, Alert#alert{role = opposite_role(Role)}), + StateName, Alert), Pids = Connection:pids(State), - alert_user(Pids, Transport, Tracker, Socket, StateName, Opts, Pid, From, Alert, - opposite_role(Role), Connection), + alert_user(Pids, Transport, Tracker, Socket, StateName, Opts, Pid, From, Alert, Role, StateName, Connection), {stop, {shutdown, normal}, State}; handle_alert(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert, @@ -396,13 +395,14 @@ handle_alert(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert, StateName, State) -> handle_normal_shutdown(Alert, StateName, State), {stop,{shutdown, peer_close}, State}; -handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName, +handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert0, StateName, #state{static_env = #static_env{role = Role, protocol_cb = Connection}, handshake_env = #handshake_env{renegotiation = {true, internal}}, ssl_options = SslOpts} = State) -> + Alert = Alert0#alert{role = opposite_role(Role)}, log_alert(SslOpts#ssl_options.log_level, Role, - Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}), + Connection:protocol_name(), StateName, Alert), handle_normal_shutdown(Alert, StateName, State), {stop,{shutdown, peer_close}, State}; @@ -709,6 +709,7 @@ handle_session(#server_hello{cipher_suite = CipherSuite, {ExpectNPN, Protocol} = case Protocol0 of undefined -> + {false, CurrentProtocol}; _ -> {ProtoExt =:= npn, Protocol0} @@ -1194,7 +1195,7 @@ cipher(internal, #next_protocol{selected_protocol = SelectedProtocol}, #state{static_env = #static_env{role = server}, handshake_env = #handshake_env{expecting_finished = true, expecting_next_protocol_negotiation = true} = HsEnv} = State, Connection) -> - Connection:next_event(?FUNCTION_NAME, no_record, + Connection:next_event(?FUNCTION_NAME, no_record, State#state{handshake_env = HsEnv#handshake_env{negotiated_protocol = SelectedProtocol, expecting_next_protocol_negotiation = false}}); cipher(internal, #change_cipher_spec{type = <<1>>}, #state{handshake_env = HsEnv, connection_states = ConnectionStates0} = @@ -1445,7 +1446,7 @@ handle_info({ErrorTag, Socket, econnaborted}, StateName, } = State) when StateName =/= connection -> Pids = Connection:pids(State), alert_user(Pids, Transport, Tracker,Socket, - StartFrom, ?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), Role, Connection), + StartFrom, ?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), Role, StateName, Connection), {stop, {shutdown, normal}, State}; handle_info({ErrorTag, Socket, Reason}, StateName, #state{static_env = #static_env{socket = Socket, @@ -2907,22 +2908,22 @@ send_user(Pid, Msg) -> Pid ! Msg, ok. -alert_user(Pids, Transport, Tracker, Socket, connection, Opts, Pid, From, Alert, Role, Connection) -> - alert_user(Pids, Transport, Tracker, Socket, Opts#socket_options.active, Pid, From, Alert, Role, Connection); -alert_user(Pids, Transport, Tracker, Socket,_, _, _, From, Alert, Role, Connection) -> - alert_user(Pids, Transport, Tracker, Socket, From, Alert, Role, Connection). +alert_user(Pids, Transport, Tracker, Socket, connection, Opts, Pid, From, Alert, Role, StateName, Connection) -> + alert_user(Pids, Transport, Tracker, Socket, Opts#socket_options.active, Pid, From, Alert, Role, StateName, Connection); +alert_user(Pids, Transport, Tracker, Socket,_, _, _, From, Alert, Role, StateName, Connection) -> + alert_user(Pids, Transport, Tracker, Socket, From, Alert, Role, StateName, Connection). -alert_user(Pids, Transport, Tracker, Socket, From, Alert, Role, Connection) -> - alert_user(Pids, Transport, Tracker, Socket, false, no_pid, From, Alert, Role, Connection). +alert_user(Pids, Transport, Tracker, Socket, From, Alert, Role, StateName, Connection) -> + alert_user(Pids, Transport, Tracker, Socket, false, no_pid, From, Alert, Role, StateName, Connection). -alert_user(_, _, _, _, false = Active, Pid, From, Alert, Role, _) when From =/= undefined -> +alert_user(_, _, _, _, false = Active, Pid, From, Alert, Role, StateName, Connection) when From =/= undefined -> %% If there is an outstanding ssl_accept | recv %% From will be defined and send_or_reply will %% send the appropriate error message. - ReasonCode = ssl_alert:reason_code(Alert, Role), + ReasonCode = ssl_alert:reason_code(Alert, Role, Connection:protocol_name(), StateName), send_or_reply(Active, Pid, From, {error, ReasonCode}); -alert_user(Pids, Transport, Tracker, Socket, Active, Pid, From, Alert, Role, Connection) -> - case ssl_alert:reason_code(Alert, Role) of +alert_user(Pids, Transport, Tracker, Socket, Active, Pid, From, Alert, Role, StateName, Connection) -> + case ssl_alert:reason_code(Alert, Role, Connection:protocol_name(), StateName) of closed -> send_or_reply(Active, Pid, From, {ssl_closed, Connection:socket(Pids, Transport, Socket, Tracker)}); @@ -2933,11 +2934,11 @@ alert_user(Pids, Transport, Tracker, Socket, Active, Pid, From, Alert, Role, Con log_alert(Level, Role, ProtocolName, StateName, #alert{role = Role} = Alert) -> Txt = ssl_alert:own_alert_txt(Alert), - Report = io_lib:format("~s ~p: In state ~p ~s\n", [ProtocolName, Role, StateName, Txt]), + Report = ssl_alert:alert_txt(ProtocolName, Role, StateName, Txt), ssl_logger:notice(Level, Report); log_alert(Level, Role, ProtocolName, StateName, Alert) -> Txt = ssl_alert:alert_txt(Alert), - Report = io_lib:format("~s ~p: In state ~p ~s\n", [ProtocolName, Role, StateName, Txt]), + Report = ssl_alert:alert_txt(ProtocolName, Role, StateName, Txt), ssl_logger:notice(Level, Report). invalidate_session(client, Host, Port, Session) -> @@ -3000,3 +3001,8 @@ new_emulated([], EmOpts) -> EmOpts; new_emulated(NewEmOpts, _) -> NewEmOpts. + +no_records(Extensions) -> + maps:map(fun(_, Value) -> + ssl_handshake:extension_value(Value) + end, Extensions). diff --git a/lib/ssl/src/ssl_connection.hrl b/lib/ssl/src/ssl_connection.hrl index ff7207a8ce..844368c761 100644 --- a/lib/ssl/src/ssl_connection.hrl +++ b/lib/ssl/src/ssl_connection.hrl @@ -66,6 +66,7 @@ sni_hostname = undefined, expecting_next_protocol_negotiation = false ::boolean(), next_protocol = undefined :: undefined | binary(), + alpn = undefined, %% Used in TLS 1.3 negotiated_protocol, hashsign_algorithm = {undefined, undefined}, cert_hashsign_algorithm = {undefined, undefined}, @@ -76,7 +77,7 @@ srp_params :: #srp_user{} | secret_printout() | 'undefined', public_key_info :: ssl_handshake:public_key_info() | 'undefined', premaster_secret :: binary() | secret_printout() | 'undefined', - server_psk_identity :: binary() | 'undefined' % server psk identity hint + server_psk_identity :: binary() | 'undefined' % server psk identity hint }). -record(connection_env, { diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index 7b34991f4f..c6698bc74a 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -76,7 +76,8 @@ handle_client_hello_extensions/9, %% Returns server hello extensions handle_server_hello_extensions/9, select_curve/2, select_curve/3, select_hashsign/4, select_hashsign/5, - select_hashsign_algs/3, empty_extensions/2, add_server_share/3 + select_hashsign_algs/3, empty_extensions/2, add_server_share/3, + add_alpn/2, add_selected_version/1, decode_alpn/1 ]). -export([get_cert_params/1, @@ -98,8 +99,8 @@ hello_request() -> #hello_request{}. %%-------------------------------------------------------------------- --spec server_hello(binary(), ssl_record:ssl_version(), ssl_record:connection_states(), - Extension::map()) -> #server_hello{}. +%%-spec server_hello(binary(), ssl_record:ssl_version(), ssl_record:connection_states(), +%% Extension::map()) -> #server_hello{}. %% %% Description: Creates a server hello message. %%-------------------------------------------------------------------- @@ -363,7 +364,7 @@ certify(#certificate{asn1_certificates = ASN1Certs}, CertDbHandle, CertDbRef, CertDbHandle, CertDbRef) end catch - error:{badmatch,{asn1, Asn1Reason}} -> + error:{badmatch,{error, {asn1, Asn1Reason}}} -> %% ASN-1 decode of certificate somehow failed ?ALERT_REC(?FATAL, ?CERTIFICATE_UNKNOWN, {failed_to_decode_certificate, Asn1Reason}); error:OtherReason -> @@ -1165,6 +1166,13 @@ add_server_share(hello_retry_request, Extensions, Extensions#{key_share => #key_share_hello_retry_request{ selected_group = Group}}. +add_alpn(Extensions, ALPN0) -> + ALPN = encode_alpn([ALPN0], false), + Extensions#{alpn => ALPN}. + +add_selected_version(Extensions) -> + SupportedVersions = #server_hello_selected_version{selected_version = {3,4}}, + Extensions#{server_hello_selected_version => SupportedVersions}. kse_remove_private_key(#key_share_entry{ group = Group, @@ -1186,10 +1194,7 @@ signature_algs_ext(undefined) -> signature_algs_ext(SignatureSchemes0) -> %% The SSL option signature_algs contains both hash-sign algorithms (tuples) and %% signature schemes (atoms) if TLS 1.3 is configured. - %% Filter out all hash-sign tuples when creating the signature_algs extension. - %% (TLS 1.3 specific record type) - SignatureSchemes = lists:filter(fun is_atom/1, SignatureSchemes0), - #signature_algorithms{signature_scheme_list = SignatureSchemes}. + #signature_algorithms{signature_scheme_list = SignatureSchemes0}. signature_algs_cert(undefined) -> undefined; @@ -1474,7 +1479,16 @@ extension_value(#next_protocol_negotiation{extension_data = Data}) -> extension_value(#srp{username = Name}) -> Name; extension_value(#renegotiation_info{renegotiated_connection = Data}) -> - Data. + Data; +extension_value(#signature_algorithms{signature_scheme_list = Schemes}) -> + Schemes; +extension_value(#signature_algorithms_cert{signature_scheme_list = Schemes}) -> + Schemes; +extension_value(#key_share_client_hello{client_shares = ClientShares}) -> + ClientShares; +extension_value(#client_hello_versions{versions = Versions}) -> + Versions. + %%-------------------------------------------------------------------- %%% Internal functions @@ -3046,6 +3060,11 @@ empty_extensions({3,4}, server_hello) -> key_share => undefined, pre_shared_key => undefined }; +empty_extensions({3,4}, hello_retry_request) -> + #{server_hello_selected_version => undefined, + key_share => undefined, + pre_shared_key => undefined + }; empty_extensions(_, server_hello) -> #{renegotiation_info => undefined, alpn => undefined, diff --git a/lib/ssl/src/ssl_logger.erl b/lib/ssl/src/ssl_logger.erl index 987693b96b..514a4464bc 100644 --- a/lib/ssl/src/ssl_logger.erl +++ b/lib/ssl/src/ssl_logger.erl @@ -200,6 +200,11 @@ parse_handshake(Direction, #encrypted_extensions{} = EncryptedExtensions) -> Header = io_lib:format("~s Handshake, EncryptedExtensions", [header_prefix(Direction)]), Message = io_lib:format("~p", [?rec_info(encrypted_extensions, EncryptedExtensions)]), + {Header, Message}; +parse_handshake(Direction, #new_session_ticket{} = NewSessionTicket) -> + Header = io_lib:format("~s Post-Handshake, NewSessionTicket", + [header_prefix(Direction)]), + Message = io_lib:format("~p", [?rec_info(new_session_ticket, NewSessionTicket)]), {Header, Message}. diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl index a05858221a..3998f03519 100644 --- a/lib/ssl/src/tls_connection.erl +++ b/lib/ssl/src/tls_connection.erl @@ -31,6 +31,7 @@ -include("tls_connection.hrl"). -include("tls_handshake.hrl"). +-include("tls_handshake_1_3.hrl"). -include("ssl_alert.hrl"). -include("tls_record.hrl"). -include("ssl_cipher.hrl"). @@ -62,7 +63,7 @@ close/5, protocol_name/0]). %% Data handling --export([next_record/1, socket/4, setopts/3, getopts/3]). +-export([socket/4, setopts/3, getopts/3]). %% gen_statem state functions -export([init/3, error/3, downgrade/3, %% Initiation and take down states @@ -161,32 +162,60 @@ pids(#state{protocol_specific = #{sender := Sender}}) -> %%==================================================================== %% State transition handling %%==================================================================== -next_record(#state{handshake_env = +next_record(_, #state{handshake_env = #handshake_env{unprocessed_handshake_events = N} = HsEnv} = State) when N > 0 -> {no_record, State#state{handshake_env = HsEnv#handshake_env{unprocessed_handshake_events = N-1}}}; -next_record(#state{protocol_buffers = - #protocol_buffers{tls_cipher_texts = [_|_] = CipherTexts}, - connection_states = ConnectionStates, - ssl_options = #ssl_options{padding_check = Check}} = State) -> +next_record(_, #state{protocol_buffers = + #protocol_buffers{tls_cipher_texts = [_|_] = CipherTexts}, + connection_states = ConnectionStates, + ssl_options = #ssl_options{padding_check = Check}} = State) -> next_record(State, CipherTexts, ConnectionStates, Check); -next_record(#state{protocol_buffers = #protocol_buffers{tls_cipher_texts = []}, - protocol_specific = #{active_n_toggle := true, active_n := N} = ProtocolSpec, - static_env = #static_env{socket = Socket, - close_tag = CloseTag, - transport_cb = Transport} - } = State) -> - case tls_socket:setopts(Transport, Socket, [{active, N}]) of - ok -> - {no_record, State#state{protocol_specific = ProtocolSpec#{active_n_toggle => false}}}; - _ -> - self() ! {CloseTag, Socket}, - {no_record, State} - end; -next_record(State) -> +next_record(connection, #state{protocol_buffers = #protocol_buffers{tls_cipher_texts = []}, + protocol_specific = #{active_n_toggle := true} + } = State) -> + %% If ssl application user is not reading data wait to activate socket + flow_ctrl(State); + +next_record(_, #state{protocol_buffers = #protocol_buffers{tls_cipher_texts = []}, + protocol_specific = #{active_n_toggle := true} + } = State) -> + activate_socket(State); +next_record(_, State) -> {no_record, State}. + +flow_ctrl(#state{user_data_buffer = {_,Size,_}, + socket_options = #socket_options{active = false}, + bytes_to_read = undefined} = State) when Size =/= 0 -> + {no_record, State}; +flow_ctrl(#state{user_data_buffer = {_,Size,_}, + socket_options = #socket_options{active = false}, + bytes_to_read = 0} = State) when Size =/= 0 -> + {no_record, State}; +flow_ctrl(#state{user_data_buffer = {_,Size,_}, + socket_options = #socket_options{active = false}, + bytes_to_read = BytesToRead} = State) when (Size >= BytesToRead) andalso + (BytesToRead > 0) -> + {no_record, State}; +flow_ctrl(State) -> + activate_socket(State). + + +activate_socket(#state{protocol_specific = #{active_n_toggle := true, active_n := N} = ProtocolSpec, + static_env = #static_env{socket = Socket, + close_tag = CloseTag, + transport_cb = Transport} + } = State) -> + case tls_socket:setopts(Transport, Socket, [{active, N}]) of + ok -> + {no_record, State#state{protocol_specific = ProtocolSpec#{active_n_toggle => false}}}; + _ -> + self() ! {CloseTag, Socket}, + {no_record, State} + end. + %% Decipher next record and concatenate consecutive ?APPLICATION_DATA records into one %% next_record(State, CipherTexts, ConnectionStates, Check) -> @@ -224,31 +253,20 @@ next_record_done(#state{protocol_buffers = Buffers} = State, CipherTexts, Connec State#state{protocol_buffers = Buffers#protocol_buffers{tls_cipher_texts = CipherTexts}, connection_states = ConnectionStates}}. - next_event(StateName, Record, State) -> next_event(StateName, Record, State, []). %% next_event(StateName, no_record, State0, Actions) -> - case next_record(State0) of + case next_record(StateName, State0) of {no_record, State} -> - {next_state, StateName, State, Actions}; - {#ssl_tls{} = Record, State} -> - {next_state, StateName, State, [{next_event, internal, {protocol_record, Record}} | Actions]}; - #alert{} = Alert -> - Version = State0#state.connection_env#connection_env.negotiated_version, - ssl_connection:handle_own_alert(Alert, Version, StateName, State0) + ssl_connection:hibernate_after(StateName, State, Actions); + {Record, State} -> + next_event(StateName, Record, State, Actions) end; -next_event(StateName, Record, State, Actions) -> - case Record of - no_record -> - {next_state, StateName, State, Actions}; - #ssl_tls{} = Record -> - {next_state, StateName, State, [{next_event, internal, {protocol_record, Record}} | Actions]}; - #alert{} = Alert -> - Version = State#state.connection_env#connection_env.negotiated_version, - ssl_connection:handle_own_alert(Alert, Version, StateName, State) - end. - +next_event(StateName, #ssl_tls{} = Record, State, Actions) -> + {next_state, StateName, State, [{next_event, internal, {protocol_record, Record}} | Actions]}; +next_event(StateName, #alert{} = Alert, State, Actions) -> + {next_state, StateName, State, [{next_event, internal, Alert} | Actions]}. %%% TLS record protocol level application data messages handle_protocol_record(#ssl_tls{type = ?APPLICATION_DATA, fragment = Data}, StateName, @@ -272,12 +290,8 @@ handle_protocol_record(#ssl_tls{type = ?APPLICATION_DATA, fragment = Data}, Stat {stop, _, _} = Stop-> Stop; {Record, State1} -> - case next_event(StateName, Record, State1) of - {next_state, StateName, State, Actions} -> - ssl_connection:hibernate_after(StateName, State, Actions); - {stop, _, _} = Stop -> - Stop - end + {next_state, StateName, State, Actions} = next_event(StateName, Record, State1), + ssl_connection:hibernate_after(StateName, State, Actions) end; %%% TLS record protocol level handshake messages handle_protocol_record(#ssl_tls{type = ?HANDSHAKE, fragment = Data}, @@ -303,8 +317,7 @@ handle_protocol_record(#ssl_tls{type = ?HANDSHAKE, fragment = Data}, _ -> HsEnv = State#state.handshake_env, {next_state, StateName, - State#state{protocol_buffers = Buffers, - handshake_env = + State#state{handshake_env = HsEnv#handshake_env{unprocessed_handshake_events = unprocessed_events(Events)}}, Events} end @@ -323,7 +336,9 @@ handle_protocol_record(#ssl_tls{type = ?ALERT, fragment = EncAlerts}, StateName, handle_alerts(Alerts, {next_state, StateName, State}); [] -> ssl_connection:handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, empty_alert), - Version, StateName, State) + Version, StateName, State); + #alert{} = Alert -> + ssl_connection:handle_own_alert(Alert, Version, StateName, State) catch _:_ -> ssl_connection:handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, alert_decode_error), @@ -381,6 +396,7 @@ queue_handshake(Handshake, #state{handshake_env = #handshake_env{tls_handshake_h handshake_env = HsEnv#handshake_env{tls_handshake_history = Hist}, flight_buffer = Flight0 ++ [BinHandshake]}. + send_handshake_flight(#state{static_env = #static_env{socket = Socket, transport_cb = Transport}, flight_buffer = Flight} = State0) -> @@ -646,10 +662,16 @@ hello(internal, #server_hello{} = Hello, case tls_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of #alert{} = Alert -> %%TODO ssl_connection:handle_own_alert(Alert, ReqVersion, hello, - State#state{connection_env = CEnv#connection_env{negotiated_version = ReqVersion}}); + State#state{connection_env = + CEnv#connection_env{negotiated_version = ReqVersion}}); + %% Legacy TLS 1.2 and older {Version, NewId, ConnectionStates, ProtoExt, Protocol} -> ssl_connection:handle_session(Hello, - Version, NewId, ConnectionStates, ProtoExt, Protocol, State) + Version, NewId, ConnectionStates, ProtoExt, Protocol, State); + %% TLS 1.3 + {next_state, wait_sh} -> + %% Continue in TLS 1.3 'wait_sh' state + {next_state, wait_sh, State, [{next_event, internal, Hello}]} end; hello(info, Event, State) -> gen_info(Event, ?FUNCTION_NAME, State); @@ -790,6 +812,11 @@ connection(internal, #client_hello{}, State = reinit_handshake_data(State0), next_event(?FUNCTION_NAME, no_record, State); +connection(internal, #new_session_ticket{}, State) -> + %% TLS 1.3 + %% Drop NewSessionTicket (currently not supported) + next_event(?FUNCTION_NAME, no_record, State); + connection(Type, Event, State) -> ssl_connection:?FUNCTION_NAME(Type, Event, State, ?MODULE). @@ -1049,7 +1076,7 @@ next_tls_record(Data, StateName, case tls_record:get_tls_records(Data, Versions, Buf0, SslOpts) of {Records, Buf1} -> CT1 = CT0 ++ Records, - next_record(State0#state{protocol_buffers = + next_record(StateName, State0#state{protocol_buffers = Buffers#protocol_buffers{tls_record_buffer = Buf1, tls_cipher_texts = CT1}}); #alert{} = Alert -> @@ -1150,7 +1177,6 @@ encode_handshake(Handshake, Version, ConnectionStates0, Hist0) -> encode_change_cipher(#change_cipher_spec{}, Version, ConnectionStates) -> tls_record:encode_change_cipher_spec(Version, ConnectionStates). --spec decode_alerts(binary()) -> list(). decode_alerts(Bin) -> ssl_alert:decode(Bin). @@ -1273,9 +1299,10 @@ maybe_generate_client_shares(#ssl_options{ versions = [Version|_], supported_groups = #supported_groups{ - supported_groups = Groups}}) + supported_groups = [Group|_]}}) when Version =:= {3,4} -> - ssl_cipher:generate_client_shares(Groups); + %% Generate only key_share entry for the most preferred group + ssl_cipher:generate_client_shares([Group]); maybe_generate_client_shares(_) -> undefined. diff --git a/lib/ssl/src/tls_connection_1_3.erl b/lib/ssl/src/tls_connection_1_3.erl index 701a5860c2..117e4f059d 100644 --- a/lib/ssl/src/tls_connection_1_3.erl +++ b/lib/ssl/src/tls_connection_1_3.erl @@ -112,13 +112,15 @@ negotiated/4, wait_cert/4, wait_cv/4, - wait_finished/4 + wait_finished/4, + wait_sh/4, + wait_ee/4, + wait_cert_cr/4 ]). -start(internal, #change_cipher_spec{}, State0, _Module) -> - {Record, State} = tls_connection:next_record(State0), - tls_connection:next_event(?FUNCTION_NAME, Record, State); +start(internal, #change_cipher_spec{}, State, _Module) -> + tls_connection:next_event(?FUNCTION_NAME, no_record, State); start(internal, #client_hello{} = Hello, State0, _Module) -> case tls_handshake_1_3:do_start(Hello, State0) of #alert{} = Alert -> @@ -128,13 +130,19 @@ start(internal, #client_hello{} = Hello, State0, _Module) -> {State, negotiated} -> {next_state, negotiated, State, [{next_event, internal, start_handshake}]} end; +start(internal, #server_hello{} = ServerHello, State0, _Module) -> + case tls_handshake_1_3:do_start(ServerHello, State0) of + #alert{} = Alert -> + ssl_connection:handle_own_alert(Alert, {3,4}, start, State0); + {State, NextState} -> + {next_state, NextState, State, []} + end; start(Type, Msg, State, Connection) -> ssl_connection:handle_common_event(Type, Msg, ?FUNCTION_NAME, State, Connection). -negotiated(internal, #change_cipher_spec{}, State0, _Module) -> - {Record, State} = tls_connection:next_record(State0), - tls_connection:next_event(?FUNCTION_NAME, Record, State); +negotiated(internal, #change_cipher_spec{}, State, _Module) -> + tls_connection:next_event(?FUNCTION_NAME, no_record, State); negotiated(internal, Message, State0, _Module) -> case tls_handshake_1_3:do_negotiated(Message, State0) of #alert{} = Alert -> @@ -144,41 +152,36 @@ negotiated(internal, Message, State0, _Module) -> end. -wait_cert(internal, #change_cipher_spec{}, State0, _Module) -> - {Record, State} = tls_connection:next_record(State0), - tls_connection:next_event(?FUNCTION_NAME, Record, State); +wait_cert(internal, #change_cipher_spec{}, State, _Module) -> + tls_connection:next_event(?FUNCTION_NAME, no_record, State); wait_cert(internal, #certificate_1_3{} = Certificate, State0, _Module) -> case tls_handshake_1_3:do_wait_cert(Certificate, State0) of {#alert{} = Alert, State} -> ssl_connection:handle_own_alert(Alert, {3,4}, wait_cert, State); - {State1, NextState} -> - {Record, State} = tls_connection:next_record(State1), - tls_connection:next_event(NextState, Record, State) + {State, NextState} -> + tls_connection:next_event(NextState, no_record, State) end; wait_cert(Type, Msg, State, Connection) -> ssl_connection:handle_common_event(Type, Msg, ?FUNCTION_NAME, State, Connection). -wait_cv(internal, #change_cipher_spec{}, State0, _Module) -> - {Record, State} = tls_connection:next_record(State0), - tls_connection:next_event(?FUNCTION_NAME, Record, State); +wait_cv(internal, #change_cipher_spec{}, State, _Module) -> + tls_connection:next_event(?FUNCTION_NAME, no_record, State); wait_cv(internal, #certificate_verify_1_3{} = CertificateVerify, State0, _Module) -> case tls_handshake_1_3:do_wait_cv(CertificateVerify, State0) of {#alert{} = Alert, State} -> ssl_connection:handle_own_alert(Alert, {3,4}, wait_cv, State); - {State1, NextState} -> - {Record, State} = tls_connection:next_record(State1), - tls_connection:next_event(NextState, Record, State) + {State, NextState} -> + tls_connection:next_event(NextState, no_record, State) end; wait_cv(Type, Msg, State, Connection) -> ssl_connection:handle_common_event(Type, Msg, ?FUNCTION_NAME, State, Connection). -wait_finished(internal, #change_cipher_spec{}, State0, _Module) -> - {Record, State} = tls_connection:next_record(State0), - tls_connection:next_event(?FUNCTION_NAME, Record, State); +wait_finished(internal, #change_cipher_spec{}, State, _Module) -> + tls_connection:next_event(?FUNCTION_NAME, no_record, State); wait_finished(internal, #finished{} = Finished, State0, Module) -> case tls_handshake_1_3:do_wait_finished(Finished, State0) of @@ -190,3 +193,52 @@ wait_finished(internal, end; wait_finished(Type, Msg, State, Connection) -> ssl_connection:handle_common_event(Type, Msg, ?FUNCTION_NAME, State, Connection). + + +wait_sh(internal, #change_cipher_spec{}, State, _Module) -> + tls_connection:next_event(?FUNCTION_NAME, no_record, State); +wait_sh(internal, #server_hello{} = Hello, State0, _Module) -> + case tls_handshake_1_3:do_wait_sh(Hello, State0) of + #alert{} = Alert -> + ssl_connection:handle_own_alert(Alert, {3,4}, wait_sh, State0); + {State1, start, ServerHello} -> + %% hello_retry_request: go to start + {next_state, start, State1, [{next_event, internal, ServerHello}]}; + {State1, wait_ee} -> + tls_connection:next_event(wait_ee, no_record, State1) + end; +wait_sh(Type, Msg, State, Connection) -> + ssl_connection:handle_common_event(Type, Msg, ?FUNCTION_NAME, State, Connection). + + +wait_ee(internal, #change_cipher_spec{}, State, _Module) -> + tls_connection:next_event(?FUNCTION_NAME, no_record, State); +wait_ee(internal, #encrypted_extensions{} = EE, State0, _Module) -> + case tls_handshake_1_3:do_wait_ee(EE, State0) of + #alert{} = Alert -> + ssl_connection:handle_own_alert(Alert, {3,4}, wait_ee, State0); + {State1, NextState} -> + tls_connection:next_event(NextState, no_record, State1) + end; +wait_ee(Type, Msg, State, Connection) -> + ssl_connection:handle_common_event(Type, Msg, ?FUNCTION_NAME, State, Connection). + + +wait_cert_cr(internal, #change_cipher_spec{}, State, _Module) -> + tls_connection:next_event(?FUNCTION_NAME, no_record, State); +wait_cert_cr(internal, #certificate_1_3{} = Certificate, State0, _Module) -> + case tls_handshake_1_3:do_wait_cert_cr(Certificate, State0) of + {#alert{} = Alert, State} -> + ssl_connection:handle_own_alert(Alert, {3,4}, wait_cert_cr, State); + {State1, NextState} -> + tls_connection:next_event(NextState, no_record, State1) + end; +wait_cert_cr(internal, #certificate_request_1_3{} = CertificateRequest, State0, _Module) -> + case tls_handshake_1_3:do_wait_cert_cr(CertificateRequest, State0) of + #alert{} = Alert -> + ssl_connection:handle_own_alert(Alert, {3,4}, wait_cert_cr, State0); + {State1, NextState} -> + tls_connection:next_event(NextState, no_record, State1) + end; +wait_cert_cr(Type, Msg, State, Connection) -> + ssl_connection:handle_common_event(Type, Msg, ?FUNCTION_NAME, State, Connection). diff --git a/lib/ssl/src/tls_handshake.erl b/lib/ssl/src/tls_handshake.erl index 2480e05097..c132f75eae 100644 --- a/lib/ssl/src/tls_handshake.erl +++ b/lib/ssl/src/tls_handshake.erl @@ -105,7 +105,7 @@ client_hello(Host, Port, ConnectionStates, {tls_record:tls_version(), {resumed | new, #session{}}, ssl_record:connection_states(), binary() | undefined, HelloExt::map(), {ssl:hash(), ssl:sign_algo()} | - undefined} | #alert{}. + undefined} | {atom(), atom()} |#alert{}. %% %% Description: Handles a received hello message %%-------------------------------------------------------------------- @@ -148,29 +148,48 @@ hello(#server_hello{server_version = {Major, Minor}, %% %% - If "supported_version" is present (ServerHello): %% - Abort handshake with an "illegal_parameter" alert -hello(#server_hello{server_version = Version, +hello(#server_hello{server_version = LegacyVersion, + random = Random, + cipher_suite = CipherSuite, + compression_method = Compression, + session_id = SessionId, extensions = #{server_hello_selected_version := - #server_hello_selected_version{selected_version = Version}} + #server_hello_selected_version{selected_version = Version} = HelloExt} }, - #ssl_options{versions = SupportedVersions}, - _ConnectionStates0, _Renegotiation) -> - case tls_record:is_higher({3,4}, Version) of + #ssl_options{versions = SupportedVersions} = SslOpt, + ConnectionStates0, Renegotiation) -> + %% In TLS 1.3, the TLS server indicates its version using the "supported_versions" extension + %% (Section 4.2.1), and the legacy_version field MUST be set to 0x0303, which is the version + %% number for TLS 1.2. + %% The "supported_versions" extension is supported from TLS 1.2. + case LegacyVersion > {3,3} orelse + LegacyVersion =:= {3,3} andalso Version < {3,3} of true -> ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER); false -> case tls_record:is_acceptable_version(Version, SupportedVersions) of true -> - %% Implement TLS 1.3 statem ??? - ?ALERT_REC(?FATAL, ?PROTOCOL_VERSION); + case Version of + {3,3} -> + %% TLS 1.2 ServerHello with "supported_versions" (special case) + handle_server_hello_extensions(Version, SessionId, Random, CipherSuite, + Compression, HelloExt, SslOpt, + ConnectionStates0, Renegotiation); + {3,4} -> + %% TLS 1.3 + {next_state, wait_sh} + end; false -> ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER) end end; -hello(#server_hello{server_version = Version, random = Random, +hello(#server_hello{server_version = Version, + random = Random, cipher_suite = CipherSuite, compression_method = Compression, - session_id = SessionId, extensions = HelloExt}, + session_id = SessionId, + extensions = HelloExt}, #ssl_options{versions = SupportedVersions} = SslOpt, ConnectionStates0, Renegotiation) -> case tls_record:is_acceptable_version(Version, SupportedVersions) of diff --git a/lib/ssl/src/tls_handshake_1_3.erl b/lib/ssl/src/tls_handshake_1_3.erl index 8a4ad922e1..49d20b3ec0 100644 --- a/lib/ssl/src/tls_handshake_1_3.erl +++ b/lib/ssl/src/tls_handshake_1_3.erl @@ -39,23 +39,32 @@ %% Create handshake messages -export([certificate/5, certificate_verify/4, - encrypted_extensions/0, - server_hello/4]). + encrypted_extensions/0]). -export([do_start/2, do_negotiated/2, do_wait_cert/2, do_wait_cv/2, - do_wait_finished/2]). + do_wait_finished/2, + do_wait_sh/2, + do_wait_ee/2, + do_wait_cert_cr/2]). + + +%% crypto:hash(sha256, "HelloRetryRequest"). +-define(HELLO_RETRY_REQUEST_RANDOM, <<207,33,173,116,229,154,97,17, + 190,29,140,2,30,101,184,145, + 194,162,17,22,122,187,140,94, + 7,158,9,226,200,168,51,156>>). %%==================================================================== %% Create handshake messages %%==================================================================== -server_hello(MsgType, SessionId, KeyShare, ConnectionStates) -> +server_hello(MsgType, SessionId, KeyShare, ConnectionStates, ALPN) -> #{security_parameters := SecParams} = ssl_record:pending_connection_state(ConnectionStates, read), - Extensions = server_hello_extensions(MsgType, KeyShare), + Extensions = server_hello_extensions(MsgType, KeyShare, ALPN), #server_hello{server_version = {3,3}, %% legacy_version cipher_suite = SecParams#security_parameters.cipher_suite, compression_method = 0, %% legacy attribute @@ -64,10 +73,26 @@ server_hello(MsgType, SessionId, KeyShare, ConnectionStates) -> extensions = Extensions }. -server_hello_extensions(MsgType, KeyShare) -> +%% The server's extensions MUST contain "supported_versions". +%% Additionally, it SHOULD contain the minimal set of extensions +%% necessary for the client to generate a correct ClientHello pair. As +%% with the ServerHello, a HelloRetryRequest MUST NOT contain any +%% extensions that were not first offered by the client in its +%% ClientHello, with the exception of optionally the "cookie" (see +%% Section 4.2.2) extension. +server_hello_extensions(hello_retry_request = MsgType, KeyShare, _) -> + SupportedVersions = #server_hello_selected_version{selected_version = {3,4}}, + Extensions = #{server_hello_selected_version => SupportedVersions}, + ssl_handshake:add_server_share(MsgType, Extensions, KeyShare); +server_hello_extensions(MsgType, KeyShare, undefined) -> SupportedVersions = #server_hello_selected_version{selected_version = {3,4}}, Extensions = #{server_hello_selected_version => SupportedVersions}, - ssl_handshake:add_server_share(MsgType, Extensions, KeyShare). + ssl_handshake:add_server_share(MsgType, Extensions, KeyShare); +server_hello_extensions(MsgType, KeyShare, ALPN0) -> + Extensions0 = ssl_handshake:add_selected_version(#{}), %% {3,4} (TLS 1.3) + Extensions1 = ssl_handshake:add_alpn(Extensions0, ALPN0), + ssl_handshake:add_server_share(MsgType, Extensions1, KeyShare). + server_hello_random(server_hello, #security_parameters{server_random = Random}) -> Random; @@ -79,7 +104,7 @@ server_hello_random(server_hello, #security_parameters{server_random = Random}) %% CF 21 AD 74 E5 9A 61 11 BE 1D 8C 02 1E 65 B8 91 %% C2 A2 11 16 7A BB 8C 5E 07 9E 09 E2 C8 A8 33 9C server_hello_random(hello_retry_request, _) -> - crypto:hash(sha256, "HelloRetryRequest"). + ?HELLO_RETRY_REQUEST_RANDOM. %% TODO: implement support for encrypted_extensions @@ -111,7 +136,7 @@ add_signature_algorithms_cert(Extensions, undefined) -> Extensions; add_signature_algorithms_cert(Extensions, SignAlgsCert) -> Extensions#{signature_algorithms_cert => - #signature_algorithms{signature_scheme_list = SignAlgsCert}}. + #signature_algorithms_cert{signature_scheme_list = SignAlgsCert}}. filter_tls13_algs(undefined) -> undefined; @@ -119,7 +144,6 @@ filter_tls13_algs(Algo) -> lists:filter(fun is_atom/1, Algo). -%% TODO: use maybe monad for error handling! %% enum { %% X509(0), %% RawPublicKey(2), @@ -142,18 +166,28 @@ filter_tls13_algs(Algo) -> %% opaque certificate_request_context<0..2^8-1>; %% CertificateEntry certificate_list<0..2^24-1>; %% } Certificate; -certificate(OwnCert, CertDbHandle, CertDbRef, _CRContext, server) -> +certificate(OwnCert, CertDbHandle, CertDbRef, _CRContext, Role) -> case ssl_certificate:certificate_chain(OwnCert, CertDbHandle, CertDbRef) of {ok, _, Chain} -> CertList = chain_to_cert_list(Chain), %% If this message is in response to a CertificateRequest, the value of %% certificate_request_context in that message. Otherwise (in the case %%of server authentication), this field SHALL be zero length. - #certificate_1_3{ - certificate_request_context = <<>>, - certificate_list = CertList}; - {error, Error} -> - ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, {server_has_no_suitable_certificates, Error}) + {ok, #certificate_1_3{ + certificate_request_context = <<>>, + certificate_list = CertList}}; + {error, Error} when Role =:= server -> + {error, {no_suitable_certificates, Error}}; + {error, _Error} when Role =:= client -> + %% The client MUST send a Certificate message if and only if the server + %% has requested client authentication via a CertificateRequest message + %% (Section 4.3.2). If the server requests client authentication but no + %% suitable certificate is available, the client MUST send a Certificate + %% message containing no certificates (i.e., with the "certificate_list" + %% field having length 0). + {ok, #certificate_1_3{ + certificate_request_context = <<>>, + certificate_list = []}} end. @@ -161,7 +195,7 @@ certificate_verify(PrivateKey, SignatureScheme, #state{connection_states = ConnectionStates, handshake_env = #handshake_env{ - tls_handshake_history = {Messages, _}}}, server) -> + tls_handshake_history = {Messages, _}}}, Role) -> #{security_parameters := SecParamsR} = ssl_record:pending_connection_state(ConnectionStates, write), #security_parameters{prf_algorithm = HKDFAlgo} = SecParamsR, @@ -173,11 +207,11 @@ certificate_verify(PrivateKey, SignatureScheme, %% Transcript-Hash uses the HKDF hash function defined by the cipher suite. THash = tls_v1:transcript_hash(Context, HKDFAlgo), + ContextString = context_string(Role), %% Digital signatures use the hash function defined by the selected signature %% scheme. - case sign(THash, <<"TLS 1.3, server CertificateVerify">>, - HashAlgo, PrivateKey) of + case sign(THash, ContextString, HashAlgo, PrivateKey) of {ok, Signature} -> {ok, #certificate_verify_1_3{ algorithm = SignatureScheme, @@ -252,6 +286,21 @@ encode_handshake(HandshakeMsg) -> %% Decode handshake %%==================================================================== + +decode_handshake(?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, + ?BYTE(SID_length), Session_ID:SID_length/binary, + Cipher_suite:2/binary, ?BYTE(Comp_method), + ?UINT16(ExtLen), Extensions:ExtLen/binary>>) + when Random =:= ?HELLO_RETRY_REQUEST_RANDOM -> + HelloExtensions = ssl_handshake:decode_hello_extensions(Extensions, {3,4}, {Major, Minor}, + hello_retry_request), + #server_hello{ + server_version = {Major,Minor}, + random = Random, + session_id = Session_ID, + cipher_suite = Cipher_suite, + compression_method = Comp_method, + extensions = HelloExtensions}; decode_handshake(?CERTIFICATE_REQUEST, <<?BYTE(0), ?UINT16(Size), EncExts:Size/binary>>) -> Exts = decode_extensions(EncExts, certificate_request), #certificate_request_1_3{ @@ -384,6 +433,15 @@ certificate_entry(DER) -> %% 79 %% 00 %% 0101010101010101010101010101010101010101010101010101010101010101 +sign(THash, Context, HashAlgo, #'ECPrivateKey'{} = PrivateKey) -> + Content = build_content(Context, THash), + try public_key:sign(Content, HashAlgo, PrivateKey) of + Signature -> + {ok, Signature} + catch + error:badarg -> + {error, badarg} + end; sign(THash, Context, HashAlgo, PrivateKey) -> Content = build_content(Context, THash), @@ -401,7 +459,16 @@ sign(THash, Context, HashAlgo, PrivateKey) -> end. -verify(THash, Context, HashAlgo, Signature, PublicKey) -> +verify(THash, Context, HashAlgo, Signature, {?'id-ecPublicKey', PublicKey, PublicKeyParams}) -> + Content = build_content(Context, THash), + try public_key:verify(Content, HashAlgo, Signature, {PublicKey, PublicKeyParams}) of + Result -> + {ok, Result} + catch + error:badarg -> + {error, badarg} + end; +verify(THash, Context, HashAlgo, Signature, {?rsaEncryption, PublicKey, _PubKeyParams}) -> Content = build_content(Context, THash), %% The length of the Salt MUST be equal to the length of the output @@ -428,15 +495,16 @@ build_content(Context, THash) -> %%==================================================================== +%% TLS Server do_start(#client_hello{cipher_suites = ClientCiphers, session_id = SessionId, extensions = Extensions} = _Hello, #state{connection_states = _ConnectionStates0, ssl_options = #ssl_options{ciphers = ServerCiphers, signature_algs = ServerSignAlgs, - supported_groups = ServerGroups0}, + supported_groups = ServerGroups0, + alpn_preferred_protocols = ALPNPreferredProtocols}, session = #session{own_certificate = Cert}} = State0) -> - ClientGroups0 = maps:get(elliptic_curves, Extensions, undefined), ClientGroups = get_supported_groups(ClientGroups0), ServerGroups = get_supported_groups(ServerGroups0), @@ -444,23 +512,27 @@ do_start(#client_hello{cipher_suites = ClientCiphers, ClientShares0 = maps:get(key_share, Extensions, undefined), ClientShares = get_key_shares(ClientShares0), + ClientALPN0 = maps:get(alpn, Extensions, undefined), + ClientALPN = ssl_handshake:decode_alpn(ClientALPN0), + ClientSignAlgs = get_signature_scheme_list( maps:get(signature_algs, Extensions, undefined)), ClientSignAlgsCert = get_signature_scheme_list( maps:get(signature_algs_cert, Extensions, undefined)), - %% TODO: use library function if it exists - %% Init the maybe "monad" {Ref,Maybe} = maybe(), try + %% Handle ALPN extension if ALPN is configured + ALPNProtocol = Maybe(handle_alpn(ALPNPreferredProtocols, ClientALPN)), + %% If the server does not select a PSK, then the server independently selects a %% cipher suite, an (EC)DHE group and key share for key establishment, %% and a signature algorithm/certificate pair to authenticate itself to %% the client. Cipher = Maybe(select_cipher_suite(ClientCiphers, ServerCiphers)), Groups = Maybe(select_common_groups(ServerGroups, ClientGroups)), - Maybe(validate_key_share(ClientGroups, ClientShares)), + Maybe(validate_client_key_share(ClientGroups, ClientShares)), {PublicKeyAlgo, SignAlgo, SignHash} = get_certificate_params(Cert), @@ -479,8 +551,14 @@ do_start(#client_hello{cipher_suites = ClientCiphers, %% Generate server_share KeyShare = ssl_cipher:generate_server_share(Group), - State1 = update_start_state(State0, Cipher, KeyShare, SessionId, - Group, SelectedSignAlg, ClientPubKey), + State1 = update_start_state(State0, + #{cipher => Cipher, + key_share => KeyShare, + session_id => SessionId, + group => Group, + sign_alg => SelectedSignAlg, + peer_public_key => ClientPubKey, + alpn => ALPNProtocol}), %% 4.1.4. Hello Retry Request %% @@ -490,10 +568,7 @@ do_start(#client_hello{cipher_suites = ClientCiphers, %% the handshake. Maybe(send_hello_retry_request(State1, ClientPubKey, KeyShare, SessionId)) - %% TODO: - %% - session handling - %% - handle extensions: ALPN - %% (do not handle: NPN, srp, renegotiation_info, ec_point_formats) + %% TODO: session handling catch {Ref, {insufficient_security, no_suitable_groups}} -> @@ -505,7 +580,87 @@ do_start(#client_hello{cipher_suites = ClientCiphers, {Ref, {insufficient_security, no_suitable_signature_algorithm}} -> ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, "No suitable signature algorithm"); {Ref, {insufficient_security, no_suitable_public_key}} -> - ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_public_key) + ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_public_key); + {Ref, no_application_protocol} -> + ?ALERT_REC(?FATAL, ?NO_APPLICATION_PROTOCOL) + end; +%% TLS Client +do_start(#server_hello{cipher_suite = SelectedCipherSuite, + session_id = SessionId, + extensions = Extensions} = _ServerHello, + #state{static_env = #static_env{role = client, + host = Host, + port = Port, + transport_cb = Transport, + socket = Socket, + session_cache = Cache, + session_cache_cb = CacheCb}, + handshake_env = #handshake_env{renegotiation = {Renegotiation, _}, + tls_handshake_history = _HHistory} = HsEnv, + connection_env = CEnv, + ssl_options = #ssl_options{ciphers = ClientCiphers, + supported_groups = ClientGroups0} = SslOpts, + session = #session{own_certificate = Cert} = Session0, + connection_states = ConnectionStates0 + } = State0) -> + ClientGroups = get_supported_groups(ClientGroups0), + + {Ref,Maybe} = maybe(), + try + ServerKeyShare = maps:get(key_share, Extensions, undefined), + SelectedGroup = get_selected_group(ServerKeyShare), + + %% Upon receipt of this extension in a HelloRetryRequest, the client + %% MUST verify that (1) the selected_group field corresponds to a group + %% which was provided in the "supported_groups" extension in the + %% original ClientHello and (2) the selected_group field does not + %% correspond to a group which was provided in the "key_share" extension + %% in the original ClientHello. If either of these checks fails, then + %% the client MUST abort the handshake with an "illegal_parameter" + %% alert. + Maybe(validate_selected_group(SelectedGroup, ClientGroups)), + + Maybe(validate_cipher_suite(SelectedCipherSuite, ClientCiphers)), + + %% Otherwise, when sending the new ClientHello, the client MUST + %% replace the original "key_share" extension with one containing only a + %% new KeyShareEntry for the group indicated in the selected_group field + %% of the triggering HelloRetryRequest. + ClientKeyShare = ssl_cipher:generate_client_shares([SelectedGroup]), + Hello = tls_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts, + Cache, CacheCb, Renegotiation, Cert, ClientKeyShare), + + HelloVersion = tls_record:hello_version(SslOpts#ssl_options.versions), + + %% Update state + State1 = update_start_state(State0, + #{cipher => SelectedCipherSuite, + key_share => ClientKeyShare, + session_id => SessionId, + group => SelectedGroup}), + + %% Replace ClientHello1 with a special synthetic handshake message + State2 = replace_ch1_with_message_hash(State1), + #state{handshake_env = #handshake_env{tls_handshake_history = HHistory}} = State2, + + {BinMsg, ConnectionStates, Handshake} = + tls_connection:encode_handshake(Hello, HelloVersion, ConnectionStates0, HHistory), + tls_socket:send(Transport, Socket, BinMsg), + ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'handshake', Hello), + ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'record', BinMsg), + + State = State2#state{ + connection_states = ConnectionStates, + connection_env = CEnv#connection_env{negotiated_version = HelloVersion}, %% Requested version + session = Session0#session{session_id = Hello#client_hello.session_id}, + handshake_env = HsEnv#handshake_env{tls_handshake_history = Handshake}, + key_share = ClientKeyShare}, + + {State, wait_sh} + + catch + {Ref, {illegal_parameter, Reason}} -> + ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER, Reason) end. @@ -515,10 +670,11 @@ do_negotiated(start_handshake, own_certificate = OwnCert, ecc = SelectedGroup, sign_alg = SignatureScheme, - dh_public_value = ClientKey}, + dh_public_value = ClientPublicKey}, ssl_options = #ssl_options{} = SslOpts, key_share = KeyShare, - handshake_env = #handshake_env{tls_handshake_history = _HHistory0}, + handshake_env = #handshake_env{tls_handshake_history = _HHistory0, + alpn = ALPN}, connection_env = #connection_env{private_key = CertPrivateKey}, static_env = #static_env{ cert_db = CertDbHandle, @@ -526,17 +682,19 @@ do_negotiated(start_handshake, socket = _Socket, transport_cb = _Transport} } = State0) -> + ServerPrivateKey = get_server_private_key(KeyShare), + {Ref,Maybe} = maybe(), try %% Create server_hello %% Extensions: supported_versions, key_share, (pre_shared_key) - ServerHello = server_hello(server_hello, SessionId, KeyShare, ConnectionStates0), + ServerHello = server_hello(server_hello, SessionId, KeyShare, ConnectionStates0, ALPN), {State1, _} = tls_connection:send_handshake(ServerHello, State0), State2 = - calculate_handshake_secrets(ClientKey, SelectedGroup, KeyShare, State1), + calculate_handshake_secrets(ClientPublicKey, ServerPrivateKey, SelectedGroup, State1), State3 = ssl_record:step_encryption_state(State2), @@ -550,7 +708,7 @@ do_negotiated(start_handshake, {State5, NextState} = maybe_send_certificate_request(State4, SslOpts), %% Create Certificate - Certificate = certificate(OwnCert, CertDbHandle, CertDbRef, <<>>, server), + Certificate = Maybe(certificate(OwnCert, CertDbHandle, CertDbRef, <<>>, server)), %% Encode Certificate State6 = tls_connection:queue_handshake(Certificate, State5), @@ -574,14 +732,16 @@ do_negotiated(start_handshake, catch {Ref, badarg} -> - ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, {digitally_sign, badarg}) + ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, {digitally_sign, badarg}); + {Ref, {no_suitable_certificates, Reason}} -> + ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, {no_suitable_certificates, Reason}) end. do_wait_cert(#certificate_1_3{} = Certificate, State0) -> {Ref,Maybe} = maybe(), try - Maybe(process_client_certificate(Certificate, State0)) + Maybe(process_certificate(Certificate, State0)) catch {Ref, {certificate_required, State}} -> {?ALERT_REC(?FATAL, ?CERTIFICATE_REQUIRED, certificate_required), State}; @@ -591,6 +751,8 @@ do_wait_cert(#certificate_1_3{} = Certificate, State0) -> {?ALERT_REC(?FATAL, ?INTERNAL_ERROR, Reason), State}; {Ref, {{handshake_failure, Reason}, State}} -> {?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, Reason), State}; + {Ref, {#alert{} = Alert, State}} -> + {Alert, State}; {#alert{} = Alert, State} -> {Alert, State} end. @@ -599,8 +761,8 @@ do_wait_cert(#certificate_1_3{} = Certificate, State0) -> do_wait_cv(#certificate_verify_1_3{} = CertificateVerify, State0) -> {Ref,Maybe} = maybe(), try - Maybe(verify_signature_algorithm(State0, CertificateVerify)), - Maybe(verify_certificate_verify(State0, CertificateVerify)) + State1 = Maybe(verify_signature_algorithm(State0, CertificateVerify)), + Maybe(verify_certificate_verify(State1, CertificateVerify)) catch {Ref, {{bad_certificate, Reason}, State}} -> {?ALERT_REC(?FATAL, ?BAD_CERTIFICATE, {bad_certificate, Reason}), State}; @@ -610,20 +772,9 @@ do_wait_cv(#certificate_verify_1_3{} = CertificateVerify, State0) -> {?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, {handshake_failure, Reason}), State} end. - +%% TLS Server do_wait_finished(#finished{verify_data = VerifyData}, - #state{connection_states = _ConnectionStates0, - session = #session{session_id = _SessionId, - own_certificate = _OwnCert}, - ssl_options = #ssl_options{} = _SslOpts, - key_share = _KeyShare, - handshake_env = #handshake_env{tls_handshake_history = _HHistory0}, - static_env = #static_env{ - cert_db = _CertDbHandle, - cert_db_ref = _CertDbRef, - socket = _Socket, - transport_cb = _Transport} - } = State0) -> + #state{static_env = #static_env{role = server}} = State0) -> {Ref,Maybe} = maybe(), @@ -639,19 +790,230 @@ do_wait_finished(#finished{verify_data = VerifyData}, catch {Ref, decrypt_error} -> ?ALERT_REC(?FATAL, ?DECRYPT_ERROR, decrypt_error) + end; +%% TLS Client +do_wait_finished(#finished{verify_data = _VerifyData}, + #state{static_env = #static_env{role = client}} = State0) -> + + {Ref,Maybe} = maybe(), + + try + %% Maybe(validate_client_finished(State0, VerifyData)), + + %% Maybe send Certificate + CertificateVerify + State1 = Maybe(maybe_queue_cert_cert_cv(State0)), + + Finished = finished(State1), + + %% Encode Finished + State2 = tls_connection:queue_handshake(Finished, State1), + + %% Send first flight + {State3, _} = tls_connection:send_handshake_flight(State2), + + State4 = calculate_traffic_secrets(State3), + + %% Configure traffic keys + ssl_record:step_encryption_state(State4) + + catch + {Ref, decrypt_error} -> + ?ALERT_REC(?FATAL, ?DECRYPT_ERROR, decrypt_error); + {Ref, badarg} -> + ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, {digitally_sign, badarg}); + {Ref, {no_suitable_certificates, Reason}} -> + ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, {no_suitable_certificates, Reason}) + end. + + +do_wait_sh(#server_hello{cipher_suite = SelectedCipherSuite, + session_id = SessionId, + extensions = Extensions} = ServerHello, + #state{key_share = ClientKeyShare0, + ssl_options = #ssl_options{ciphers = ClientCiphers, + supported_groups = ClientGroups0}} = State0) -> + ClientGroups = get_supported_groups(ClientGroups0), + ServerKeyShare0 = maps:get(key_share, Extensions, undefined), + ClientKeyShare = get_key_shares(ClientKeyShare0), + + {Ref,Maybe} = maybe(), + try + %% Go to state 'start' if server replies with 'HelloRetryRequest'. + Maybe(maybe_hello_retry_request(ServerHello, State0)), + + ServerKeyShare = get_key_shares(ServerKeyShare0), + + Maybe(validate_cipher_suite(SelectedCipherSuite, ClientCiphers)), + Maybe(validate_server_key_share(ClientGroups, ServerKeyShare)), + + %% Get server public key + {SelectedGroup, ServerPublicKey} = get_server_public_key(ServerKeyShare), + + {_, ClientPrivateKey} = get_client_private_key([SelectedGroup], ClientKeyShare), + + %% Update state + State1 = update_start_state(State0, + #{cipher => SelectedCipherSuite, + key_share => ClientKeyShare0, + session_id => SessionId, + group => SelectedGroup, + peer_public_key => ServerPublicKey}), + + State2 = calculate_handshake_secrets(ServerPublicKey, ClientPrivateKey, SelectedGroup, State1), + + State3 = ssl_record:step_encryption_state(State2), + + {State3, wait_ee} + + catch + {Ref, {State, StateName, ServerHello}} -> + {State, StateName, ServerHello}; + {Ref, {insufficient_security, no_suitable_groups}} -> + ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_groups); + {Ref, illegal_parameter} -> + ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER); + {Ref, no_suitable_cipher} -> + ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_cipher); + {Ref, {insufficient_security, no_suitable_signature_algorithm}} -> + ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, "No suitable signature algorithm"); + {Ref, {insufficient_security, no_suitable_public_key}} -> + ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_public_key) + end. + + +do_wait_ee(#encrypted_extensions{extensions = _Extensions}, State0) -> + + {Ref,_Maybe} = maybe(), + + try + {State0, wait_cert_cr} + catch + {Ref, {insufficient_security, no_suitable_groups}} -> + ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_groups); + {Ref, illegal_parameter} -> + ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER); + {Ref, no_suitable_cipher} -> + ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_cipher); + {Ref, {insufficient_security, no_suitable_signature_algorithm}} -> + ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, "No suitable signature algorithm"); + {Ref, {insufficient_security, no_suitable_public_key}} -> + ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_public_key) + end. + + +do_wait_cert_cr(#certificate_1_3{} = Certificate, State0) -> + {Ref,Maybe} = maybe(), + try + Maybe(process_certificate(Certificate, State0)) + catch + {Ref, {certificate_required, _State}} -> + ?ALERT_REC(?FATAL, ?CERTIFICATE_REQUIRED, certificate_required); + {Ref, {{certificate_unknown, Reason}, _State}} -> + ?ALERT_REC(?FATAL, ?CERTIFICATE_UNKNOWN, Reason); + {Ref, {{internal_error, Reason}, _State}} -> + ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, Reason); + {Ref, {{handshake_failure, Reason}, _State}} -> + ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, Reason); + {Ref, {#alert{} = Alert, State}} -> + {Alert, State} + end; +do_wait_cert_cr(#certificate_request_1_3{} = CertificateRequest, State0) -> + {Ref,Maybe} = maybe(), + try + Maybe(process_certificate_request(CertificateRequest, State0)) + catch + {Ref, {certificate_required, _State}} -> + ?ALERT_REC(?FATAL, ?CERTIFICATE_REQUIRED, certificate_required); + {Ref, {{certificate_unknown, Reason}, _State}} -> + ?ALERT_REC(?FATAL, ?CERTIFICATE_UNKNOWN, Reason); + {Ref, {illegal_parameter, Reason}} -> + ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER, Reason); + {Ref, {{internal_error, Reason}, _State}} -> + ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, Reason); + {Ref, {{handshake_failure, Reason}, _State}} -> + ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, Reason) end. + %% TODO: Remove this function! %% not_implemented(State, Reason) -> %% {error, {not_implemented, State, Reason}}. -%% + %% not_implemented(update_secrets, State0, Reason) -> %% State1 = calculate_traffic_secrets(State0), %% State = ssl_record:step_encryption_state(State1), %% {error, {not_implemented, State, Reason}}. +%% For reasons of backward compatibility with middleboxes (see +%% Appendix D.4), the HelloRetryRequest message uses the same structure +%% as the ServerHello, but with Random set to the special value of the +%% SHA-256 of "HelloRetryRequest": +%% +%% CF 21 AD 74 E5 9A 61 11 BE 1D 8C 02 1E 65 B8 91 +%% C2 A2 11 16 7A BB 8C 5E 07 9E 09 E2 C8 A8 33 9C +%% +%% Upon receiving a message with type server_hello, implementations MUST +%% first examine the Random value and, if it matches this value, process +%% it as described in Section 4.1.4). +maybe_hello_retry_request(#server_hello{random = ?HELLO_RETRY_REQUEST_RANDOM} = ServerHello, State0) -> + {error, {State0, start, ServerHello}}; +maybe_hello_retry_request(_, _) -> + ok. + + +maybe_queue_cert_cert_cv(#state{client_certificate_requested = false} = State) -> + {ok, State}; +maybe_queue_cert_cert_cv(#state{connection_states = _ConnectionStates0, + session = #session{session_id = _SessionId, + own_certificate = OwnCert}, + ssl_options = #ssl_options{} = _SslOpts, + key_share = _KeyShare, + handshake_env = #handshake_env{tls_handshake_history = _HHistory0}, + static_env = #static_env{ + role = client, + cert_db = CertDbHandle, + cert_db_ref = CertDbRef, + socket = _Socket, + transport_cb = _Transport} + } = State0) -> + {Ref,Maybe} = maybe(), + try + %% Create Certificate + Certificate = Maybe(certificate(OwnCert, CertDbHandle, CertDbRef, <<>>, client)), + + %% Encode Certificate + State1 = tls_connection:queue_handshake(Certificate, State0), + + %% Maybe create and queue CertificateVerify + State = Maybe(maybe_queue_cert_verify(Certificate, State1)), + {ok, State} + catch + {Ref, badarg} -> + {error, badarg} + end. + + +%% Clients MUST send this message whenever authenticating via a certificate +%% (i.e., when the Certificate message is non-empty). +maybe_queue_cert_verify(#certificate_1_3{certificate_list = []}, State) -> + {ok, State}; +maybe_queue_cert_verify(_Certificate, + #state{connection_states = _ConnectionStates0, + session = #session{sign_alg = SignatureScheme}, + connection_env = #connection_env{private_key = CertPrivateKey}, + static_env = #static_env{role = client} + } = State) -> + {Ref,Maybe} = maybe(), + try + CertificateVerify = Maybe(certificate_verify(CertPrivateKey, SignatureScheme, State, client)), + {ok, tls_connection:queue_handshake(CertificateVerify, State)} + catch + {Ref, badarg} -> + {error, badarg} + end. + %% Recipients of Finished messages MUST verify that the contents are %% correct and if incorrect MUST terminate the connection with a @@ -679,12 +1041,13 @@ compare_verify_data(_, _) -> {error, decrypt_error}. -send_hello_retry_request(#state{connection_states = ConnectionStates0} = State0, +send_hello_retry_request(#state{connection_states = ConnectionStates0, + handshake_env = #handshake_env{alpn = ALPN}} = State0, no_suitable_key, KeyShare, SessionId) -> - ServerHello = server_hello(hello_retry_request, SessionId, KeyShare, ConnectionStates0), + ServerHello = server_hello(hello_retry_request, SessionId, KeyShare, ConnectionStates0, ALPN), {State1, _} = tls_connection:send_handshake(ServerHello, State0), - %% TODO: Fix handshake history! + %% Update handshake history State2 = replace_ch1_with_message_hash(State1), {ok, {State2, start}}; @@ -703,19 +1066,44 @@ maybe_send_certificate_request(State, #ssl_options{ {tls_connection:queue_handshake(CertificateRequest, State), wait_cert}. -process_client_certificate(#certificate_1_3{ - certificate_request_context = <<>>, - certificate_list = []}, - #state{ssl_options = - #ssl_options{ - fail_if_no_peer_cert = false}} = State) -> +process_certificate_request(#certificate_request_1_3{}, + #state{session = #session{own_certificate = undefined}} = State) -> + {ok, {State#state{client_certificate_requested = true}, wait_cert}}; + +process_certificate_request(#certificate_request_1_3{ + extensions = Extensions}, + #state{session = #session{own_certificate = Cert} = Session} = State) -> + ServerSignAlgs = get_signature_scheme_list( + maps:get(signature_algs, Extensions, undefined)), + ServerSignAlgsCert = get_signature_scheme_list( + maps:get(signature_algs_cert, Extensions, undefined)), + + {_PublicKeyAlgo, SignAlgo, SignHash} = get_certificate_params(Cert), + + %% Check if server supports signature algorithm of client certificate + case check_cert_sign_algo(SignAlgo, SignHash, ServerSignAlgs, ServerSignAlgsCert) of + ok -> + {ok, {State#state{client_certificate_requested = true}, wait_cert}}; + {error, _} -> + %% Certificate not supported: send empty certificate in state 'wait_finished' + {ok, {State#state{client_certificate_requested = true, + session = Session#session{own_certificate = undefined}}, wait_cert}} + end. + + +process_certificate(#certificate_1_3{ + certificate_request_context = <<>>, + certificate_list = []}, + #state{ssl_options = + #ssl_options{ + fail_if_no_peer_cert = false}} = State) -> {ok, {State, wait_finished}}; -process_client_certificate(#certificate_1_3{ - certificate_request_context = <<>>, - certificate_list = []}, - #state{ssl_options = - #ssl_options{ - fail_if_no_peer_cert = true}} = State0) -> +process_certificate(#certificate_1_3{ + certificate_request_context = <<>>, + certificate_list = []}, + #state{ssl_options = + #ssl_options{ + fail_if_no_peer_cert = true}} = State0) -> %% At this point the client believes that the connection is up and starts using %% its traffic secrets. In order to be able send an proper Alert to the client @@ -724,19 +1112,18 @@ process_client_certificate(#certificate_1_3{ State1 = calculate_traffic_secrets(State0), State = ssl_record:step_encryption_state(State1), {error, {certificate_required, State}}; -process_client_certificate(#certificate_1_3{certificate_list = Certs0}, - #state{ssl_options = - #ssl_options{signature_algs = SignAlgs, - signature_algs_cert = SignAlgsCert} = SslOptions, - static_env = - #static_env{ - role = Role, - host = Host, - cert_db = CertDbHandle, - cert_db_ref = CertDbRef, - crl_db = CRLDbHandle}} = State0) -> +process_certificate(#certificate_1_3{certificate_list = Certs0}, + #state{ssl_options = + #ssl_options{signature_algs = SignAlgs, + signature_algs_cert = SignAlgsCert} = SslOptions, + static_env = + #static_env{ + role = Role, + host = Host, + cert_db = CertDbHandle, + cert_db_ref = CertDbRef, + crl_db = CRLDbHandle}} = State0) -> %% TODO: handle extensions! - %% Remove extensions from list of certificates! Certs = convert_certificate_chain(Certs0), case is_supported_signature_algorithm(Certs, SignAlgs, SignAlgsCert) of @@ -747,13 +1134,11 @@ process_client_certificate(#certificate_1_3{certificate_list = Certs0}, State = store_peer_cert(State0, PeerCert, PublicKeyInfo), {ok, {State, wait_cv}}; {error, Reason} -> - State1 = calculate_traffic_secrets(State0), - State = ssl_record:step_encryption_state(State1), + State = update_encryption_state(Role, State0), {error, {Reason, State}}; - #alert{} = Alert -> - State1 = calculate_traffic_secrets(State0), - State = ssl_record:step_encryption_state(State1), - {Alert, State} + {ok, #alert{} = Alert} -> + State = update_encryption_state(Role, State0), + {error, {Alert, State}} end; false -> State1 = calculate_traffic_secrets(State0), @@ -777,6 +1162,17 @@ is_supported_signature_algorithm([BinCert|_], SignAlgs0) -> lists:member(Scheme, SignAlgs). +%% Sets correct encryption state when sending Alerts in shared states that use different secrets. +%% - If client: use handshake secrets. +%% - If server: use traffic secrets as by this time the client's state machine +%% already stepped into the 'connection' state. +update_encryption_state(server, State0) -> + State1 = calculate_traffic_secrets(State0), + ssl_record:step_encryption_state(State1); +update_encryption_state(client, State) -> + State. + + validate_certificate_chain(Certs, CertDbHandle, CertDbRef, SslOptions, CRLDbHandle, Role, Host) -> ServerName = ssl_handshake:server_name(SslOptions#ssl_options.server_name_indication, Host, Role), [PeerCert | ChainCerts ] = Certs, @@ -797,12 +1193,12 @@ validate_certificate_chain(Certs, CertDbHandle, CertDbRef, SslOptions, CRLDbHand {ok, {PublicKeyInfo,_}} -> {ok, {PeerCert, PublicKeyInfo}}; {error, Reason} -> - ssl_handshake:handle_path_validation_error(Reason, PeerCert, ChainCerts, - SslOptions, Options, - CertDbHandle, CertDbRef) + {ok, ssl_handshake:handle_path_validation_error(Reason, PeerCert, ChainCerts, + SslOptions, Options, + CertDbHandle, CertDbRef)} end catch - error:{badmatch,{asn1, Asn1Reason}} -> + error:{badmatch,{error, {asn1, Asn1Reason}}} -> %% ASN-1 decode of certificate somehow failed {error, {certificate_unknown, {failed_to_decode_certificate, Asn1Reason}}}; error:OtherReason -> @@ -861,7 +1257,7 @@ message_hash(ClientHello1, HKDFAlgo) -> crypto:hash(HKDFAlgo, ClientHello1)]. -calculate_handshake_secrets(ClientKey, SelectedGroup, KeyShare, +calculate_handshake_secrets(PublicKey, PrivateKey, SelectedGroup, #state{connection_states = ConnectionStates, handshake_env = #handshake_env{ @@ -874,13 +1270,13 @@ calculate_handshake_secrets(ClientKey, SelectedGroup, KeyShare, %% Calculate handshake_secret PSK = binary:copy(<<0>>, ssl_cipher:hash_size(HKDFAlgo)), EarlySecret = tls_v1:key_schedule(early_secret, HKDFAlgo , {psk, PSK}), - PrivateKey = get_server_private_key(KeyShare), %% #'ECPrivateKey'{} - IKM = calculate_shared_secret(ClientKey, PrivateKey, SelectedGroup), + IKM = calculate_shared_secret(PublicKey, PrivateKey, SelectedGroup), HandshakeSecret = tls_v1:key_schedule(handshake_secret, HKDFAlgo, IKM, EarlySecret), %% Calculate [sender]_handshake_traffic_secret {Messages, _} = HHistory, + ClientHSTrafficSecret = tls_v1:client_handshake_traffic_secret(HKDFAlgo, HandshakeSecret, lists:reverse(Messages)), ServerHSTrafficSecret = @@ -899,10 +1295,13 @@ calculate_handshake_secrets(ClientKey, SelectedGroup, KeyShare, ReadKey, ReadIV, ReadFinishedKey, WriteKey, WriteIV, WriteFinishedKey). -calculate_traffic_secrets(#state{connection_states = ConnectionStates, - handshake_env = - #handshake_env{ - tls_handshake_history = HHistory}} = State0) -> + +calculate_traffic_secrets(#state{ + static_env = #static_env{role = Role}, + connection_states = ConnectionStates, + handshake_env = + #handshake_env{ + tls_handshake_history = HHistory}} = State0) -> #{security_parameters := SecParamsR} = ssl_record:pending_connection_state(ConnectionStates, read), #security_parameters{prf_algorithm = HKDFAlgo, @@ -913,7 +1312,7 @@ calculate_traffic_secrets(#state{connection_states = ConnectionStates, tls_v1:key_schedule(master_secret, HKDFAlgo, HandshakeSecret), %% Get the correct list messages for the handshake context. - Messages = get_handshake_context(HHistory), + Messages = get_handshake_context(Role, HHistory), %% Calculate [sender]_application_traffic_secret_0 ClientAppTrafficSecret0 = @@ -942,11 +1341,6 @@ get_private_key(#key_share_entry{ {_, PrivateKey}}) -> PrivateKey. -%% TODO: implement EC keys -get_public_key({?'rsaEncryption', PublicKey, _}) -> - PublicKey. - - %% X25519, X448 calculate_shared_secret(OthersKey, MyKey, Group) when is_binary(OthersKey) andalso is_binary(MyKey) andalso @@ -966,9 +1360,11 @@ calculate_shared_secret(OthersKey, MyKey = #'ECPrivateKey'{}, _Group) public_key:compute_key(Point, MyKey). -update_pending_connection_states(#state{connection_states = - CS = #{pending_read := PendingRead0, - pending_write := PendingWrite0}} = State, +update_pending_connection_states(#state{ + static_env = #static_env{role = server}, + connection_states = + CS = #{pending_read := PendingRead0, + pending_write := PendingWrite0}} = State, HandshakeSecret, ReadKey, ReadIV, ReadFinishedKey, WriteKey, WriteIV, WriteFinishedKey) -> @@ -977,8 +1373,23 @@ update_pending_connection_states(#state{connection_states = PendingWrite = update_connection_state(PendingWrite0, HandshakeSecret, WriteKey, WriteIV, WriteFinishedKey), State#state{connection_states = CS#{pending_read => PendingRead, + pending_write => PendingWrite}}; +update_pending_connection_states(#state{ + static_env = #static_env{role = client}, + connection_states = + CS = #{pending_read := PendingRead0, + pending_write := PendingWrite0}} = State, + HandshakeSecret, + ReadKey, ReadIV, ReadFinishedKey, + WriteKey, WriteIV, WriteFinishedKey) -> + PendingRead = update_connection_state(PendingRead0, HandshakeSecret, + WriteKey, WriteIV, WriteFinishedKey), + PendingWrite = update_connection_state(PendingWrite0, HandshakeSecret, + ReadKey, ReadIV, ReadFinishedKey), + State#state{connection_states = CS#{pending_read => PendingRead, pending_write => PendingWrite}}. + update_connection_state(ConnectionState = #{security_parameters := SecurityParameters0}, HandshakeSecret, Key, IV, FinishedKey) -> %% Store secret @@ -988,11 +1399,24 @@ update_connection_state(ConnectionState = #{security_parameters := SecurityParam cipher_state => cipher_init(Key, IV, FinishedKey)}. +update_start_state(State, Map) -> + Cipher = maps:get(cipher, Map, undefined), + KeyShare = maps:get(key_share, Map, undefined), + SessionId = maps:get(session_id, Map, undefined), + Group = maps:get(group, Map, undefined), + SelectedSignAlg = maps:get(sign_alg, Map, undefined), + PeerPublicKey = maps:get(peer_public_key, Map, undefined), + ALPNProtocol = maps:get(alpn, Map, undefined), + update_start_state(State, Cipher, KeyShare, SessionId, + Group, SelectedSignAlg, PeerPublicKey, + ALPNProtocol). +%% update_start_state(#state{connection_states = ConnectionStates0, + handshake_env = #handshake_env{} = HsEnv, connection_env = CEnv, session = Session} = State, Cipher, KeyShare, SessionId, - Group, SelectedSignAlg, ClientPubKey) -> + Group, SelectedSignAlg, PeerPublicKey, ALPNProtocol) -> #{security_parameters := SecParamsR0} = PendingRead = maps:get(pending_read, ConnectionStates0), #{security_parameters := SecParamsW0} = PendingWrite = @@ -1003,11 +1427,12 @@ update_start_state(#state{connection_states = ConnectionStates0, ConnectionStates0#{pending_read => PendingRead#{security_parameters => SecParamsR}, pending_write => PendingWrite#{security_parameters => SecParamsW}}, State#state{connection_states = ConnectionStates, + handshake_env = HsEnv#handshake_env{alpn = ALPNProtocol}, key_share = KeyShare, session = Session#session{session_id = SessionId, ecc = Group, sign_alg = SelectedSignAlg, - dh_public_value = ClientPubKey, + dh_public_value = PeerPublicKey, cipher_suite = Cipher}, connection_env = CEnv#connection_env{negotiated_version = {3,4}}}. @@ -1071,25 +1496,41 @@ get_handshake_context_cv({[<<15,_/binary>>|Messages], _}) -> %% %% Drop all client messages from the front of the iolist using the property that %% incoming messages are binaries. -get_handshake_context({Messages, _}) -> - get_handshake_context(Messages); -get_handshake_context([H|T]) when is_binary(H) -> - get_handshake_context(T); -get_handshake_context(L) -> +get_handshake_context(server, {Messages, _}) -> + get_handshake_context_server(Messages); +get_handshake_context(client, {Messages, _}) -> + get_handshake_context_client(Messages). + +get_handshake_context_server([H|T]) when is_binary(H) -> + get_handshake_context_server(T); +get_handshake_context_server(L) -> L. +get_handshake_context_client([H|T]) when is_list(H) -> + get_handshake_context_client(T); +get_handshake_context_client(L) -> + L. + + +%% If the CertificateVerify message is sent by a server, the signature +%% algorithm MUST be one offered in the client's "signature_algorithms" +%% extension unless no valid certificate chain can be produced without +%% unsupported algorithms +%% %% If sent by a client, the signature algorithm used in the signature %% MUST be one of those present in the supported_signature_algorithms %% field of the "signature_algorithms" extension in the %% CertificateRequest message. -verify_signature_algorithm(#state{ssl_options = - #ssl_options{ - signature_algs = ServerSignAlgs}} = State0, - #certificate_verify_1_3{algorithm = ClientSignAlg}) -> - case lists:member(ClientSignAlg, ServerSignAlgs) of +verify_signature_algorithm(#state{ + static_env = #static_env{role = Role}, + ssl_options = + #ssl_options{ + signature_algs = LocalSignAlgs}} = State0, + #certificate_verify_1_3{algorithm = PeerSignAlg}) -> + case lists:member(PeerSignAlg, LocalSignAlgs) of true -> - ok; + {ok, maybe_update_selected_sign_alg(State0, PeerSignAlg, Role)}; false -> State1 = calculate_traffic_secrets(State0), State = ssl_record:step_encryption_state(State1), @@ -1098,11 +1539,19 @@ verify_signature_algorithm(#state{ssl_options = end. -verify_certificate_verify(#state{connection_states = ConnectionStates, - handshake_env = - #handshake_env{ - public_key_info = PublicKeyInfo, - tls_handshake_history = HHistory}} = State0, +maybe_update_selected_sign_alg(#state{session = Session} = State, SignAlg, client) -> + State#state{session = Session#session{sign_alg = SignAlg}}; +maybe_update_selected_sign_alg(State, _, _) -> + State. + + +verify_certificate_verify(#state{ + static_env = #static_env{role = Role}, + connection_states = ConnectionStates, + handshake_env = + #handshake_env{ + public_key_info = PublicKeyInfo, + tls_handshake_history = HHistory}} = State0, #certificate_verify_1_3{ algorithm = SignatureScheme, signature = Signature}) -> @@ -1120,12 +1569,11 @@ verify_certificate_verify(#state{connection_states = ConnectionStates, %% Transcript-Hash uses the HKDF hash function defined by the cipher suite. THash = tls_v1:transcript_hash(Context, HKDFAlgo), - PublicKey = get_public_key(PublicKeyInfo), + ContextString = peer_context_string(Role), %% Digital signatures use the hash function defined by the selected signature %% scheme. - case verify(THash, <<"TLS 1.3, client CertificateVerify">>, - HashAlgo, Signature, PublicKey) of + case verify(THash, ContextString, HashAlgo, Signature, PublicKeyInfo) of {ok, true} -> {ok, {State0, wait_finished}}; {ok, false} -> @@ -1139,6 +1587,19 @@ verify_certificate_verify(#state{connection_states = ConnectionStates, end. +context_string(server) -> + <<"TLS 1.3, server CertificateVerify">>; +context_string(client) -> + <<"TLS 1.3, client CertificateVerify">>. + + +%% Return context string for verifing peer signature +peer_context_string(server) -> + <<"TLS 1.3, client CertificateVerify">>; +peer_context_string(client) -> + <<"TLS 1.3, server CertificateVerify">>. + + %% If there is no overlap between the received %% "supported_groups" and the groups supported by the server, then the %% server MUST abort the handshake with a "handshake_failure" or an @@ -1172,14 +1633,36 @@ select_common_groups(ServerGroups, ClientGroups) -> %% for groups not listed in the client's "supported_groups" extension. %% Servers MAY check for violations of these rules and abort the %% handshake with an "illegal_parameter" alert if one is violated. -validate_key_share(_ ,[]) -> +validate_client_key_share(_ ,[]) -> ok; -validate_key_share([], _) -> +validate_client_key_share([], _) -> {error, illegal_parameter}; -validate_key_share([G|ClientGroups], [{_, G, _}|ClientShares]) -> - validate_key_share(ClientGroups, ClientShares); -validate_key_share([_|ClientGroups], [_|_] = ClientShares) -> - validate_key_share(ClientGroups, ClientShares). +validate_client_key_share([G|ClientGroups], [{_, G, _}|ClientShares]) -> + validate_client_key_share(ClientGroups, ClientShares); +validate_client_key_share([_|ClientGroups], [_|_] = ClientShares) -> + validate_client_key_share(ClientGroups, ClientShares). + + +%% Verify that selected group is offered by the client. +validate_server_key_share([G|_ClientGroups], {_, G, _}) -> + ok; +validate_server_key_share([_|ClientGroups], {_, _, _} = ServerKeyShare) -> + validate_server_key_share(ClientGroups, ServerKeyShare). + + +validate_selected_group(SelectedGroup, [SelectedGroup|_]) -> + {error, {illegal_parameter, + "Selected group sent by the server shall not correspond to a group" + " which was provided in the key_share extension"}}; +validate_selected_group(SelectedGroup, ClientGroups) -> + case lists:member(SelectedGroup, ClientGroups) of + true -> + ok; + false -> + {error, {illegal_parameter, + "Selected group sent by the server shall correspond to a group" + " which was provided in the supported_groups extension"}} + end. get_client_public_key([Group|_] = Groups, ClientShares) -> @@ -1197,20 +1680,50 @@ get_client_public_key([Group|Groups], ClientShares, PreferredGroup) -> get_client_public_key(Groups, ClientShares, PreferredGroup) end. +get_client_private_key([Group|_] = Groups, ClientShares) -> + get_client_private_key(Groups, ClientShares, Group). +%% +get_client_private_key(_, [], PreferredGroup) -> + {PreferredGroup, no_suitable_key}; +get_client_private_key([], _, PreferredGroup) -> + {PreferredGroup, no_suitable_key}; +get_client_private_key([Group|Groups], ClientShares, PreferredGroup) -> + case lists:keysearch(Group, 2, ClientShares) of + {value, {_, _, {_, ClientPrivateKey}}} -> + {Group, ClientPrivateKey}; + {value, {_, _, #'ECPrivateKey'{} = ClientPrivateKey}} -> + {Group, ClientPrivateKey}; + false -> + get_client_private_key(Groups, ClientShares, PreferredGroup) + end. + + +get_server_public_key({key_share_entry, Group, PublicKey}) -> + {Group, PublicKey}. + + +%% RFC 7301 - Application-Layer Protocol Negotiation Extension +%% It is expected that a server will have a list of protocols that it +%% supports, in preference order, and will only select a protocol if the +%% client supports it. In that case, the server SHOULD select the most +%% highly preferred protocol that it supports and that is also +%% advertised by the client. In the event that the server supports no +%% protocols that the client advertises, then the server SHALL respond +%% with a fatal "no_application_protocol" alert. +handle_alpn(undefined, _) -> + {ok, undefined}; +handle_alpn([], _) -> + {error, no_application_protocol}; +handle_alpn([_|_], undefined) -> + {ok, undefined}; +handle_alpn([ServerProtocol|T], ClientProtocols) -> + case lists:member(ServerProtocol, ClientProtocols) of + true -> + {ok, ServerProtocol}; + false -> + handle_alpn(T, ClientProtocols) + end. -%% get_client_public_key(Group, ClientShares) -> -%% case lists:keysearch(Group, 2, ClientShares) of -%% {value, {_, _, ClientPublicKey}} -> -%% ClientPublicKey; -%% false -> -%% %% 4.1.4. Hello Retry Request -%% %% -%% %% The server will send this message in response to a ClientHello -%% %% message if it is able to find an acceptable set of parameters but the -%% %% ClientHello does not contain sufficient information to proceed with -%% %% the handshake. -%% no_suitable_key -%% end. select_cipher_suite([], _) -> {error, no_suitable_cipher}; @@ -1223,6 +1736,19 @@ select_cipher_suite([Cipher|ClientCiphers], ServerCiphers) -> select_cipher_suite(ClientCiphers, ServerCiphers) end. + +%% RFC 8446 4.1.3 ServerHello +%% A client which receives a cipher suite that was not offered MUST abort the +%% handshake with an "illegal_parameter" alert. +validate_cipher_suite(Cipher, ClientCiphers) -> + case lists:member(Cipher, ClientCiphers) of + true -> + ok; + false -> + {error, illegal_parameter} + end. + + %% RFC 8446 (TLS 1.3) %% TLS 1.3 provides two extensions for indicating which signature %% algorithms may be used in digital signatures. The @@ -1246,15 +1772,20 @@ check_cert_sign_algo(SignAlgo, SignHash, _, ClientSignAlgsCert) -> %% DSA keys are not supported by TLS 1.3 select_sign_algo(dsa, _ClientSignAlgs, _ServerSignAlgs) -> {error, {insufficient_security, no_suitable_public_key}}; -%% TODO: Implement support for ECDSA keys! select_sign_algo(_, [], _) -> {error, {insufficient_security, no_suitable_signature_algorithm}}; select_sign_algo(PublicKeyAlgo, [C|ClientSignAlgs], ServerSignAlgs) -> {_, S, _} = ssl_cipher:scheme_to_components(C), %% RSASSA-PKCS1-v1_5 and Legacy algorithms are not defined for use in signed %% TLS handshake messages: filter sha-1 and rsa_pkcs1. + %% + %% RSASSA-PSS RSAE algorithms: If the public key is carried in an X.509 + %% certificate, it MUST use the rsaEncryption OID. + %% RSASSA-PSS PSS algorithms: If the public key is carried in an X.509 certificate, + %% it MUST use the RSASSA-PSS OID. case ((PublicKeyAlgo =:= rsa andalso S =:= rsa_pss_rsae) - orelse (PublicKeyAlgo =:= rsa_pss andalso S =:= rsa_pss_rsae)) + orelse (PublicKeyAlgo =:= rsa_pss andalso S =:= rsa_pss_pss) + orelse (PublicKeyAlgo =:= ecdsa andalso S =:= ecdsa)) andalso lists:member(C, ServerSignAlgs) of true -> @@ -1331,7 +1862,12 @@ get_supported_groups(#supported_groups{supported_groups = Groups}) -> Groups. get_key_shares(#key_share_client_hello{client_shares = ClientShares}) -> - ClientShares. + ClientShares; +get_key_shares(#key_share_server_hello{server_share = ServerShare}) -> + ServerShare. + +get_selected_group(#key_share_hello_retry_request{selected_group = SelectedGroup}) -> + SelectedGroup. maybe() -> Ref = erlang:make_ref(), diff --git a/lib/ssl/src/tls_v1.erl b/lib/ssl/src/tls_v1.erl index 27cd5765e5..f7c8c770ae 100644 --- a/lib/ssl/src/tls_v1.erl +++ b/lib/ssl/src/tls_v1.erl @@ -606,8 +606,26 @@ signature_schemes(Version, SignatureSchemes) when is_tuple(Version) Acc end; %% Special clause for filtering out the legacy hash-sign tuples. - (_ , Acc) -> - Acc + ({Hash, dsa = Sign} = Alg, Acc) -> + case proplists:get_bool(dss, PubKeys) + andalso proplists:get_bool(Hash, Hashes) + andalso is_pair(Hash, Sign, Hashes) + of + true -> + [Alg | Acc]; + false -> + Acc + end; + ({Hash, Sign} = Alg, Acc) -> + case proplists:get_bool(Sign, PubKeys) + andalso proplists:get_bool(Hash, Hashes) + andalso is_pair(Hash, Sign, Hashes) + of + true -> + [Alg | Acc]; + false -> + Acc + end end, Supported = lists:foldl(Fun, [], SignatureSchemes), lists:reverse(Supported); diff --git a/lib/ssl/test/openssl_server_cipher_suite_SUITE.erl b/lib/ssl/test/openssl_server_cipher_suite_SUITE.erl index 6ce34ce7fa..0d68d84d61 100644 --- a/lib/ssl/test/openssl_server_cipher_suite_SUITE.erl +++ b/lib/ssl/test/openssl_server_cipher_suite_SUITE.erl @@ -177,7 +177,8 @@ init_per_suite(Config) -> end_per_suite(_Config) -> ssl:stop(), - application:stop(crypto). + application:stop(crypto), + ssl_test_lib:kill_openssl(). %%-------------------------------------------------------------------- init_per_group(GroupName, Config) -> diff --git a/lib/ssl/test/ssl_ECC_openssl_SUITE.erl b/lib/ssl/test/ssl_ECC_openssl_SUITE.erl index 68d4e910fd..787c08a517 100644 --- a/lib/ssl/test/ssl_ECC_openssl_SUITE.erl +++ b/lib/ssl/test/ssl_ECC_openssl_SUITE.erl @@ -67,7 +67,8 @@ init_per_suite(Config0) -> end_per_suite(_Config) -> application:stop(ssl), - application:stop(crypto). + application:stop(crypto), + ssl_test_lib:kill_openssl(). %%-------------------------------------------------------------------- init_per_group(GroupName, Config) -> diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl index 20d9f28512..86a0aaf67b 100644 --- a/lib/ssl/test/ssl_basic_SUITE.erl +++ b/lib/ssl/test/ssl_basic_SUITE.erl @@ -164,6 +164,7 @@ api_tests() -> prf, socket_options, active_n, + internal_active_1, cipher_suites, handshake_continue, handshake_continue_timeout, @@ -243,23 +244,46 @@ rizzo_tests() -> %% For testing TLS 1.3 features and possible regressions tls13_test_group() -> - [tls13_enable_client_side, + [handshake_continue_tls13_client, + tls13_enable_client_side, tls13_enable_server_side, tls_record_1_3_encode_decode, tls13_finished_verify_data, tls13_1_RTT_handshake, + tls12_ssl_server_tls13_ssl_client, tls13_basic_ssl_server_openssl_client, + tls13_basic_ssl_server_ssl_client, + tls13_basic_openssl_server_ssl_client, tls13_custom_groups_ssl_server_openssl_client, + tls13_custom_groups_ssl_server_ssl_client, tls13_hello_retry_request_ssl_server_openssl_client, + tls13_hello_retry_request_ssl_server_ssl_client, tls13_client_auth_empty_cert_alert_ssl_server_openssl_client, + tls13_client_auth_empty_cert_alert_ssl_server_ssl_client, tls13_client_auth_empty_cert_ssl_server_openssl_client, + tls13_client_auth_empty_cert_ssl_server_ssl_client, tls13_client_auth_ssl_server_openssl_client, + tls13_client_auth_ssl_server_ssl_client, tls13_hrr_client_auth_empty_cert_alert_ssl_server_openssl_client, + tls13_hrr_client_auth_empty_cert_alert_ssl_server_ssl_client, tls13_hrr_client_auth_empty_cert_ssl_server_openssl_client, + tls13_hrr_client_auth_empty_cert_ssl_server_ssl_client, tls13_hrr_client_auth_ssl_server_openssl_client, + tls13_hrr_client_auth_ssl_server_ssl_client, tls13_unsupported_sign_algo_client_auth_ssl_server_openssl_client, + tls13_unsupported_sign_algo_client_auth_ssl_server_ssl_client, tls13_unsupported_sign_algo_cert_client_auth_ssl_server_openssl_client, - tls13_connection_information]. + tls13_unsupported_sign_algo_cert_client_auth_ssl_server_ssl_client, + tls13_connection_information, + tls13_ssl_server_with_alpn_ssl_client, + tls13_ssl_server_with_alpn_ssl_client_empty_alpn, + tls13_ssl_server_with_alpn_ssl_client_bad_alpn, + tls13_ssl_server_with_alpn_ssl_client_alpn, + tls13_ecdsa_ssl_server_openssl_client, + tls13_ecdsa_ssl_server_ssl_client, + tls13_ecdsa_openssl_server_ssl_client, + tls13_ecdsa_client_auth_ssl_server_ssl_client + ]. %%-------------------------------------------------------------------- init_per_suite(Config0) -> @@ -290,10 +314,17 @@ init_per_group(GroupName, Config) when GroupName == basic_tls; GroupName == options; GroupName == basic; GroupName == session; - GroupName == error_handling_tests_tls; - GroupName == tls13_test_group - -> - ssl_test_lib:clean_tls_version(Config); + GroupName == error_handling_tests_tls -> + ssl_test_lib:clean_tls_version(Config); +%% Do not automatically configure TLS version for the 'tlsv1.3' group +init_per_group('tlsv1.3' = GroupName, Config) -> + case ssl_test_lib:sufficient_crypto_support(GroupName) of + true -> + ssl:start(), + Config; + false -> + {skip, "Missing crypto support"} + end; init_per_group(GroupName, Config) -> ssl_test_lib:clean_tls_version(Config), case ssl_test_lib:is_tls_version(GroupName) andalso ssl_test_lib:sufficient_crypto_support(GroupName) of @@ -488,6 +519,15 @@ init_per_testcase(accept_pool, Config) -> ssl_test_lib:ct_log_supported_protocol_versions(Config), Config end; + +init_per_testcase(internal_active_1, Config) -> + ssl:stop(), + application:load(ssl), + application:set_env(ssl, internal_active_n, 1), + ssl:start(), + ct:timetrap({seconds, 5}), + Config; + init_per_testcase(controller_dies, Config) -> ct:timetrap({seconds, 10}), Config; @@ -510,6 +550,10 @@ end_per_testcase(reuse_session_expired, Config) -> application:unset_env(ssl, session_delay_cleanup_time), end_per_testcase(default_action, Config); +end_per_testcase(internal_active_n, Config) -> + application:unset_env(ssl, internal_active_n), + end_per_testcase(default_action, Config); + end_per_testcase(Case, Config) when Case == protocol_versions; Case == empty_protocol_versions-> application:unset_env(ssl, protocol_versions), @@ -657,6 +701,43 @@ handshake_continue(Config) when is_list(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +handshake_continue_tls13_client() -> + [{doc, "Test API function ssl:handshake_continue/3"}]. +handshake_continue_tls13_client(Config) when is_list(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), + ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ClientOpts0], + + ClientOptsHello0 = ssl_test_lib:ssl_options([{handshake, hello}], Config), + ClientOptsHello = [{versions, ['tlsv1.2','tlsv1.3']}|ClientOptsHello0], + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ssl_test_lib:ssl_options([{reuseaddr, true}, {handshake, hello}], + Config)}, + {continue_options, proplists:delete(reuseaddr, ServerOpts)} + ]), + + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ClientOptsHello}, + {continue_options, proplists:delete(reuseaddr, ClientOpts)}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + + %%------------------------------------------------------------------ handshake_continue_timeout() -> [{doc, "Test API function ssl:handshake_continue/3 with short timeout"}]. @@ -1236,14 +1317,14 @@ peername(Config) when is_list(Config) -> {options, [{port, 0} | ClientOpts]}]), ClientPort = ssl_test_lib:inet_port(Client), - ServerIp = ssl_test_lib:node_to_hostip(ServerNode), - ClientIp = ssl_test_lib:node_to_hostip(ClientNode), + ServerIp = ssl_test_lib:node_to_hostip(ServerNode, server), + ClientIp = ssl_test_lib:node_to_hostip(ClientNode, client), ServerMsg = {ok, {ClientIp, ClientPort}}, ClientMsg = {ok, {ServerIp, Port}}, - + ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), - + ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg), ssl_test_lib:close(Server), @@ -1346,10 +1427,10 @@ sockname(Config) when is_list(Config) -> %% so we can only get a ClientIP, ServerIP will always be 0.0.0.0 {0,0,0,0}; _ -> - ssl_test_lib:node_to_hostip(ServerNode) + ssl_test_lib:node_to_hostip(ServerNode, server) end, - ClientIp = ssl_test_lib:node_to_hostip(ClientNode), + ClientIp = ssl_test_lib:node_to_hostip(ClientNode, client), ServerMsg = {ok, {ServerIp, Port}}, ClientMsg = {ok, {ClientIp, ClientPort}}, @@ -1975,6 +2056,10 @@ recv_active_once(Config) when is_list(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client). + + + + %%-------------------------------------------------------------------- recv_active_n() -> [{doc,"Test recv on active (n) socket"}]. @@ -2001,6 +2086,7 @@ recv_active_n(Config) when is_list(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client). + %%-------------------------------------------------------------------- %% Test case adapted from gen_tcp_misc_SUITE. active_n() -> @@ -2226,6 +2312,33 @@ upgrade_result(Socket) -> ok end. + +%%-------------------------------------------------------------------- +internal_active_1() -> + [{doc,"Test internal active 1 (behave as internal active once)"}]. + +internal_active_1(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, [{active, true} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = + ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, [{active, true} | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + %%-------------------------------------------------------------------- tls_upgrade_with_timeout() -> [{doc,"Test ssl_accept/3"}]. @@ -3575,7 +3688,7 @@ hibernate(Config) -> ssl_test_lib:check_result(Server, ok, Client, ok), - timer:sleep(1500), + ct:sleep(1500), {current_function, {erlang, hibernate, 3}} = process_info(Pid, current_function), @@ -3611,6 +3724,8 @@ hibernate_right_away(Config) -> [{port, Port1}, {options, [{hibernate_after, 0}|ClientOpts]}]), ssl_test_lib:check_result(Server1, ok, Client1, ok), + + ct:sleep(1000), %% Schedule out {current_function, {erlang, hibernate, 3}} = process_info(Pid1, current_function), @@ -5305,6 +5420,41 @@ tls13_finished_verify_data(_Config) -> FinishedKey = tls_v1:finished_key(BaseKey, sha256), VerifyData = tls_v1:finished_verify_data(FinishedKey, sha256, Messages). + +tls12_ssl_server_tls13_ssl_client() -> + [{doc,"Test basic connection between TLS 1.2 server and TLS 1.3 client"}]. + +tls12_ssl_server_tls13_ssl_client(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + %% Set versions + ServerOpts = [{versions, ['tlsv1.2']}|ServerOpts0], + ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {signature_algs_cert, [ecdsa_secp384r1_sha384, + rsa_pss_rsae_sha256, + rsa_pkcs1_sha256, + {sha256,rsa},{sha256,dsa}]}|ClientOpts0], + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ClientOpts}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close_port(Client). + + tls13_basic_ssl_server_openssl_client() -> [{doc,"Test TLS 1.3 basic connection between ssl server and openssl s_client"}]. @@ -5327,6 +5477,80 @@ tls13_basic_ssl_server_openssl_client(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close_port(Client). +tls13_basic_ssl_server_ssl_client() -> + [{doc,"Test TLS 1.3 basic connection between ssl server and ssl client"}]. + +tls13_basic_ssl_server_ssl_client(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + %% Set versions + ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ServerOpts0], + ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ClientOpts0], + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ClientOpts}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close_port(Client). + + +tls13_basic_openssl_server_ssl_client() -> + [{doc,"Test TLS 1.3 basic connection between openssl server and ssl client"}]. + +tls13_basic_openssl_server_ssl_client(Config) -> + process_flag(trap_exit, true), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), + + ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ClientOpts0], + + {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), + + Data = "From openssl to erlang", + + Port = ssl_test_lib:inet_port(node()), + CertFile = proplists:get_value(certfile, ServerOpts), + CaCertFile = proplists:get_value(cacertfile, ServerOpts), + KeyFile = proplists:get_value(keyfile, ServerOpts), + Exe = "openssl", + Args = ["s_server", "-accept", integer_to_list(Port), + "-tls1_3", + "-cert", CertFile, "-CAfile", CaCertFile, + "-key", KeyFile, "-Verify", "2"], + + OpensslPort = ssl_test_lib:portable_open_port(Exe, Args), + + ssl_test_lib:wait_for_openssl_server(Port, tls), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, + erlang_ssl_receive, [Data]}}, + {options, ClientOpts}]), + true = port_command(OpensslPort, Data), + + ssl_test_lib:check_result(Client, ok), + + %% Clean close down! Server needs to be closed first !! + ssl_test_lib:close_port(OpensslPort), + ssl_test_lib:close(Client), + process_flag(trap_exit, false). + + tls13_custom_groups_ssl_server_openssl_client() -> [{doc,"Test that ssl server can select a common group for key-exchange"}]. @@ -5351,6 +5575,39 @@ tls13_custom_groups_ssl_server_openssl_client(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close_port(Client). + +tls13_custom_groups_ssl_server_ssl_client() -> + [{doc,"Test that ssl server can select a common group for key-exchange"}]. + +tls13_custom_groups_ssl_server_ssl_client(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), + + %% Set versions + ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {supported_groups, [x448, secp256r1, secp384r1]}|ServerOpts0], + ClientOpts1 = [{versions, ['tlsv1.2','tlsv1.3']}|ClientOpts0], + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + ClientOpts = [{supported_groups,[secp384r1, secp256r1, x25519]}|ClientOpts1], + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ClientOpts}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close_port(Client). + + tls13_hello_retry_request_ssl_server_openssl_client() -> [{doc,"Test that ssl server can request a new group when the client's first key share" "is not supported"}]. @@ -5376,6 +5633,38 @@ tls13_hello_retry_request_ssl_server_openssl_client(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close_port(Client). + +tls13_hello_retry_request_ssl_server_ssl_client() -> + [{doc,"Test that ssl server can request a new group when the client's first key share" + "is not supported"}]. + +tls13_hello_retry_request_ssl_server_ssl_client(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), + %% Set versions + ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {supported_groups, [x448, x25519]}|ServerOpts0], + ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {supported_groups, [secp256r1, x25519]}|ClientOpts0], + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ClientOpts}]), + + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close_port(Client). + tls13_client_auth_empty_cert_alert_ssl_server_openssl_client() -> [{doc,"TLS 1.3: Test client authentication when client sends an empty certificate and fail_if_no_peer_cert is set to true."}]. @@ -5400,14 +5689,45 @@ tls13_client_auth_empty_cert_alert_ssl_server_openssl_client(Config) -> Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts), - ssl_test_lib:check_result(Server, - {error, - {tls_alert, - {certificate_required, - "received SERVER ALERT: Fatal - Certificate required - certificate_required"}}}), + ssl_test_lib:check_server_alert(Server, certificate_required), + ssl_test_lib:close(Server), + ssl_test_lib:close_port(Client). + + +tls13_client_auth_empty_cert_alert_ssl_server_ssl_client() -> + [{doc,"TLS 1.3: Test client authentication when client sends an empty certificate and fail_if_no_peer_cert is set to true."}]. + +tls13_client_auth_empty_cert_alert_ssl_server_ssl_client(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config), + %% Delete Client Cert and Key + ClientOpts1 = proplists:delete(certfile, ClientOpts0), + ClientOpts2 = proplists:delete(keyfile, ClientOpts1), + + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), + %% Set versions + ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {verify, verify_peer}, + {fail_if_no_peer_cert, true}|ServerOpts0], + ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ClientOpts2], + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ClientOpts}]), + + ssl_test_lib:check_server_alert(Server, certificate_required), ssl_test_lib:close(Server), ssl_test_lib:close_port(Client). + tls13_client_auth_empty_cert_ssl_server_openssl_client() -> [{doc,"TLS 1.3: Test client authentication when client sends an empty certificate and fail_if_no_peer_cert is set to false."}]. @@ -5437,13 +5757,47 @@ tls13_client_auth_empty_cert_ssl_server_openssl_client(Config) -> ssl_test_lib:close_port(Client). +tls13_client_auth_empty_cert_ssl_server_ssl_client() -> + [{doc,"TLS 1.3: Test client authentication when client sends an empty certificate and fail_if_no_peer_cert is set to false."}]. + +tls13_client_auth_empty_cert_ssl_server_ssl_client(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config), + %% Delete Client Cert and Key + ClientOpts1 = proplists:delete(certfile, ClientOpts0), + ClientOpts2 = proplists:delete(keyfile, ClientOpts1), + + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), + %% Set versions + ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {verify, verify_peer}, + {fail_if_no_peer_cert, false}|ServerOpts0], + ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ClientOpts2], + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ClientOpts}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close_port(Client). + + tls13_client_auth_ssl_server_openssl_client() -> [{doc,"TLS 1.3: Test client authentication."}]. tls13_client_auth_ssl_server_openssl_client(Config) -> ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), - ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), + %% Set versions ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, {verify, verify_peer}, @@ -5463,6 +5817,38 @@ tls13_client_auth_ssl_server_openssl_client(Config) -> ssl_test_lib:close_port(Client). +tls13_client_auth_ssl_server_ssl_client() -> + [{doc,"TLS 1.3: Test client authentication."}]. + +tls13_client_auth_ssl_server_ssl_client(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), + + %% Set versions + ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {verify, verify_peer}, + {fail_if_no_peer_cert, true}|ServerOpts0], + ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ClientOpts0], + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + %%Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ClientOpts}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close_port(Client). + + tls13_hrr_client_auth_empty_cert_alert_ssl_server_openssl_client() -> [{doc,"TLS 1.3 (HelloRetryRequest): Test client authentication when client sends an empty certificate and fail_if_no_peer_cert is set to true."}]. @@ -5489,11 +5875,43 @@ tls13_hrr_client_auth_empty_cert_alert_ssl_server_openssl_client(Config) -> Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts), - ssl_test_lib:check_result(Server, - {error, - {tls_alert, - {certificate_required, - "received SERVER ALERT: Fatal - Certificate required - certificate_required"}}}), + ssl_test_lib:check_server_alert(Server, certificate_required), + ssl_test_lib:close(Server), + ssl_test_lib:close_port(Client). + + +tls13_hrr_client_auth_empty_cert_alert_ssl_server_ssl_client() -> + [{doc,"TLS 1.3 (HelloRetryRequest): Test client authentication when client sends an empty certificate and fail_if_no_peer_cert is set to true."}]. + +tls13_hrr_client_auth_empty_cert_alert_ssl_server_ssl_client(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config), + %% Delete Client Cert and Key + ClientOpts1 = proplists:delete(certfile, ClientOpts0), + ClientOpts2 = proplists:delete(keyfile, ClientOpts1), + + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), + %% Set versions + ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {verify, verify_peer}, + {fail_if_no_peer_cert, true}, + {supported_groups, [x448, x25519]}|ServerOpts0], + ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {supported_groups, [secp256r1, x25519]}|ClientOpts2], + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ClientOpts}]), + + ssl_test_lib:check_server_alert(Server, certificate_required), ssl_test_lib:close(Server), ssl_test_lib:close_port(Client). @@ -5529,6 +5947,42 @@ tls13_hrr_client_auth_empty_cert_ssl_server_openssl_client(Config) -> ssl_test_lib:close_port(Client). +tls13_hrr_client_auth_empty_cert_ssl_server_ssl_client() -> + [{doc,"TLS 1.3 (HelloRetryRequest): Test client authentication when client sends an empty certificate and fail_if_no_peer_cert is set to false."}]. + +tls13_hrr_client_auth_empty_cert_ssl_server_ssl_client(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config), + %% Delete Client Cert and Key + ClientOpts1 = proplists:delete(certfile, ClientOpts0), + ClientOpts2 = proplists:delete(keyfile, ClientOpts1), + + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), + %% Set versions + ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {verify, verify_peer}, + {fail_if_no_peer_cert, false}, + {supported_groups, [x448, x25519]}|ServerOpts0], + ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {supported_groups, [secp256r1, x25519]}|ClientOpts2], + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ClientOpts}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close_port(Client). + + tls13_hrr_client_auth_ssl_server_openssl_client() -> [{doc,"TLS 1.3 (HelloRetryRequest): Test client authentication."}]. @@ -5557,6 +6011,39 @@ tls13_hrr_client_auth_ssl_server_openssl_client(Config) -> ssl_test_lib:close_port(Client). +tls13_hrr_client_auth_ssl_server_ssl_client() -> + [{doc,"TLS 1.3 (HelloRetryRequest): Test client authentication."}]. + +tls13_hrr_client_auth_ssl_server_ssl_client(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config), + + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), + %% Set versions + ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {verify, verify_peer}, + {fail_if_no_peer_cert, true}, + {supported_groups, [x448, x25519]}|ServerOpts0], + ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {supported_groups, [secp256r1, x25519]}|ClientOpts0], + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ClientOpts}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close_port(Client). + + tls13_unsupported_sign_algo_client_auth_ssl_server_openssl_client() -> [{doc,"TLS 1.3: Test client authentication with unsupported signature_algorithm"}]. @@ -5580,20 +6067,48 @@ tls13_unsupported_sign_algo_client_auth_ssl_server_openssl_client(Config) -> Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts), - ssl_test_lib:check_result( - Server, - {error, - {tls_alert, - {insufficient_security, - "received SERVER ALERT: Fatal - Insufficient Security - " - "\"No suitable signature algorithm\""}}}), + ssl_test_lib:check_server_alert(Server, insufficient_security), ssl_test_lib:close(Server), ssl_test_lib:close_port(Client). -%% Triggers Client Alert as openssl s_client does not have a certificate with a +tls13_unsupported_sign_algo_client_auth_ssl_server_ssl_client() -> + [{doc,"TLS 1.3: Test client authentication with unsupported signature_algorithm"}]. + +tls13_unsupported_sign_algo_client_auth_ssl_server_ssl_client(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config), + + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), + %% Set versions + ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {verify, verify_peer}, + %% Skip rsa_pkcs1_sha256! + {signature_algs, [rsa_pkcs1_sha384, rsa_pkcs1_sha512]}, + {fail_if_no_peer_cert, true}|ServerOpts0], + ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ClientOpts0], + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ClientOpts}]), + + ssl_test_lib:check_server_alert(Server, insufficient_security), + ssl_test_lib:close(Server), + ssl_test_lib:close_port(Client). + + +%% Triggers a Server Alert as openssl s_client does not have a certificate with a %% signature algorithm supported by the server (signature_algorithms_cert extension %% of CertificateRequest does not contain the algorithm of the client certificate). +%% openssl s_client sends an empty certificate. tls13_unsupported_sign_algo_cert_client_auth_ssl_server_openssl_client() -> [{doc,"TLS 1.3: Test client authentication with unsupported signature_algorithm_cert"}]. @@ -5619,12 +6134,46 @@ tls13_unsupported_sign_algo_cert_client_auth_ssl_server_openssl_client(Config) - Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts), - ssl_test_lib:check_result( - Server, - {error, - {tls_alert, - {illegal_parameter, - "received CLIENT ALERT: Fatal - Illegal Parameter"}}}), + ssl_test_lib:check_server_alert(Server, certificate_required), + ssl_test_lib:close(Server), + ssl_test_lib:close_port(Client). + + +%% Triggers a Server Alert as ssl client does not have a certificate with a +%% signature algorithm supported by the server (signature_algorithms_cert extension +%% of CertificateRequest does not contain the algorithm of the client certificate). +%% ssl client sends an empty certificate. +tls13_unsupported_sign_algo_cert_client_auth_ssl_server_ssl_client() -> + [{doc,"TLS 1.3: Test client authentication with unsupported signature_algorithm_cert"}]. + +tls13_unsupported_sign_algo_cert_client_auth_ssl_server_ssl_client(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config), + + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), + %% Set versions + ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {log_level, debug}, + {verify, verify_peer}, + {signature_algs, [rsa_pkcs1_sha256, rsa_pkcs1_sha384, rsa_pss_rsae_sha256]}, + %% Skip rsa_pkcs1_sha256! + {signature_algs_cert, [rsa_pkcs1_sha384, rsa_pkcs1_sha512]}, + {fail_if_no_peer_cert, true}|ServerOpts0], + ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ClientOpts0], + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ClientOpts}]), + + ssl_test_lib:check_server_alert(Server, certificate_required), ssl_test_lib:close(Server), ssl_test_lib:close_port(Client). @@ -5652,6 +6201,261 @@ tls13_connection_information(Config) -> ssl_test_lib:close_port(Client). +tls13_ssl_server_with_alpn_ssl_client() -> + [{doc,"Test TLS 1.3 between ssl server with ALPN configured and ssl client"}]. + +tls13_ssl_server_with_alpn_ssl_client(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + %% Set versions + ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {alpn_preferred_protocols, [<<5,6>>, <<1>>]}|ServerOpts0], + ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ClientOpts0], + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ClientOpts}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close_port(Client). + + +tls13_ssl_server_with_alpn_ssl_client_empty_alpn() -> + [{doc,"Test TLS 1.3 between ssl server with ALPN configured and ssl client with empty ALPN"}]. + +tls13_ssl_server_with_alpn_ssl_client_empty_alpn(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + %% Set versions + ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {alpn_preferred_protocols, [<<5,6>>, <<1>>]}|ServerOpts0], + ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {alpn_advertised_protocols, []}|ClientOpts0], + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ClientOpts}]), + + ssl_test_lib:check_server_alert(Server, no_application_protocol), + + ssl_test_lib:close(Server), + ssl_test_lib:close_port(Client). + + +tls13_ssl_server_with_alpn_ssl_client_bad_alpn() -> + [{doc,"Test TLS 1.3 between ssl server with ALPN configured and ssl client with bad ALPN"}]. + +tls13_ssl_server_with_alpn_ssl_client_bad_alpn(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + %% Set versions + ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {alpn_preferred_protocols, [<<5,6>>, <<1>>]}|ServerOpts0], + ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {alpn_advertised_protocols, [<<1,2,3,4>>]}|ClientOpts0], + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ClientOpts}]), + + ssl_test_lib:check_server_alert(Server, no_application_protocol), + + ssl_test_lib:close(Server), + ssl_test_lib:close_port(Client). + +tls13_ssl_server_with_alpn_ssl_client_alpn() -> + [{doc,"Test TLS 1.3 between ssl server with ALPN configured and ssl client with correct ALPN"}]. + +tls13_ssl_server_with_alpn_ssl_client_alpn(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + %% Set versions + ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {alpn_preferred_protocols, [<<5,6>>, <<1>>]}|ServerOpts0], + ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {alpn_advertised_protocols, [<<1,2,3,4>>, <<5,6>>]}|ClientOpts0], + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ClientOpts}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close_port(Client). + + +tls13_ecdsa_ssl_server_openssl_client() -> + [{doc,"Test TLS 1.3 basic connection between ssl server and openssl s_client using ECDSA certificates"}]. + +tls13_ecdsa_ssl_server_openssl_client(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_ecdsa_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_ecdsa_opts, Config), + %% Set versions + ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ServerOpts0], + {_ClientNode, ServerNode, _Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts), + + ssl_test_lib:check_result(Server, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close_port(Client). + +tls13_ecdsa_ssl_server_ssl_client() -> + [{doc,"Test TLS 1.3 basic connection between ssl server and ssl client using ECDSA certificates"}]. + +tls13_ecdsa_ssl_server_ssl_client(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_ecdsa_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_ecdsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + %% Set versions + ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ServerOpts0], + ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ClientOpts0], + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ClientOpts}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close_port(Client). + + +tls13_ecdsa_openssl_server_ssl_client() -> + [{doc,"Test TLS 1.3 basic connection between openssl server and ssl client using ECDSA certificates"}]. + +tls13_ecdsa_openssl_server_ssl_client(Config) -> + process_flag(trap_exit, true), + ServerOpts = ssl_test_lib:ssl_options(server_ecdsa_verify_opts, Config), + ClientOpts0 = ssl_test_lib:ssl_options(client_ecdsa_opts, Config), + + ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ClientOpts0], + + {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), + + Data = "From openssl to erlang", + + Port = ssl_test_lib:inet_port(node()), + CertFile = proplists:get_value(certfile, ServerOpts), + CaCertFile = proplists:get_value(cacertfile, ServerOpts), + KeyFile = proplists:get_value(keyfile, ServerOpts), + Exe = "openssl", + Args = ["s_server", "-accept", integer_to_list(Port), + "-tls1_3", + "-cert", CertFile, "-CAfile", CaCertFile, + "-key", KeyFile, "-Verify", "2"], + + OpensslPort = ssl_test_lib:portable_open_port(Exe, Args), + + ssl_test_lib:wait_for_openssl_server(Port, tls), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, + erlang_ssl_receive, [Data]}}, + {options, ClientOpts}]), + true = port_command(OpensslPort, Data), + + ssl_test_lib:check_result(Client, ok), + + %% Clean close down! Server needs to be closed first !! + ssl_test_lib:close_port(OpensslPort), + ssl_test_lib:close(Client), + process_flag(trap_exit, false). + + +tls13_ecdsa_client_auth_ssl_server_ssl_client() -> + [{doc,"TLS 1.3: Test client authentication with ECDSA certificates."}]. + +tls13_ecdsa_client_auth_ssl_server_ssl_client(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_ecdsa_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_ecdsa_opts, Config), + + %% Set versions + ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {verify, verify_peer}, + {fail_if_no_peer_cert, true}|ServerOpts0], + ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ClientOpts0], + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + %%Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ClientOpts}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close_port(Client). + + + %%-------------------------------------------------------------------- %% Internal functions ------------------------------------------------ %%-------------------------------------------------------------------- diff --git a/lib/ssl/test/ssl_certificate_verify_SUITE.erl b/lib/ssl/test/ssl_certificate_verify_SUITE.erl index 55dee9a48f..4de4a35e59 100644 --- a/lib/ssl/test/ssl_certificate_verify_SUITE.erl +++ b/lib/ssl/test/ssl_certificate_verify_SUITE.erl @@ -40,6 +40,7 @@ %%-------------------------------------------------------------------- all() -> [ + {group, 'tlsv1.3'}, {group, 'tlsv1.2'}, {group, 'tlsv1.1'}, {group, 'tlsv1'}, @@ -50,6 +51,7 @@ all() -> groups() -> [ + {'tlsv1.3', [], all_protocol_groups()}, {'tlsv1.2', [], all_protocol_groups()}, {'tlsv1.1', [], all_protocol_groups()}, {'tlsv1', [], all_protocol_groups()}, @@ -89,7 +91,8 @@ tests() -> critical_extension_verify_server, critical_extension_verify_none, customize_hostname_check, - incomplete_chain + incomplete_chain, + long_chain ]. error_handling_tests()-> @@ -300,7 +303,13 @@ server_require_peer_cert_fail(Config) when is_list(Config) -> {from, self()}, {options, [{active, Active} | BadClientOpts]}]), - ssl_test_lib:check_server_alert(Server, Client, handshake_failure). + Version = proplists:get_value(version,Config), + case Version of + 'tlsv1.3' -> + ssl_test_lib:check_server_alert(Server, Client, certificate_required); + _ -> + ssl_test_lib:check_server_alert(Server, Client, handshake_failure) + end. %%-------------------------------------------------------------------- server_require_peer_cert_empty_ok() -> @@ -853,6 +862,7 @@ invalid_signature_server(Config) when is_list(Config) -> {from, self()}, {options, [{verify, verify_peer} | ClientOpts]}]), ssl_test_lib:check_server_alert(Server, Client, unknown_ca). + %%-------------------------------------------------------------------- invalid_signature_client() -> @@ -1157,6 +1167,44 @@ incomplete_chain(Config) when is_list(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client). +long_chain() -> + [{doc,"Test option verify_peer"}]. +long_chain(Config) when is_list(Config) -> + #{server_config := ServerConf, + client_config := ClientConf} = public_key:pkix_test_data(#{server_chain => #{root => [{key, ssl_test_lib:hardcode_rsa_key(1)}], + intermediates => [[{key, ssl_test_lib:hardcode_rsa_key(2)}], + [{key, ssl_test_lib:hardcode_rsa_key(3)}], + [{key, ssl_test_lib:hardcode_rsa_key(4)}]], + peer => [{key, ssl_test_lib:hardcode_rsa_key(5)}]}, + client_chain => #{root => [{key, ssl_test_lib:hardcode_rsa_key(3)}], + intermediates => [[{key, ssl_test_lib:hardcode_rsa_key(2)}]], + peer => [{key, ssl_test_lib:hardcode_rsa_key(1)}]}}), + [ServerRoot| _] = ServerCas = proplists:get_value(cacerts, ServerConf), + ClientCas = proplists:get_value(cacerts, ClientConf), + + Active = proplists:get_value(active, Config), + ReceiveFunction = proplists:get_value(receive_function, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, ReceiveFunction, []}}, + {options, [{active, Active}, {verify, verify_peer}, + {cacerts, [ServerRoot]} | + proplists:delete(cacerts, ServerConf)]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, ReceiveFunction, []}}, + {options, [{active, Active}, + {verify, verify_peer}, + {depth, 5}, + {cacerts, ServerCas ++ ClientCas} | + proplists:delete(cacerts, ClientConf)]}]), + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + %%-------------------------------------------------------------------- %% Internal functions ------------------------------------------------ diff --git a/lib/ssl/test/ssl_payload_SUITE.erl b/lib/ssl/test/ssl_payload_SUITE.erl index 27b9c258a0..2d0ffd03d7 100644 --- a/lib/ssl/test/ssl_payload_SUITE.erl +++ b/lib/ssl/test/ssl_payload_SUITE.erl @@ -48,21 +48,27 @@ groups() -> payload_tests() -> [server_echos_passive_small, + server_echos_passive_chunk_small, server_echos_active_once_small, server_echos_active_small, client_echos_passive_small, + client_echos_passive_chunk_small, client_echos_active_once_small, client_echos_active_small, server_echos_passive_big, + server_echos_passive_chunk_big, server_echos_active_once_big, server_echos_active_big, client_echos_passive_big, + client_echos_passive_chunk_big, client_echos_active_once_big, client_echos_active_big, server_echos_passive_huge, + server_echos_passive_chunk_huge, server_echos_active_once_huge, server_echos_active_huge, client_echos_passive_huge, + client_echos_passive_chunk_huge, client_echos_active_once_huge, client_echos_active_huge, client_active_once_server_close]. @@ -109,9 +115,11 @@ end_per_group(GroupName, Config) -> init_per_testcase(TestCase, Config) when TestCase == server_echos_passive_huge; + TestCase == server_echos_passive_chunk_huge; TestCase == server_echos_active_once_huge; TestCase == server_echos_active_huge; TestCase == client_echos_passive_huge; + TestCase == client_echos_passive_chunk_huge; TestCase == client_echos_active_once_huge; TestCase == client_echos_active_huge -> case erlang:system_info(system_architecture) of @@ -124,9 +132,11 @@ init_per_testcase(TestCase, Config) init_per_testcase(TestCase, Config) when TestCase == server_echos_passive_big; + TestCase == server_echos_passive_chunk_big; TestCase == server_echos_active_once_big; TestCase == server_echos_active_big; TestCase == client_echos_passive_big; + TestCase == client_echos_passive_chunk_big; TestCase == client_echos_active_once_big; TestCase == client_echos_active_big -> ct:timetrap({seconds, 60}), @@ -157,6 +167,22 @@ server_echos_passive_small(Config) when is_list(Config) -> %%-------------------------------------------------------------------- +server_echos_passive_chunk_small() -> + [{doc, "Client sends 1000 bytes in passive mode to server, that receives them in chunks, " + "sends them back, and closes."}]. + +server_echos_passive_chunk_small(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + %% + Data = binary:copy(<<"1234567890">>, 100), + server_echos_passive_chunk( + Data, ClientOpts, ServerOpts, ClientNode, ServerNode, Hostname). + + +%%-------------------------------------------------------------------- + server_echos_active_once_small() -> [{doc, "Client sends 1000 bytes in active once mode to server, that receives " " them, sends them back, and closes."}]. @@ -200,6 +226,21 @@ client_echos_passive_small(Config) when is_list(Config) -> Data, ClientOpts, ServerOpts, ClientNode, ServerNode, Hostname). %%-------------------------------------------------------------------- +client_echos_passive_chunk__small() -> + [{doc, "Server sends 1000 bytes in passive mode to client, that receives them in chunks, " + "sends them back, and closes."}]. + +client_echos_passive_chunk_small(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + %% + Data = binary:copy(<<"1234567890">>, 100), + client_echos_passive_chunk( + Data, ClientOpts, ServerOpts, ClientNode, ServerNode, Hostname). + + +%%-------------------------------------------------------------------- client_echos_active_once_small() -> ["Server sends 1000 bytes in active once mode to client, that receives " "them, sends them back, and closes."]. @@ -241,6 +282,19 @@ server_echos_passive_big(Config) when is_list(Config) -> Data = binary:copy(<<"1234567890">>, 5000), server_echos_passive( Data, ClientOpts, ServerOpts, ClientNode, ServerNode, Hostname). +%%-------------------------------------------------------------------- +server_echos_passive_chunk_big() -> + [{doc, "Client sends 50000 bytes to server in passive mode, that receives them, " + "sends them back, and closes."}]. + +server_echos_passive_chunk_big(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + %% + Data = binary:copy(<<"1234567890">>, 5000), + server_echos_passive_chunk( + Data, ClientOpts, ServerOpts, ClientNode, ServerNode, Hostname). %%-------------------------------------------------------------------- @@ -286,6 +340,22 @@ client_echos_passive_big(Config) when is_list(Config) -> client_echos_passive( Data, ClientOpts, ServerOpts, ClientNode, ServerNode, Hostname). + +%%-------------------------------------------------------------------- +client_echos_passive_chunk_big() -> + [{doc, "Server sends 50000 bytes to client in passive mode, that receives them, " + "sends them back, and closes."}]. + +client_echos_passive_chunk_big(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + %% + Data = binary:copy(<<"1234567890">>, 5000), + client_echos_passive_chunk( + Data, ClientOpts, ServerOpts, ClientNode, ServerNode, Hostname). + + %%-------------------------------------------------------------------- client_echos_active_once_big() -> [{doc, "Server sends 50000 bytes to client in active once mode, that receives" @@ -329,6 +399,20 @@ server_echos_passive_huge(Config) when is_list(Config) -> Data, ClientOpts, ServerOpts, ClientNode, ServerNode, Hostname). %%-------------------------------------------------------------------- +server_echos_passive_chunk_huge() -> + [{doc, "Client sends 500000 bytes to server in passive mode, that receives " + " them, sends them back, and closes."}]. + +server_echos_passive_chunk_huge(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + %% + Data = binary:copy(<<"1234567890">>, 50000), + server_echos_passive_chunk( + Data, ClientOpts, ServerOpts, ClientNode, ServerNode, Hostname). + +%%-------------------------------------------------------------------- server_echos_active_once_huge() -> [{doc, "Client sends 500000 bytes to server in active once mode, that receives " "them, sends them back, and closes."}]. @@ -369,7 +453,19 @@ client_echos_passive_huge(Config) when is_list(Config) -> Data = binary:copy(<<"1234567890">>, 50000), client_echos_passive( Data, ClientOpts, ServerOpts, ClientNode, ServerNode, Hostname). +%%-------------------------------------------------------------------- +client_echos_passive_chunk_huge() -> + [{doc, "Server sends 500000 bytes to client in passive mode, that receives " + "them, sends them back, and closes."}]. +client_echos_passive_chunk_huge(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + %% + Data = binary:copy(<<"1234567890">>, 50000), + client_echos_passive_chunk( + Data, ClientOpts, ServerOpts, ClientNode, ServerNode, Hostname). %%-------------------------------------------------------------------- client_echos_active_once_huge() -> [{doc, "Server sends 500000 bytes to client in active once mode, that receives " @@ -442,6 +538,28 @@ server_echos_passive( ssl_test_lib:close(Server), ssl_test_lib:close(Client). +server_echos_passive_chunk( + Data, ClientOpts, ServerOpts, ClientNode, ServerNode, Hostname) -> + Length = byte_size(Data), + Server = + ssl_test_lib:start_server( + [{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, echoer_chunk, [Length]}}, + {options, [{active, false}, {mode, binary} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = + ssl_test_lib:start_client( + [{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, sender, [Data]}}, + {options, [{active, false}, {mode, binary} | ClientOpts]}]), + %% + ssl_test_lib:check_result(Server, ok, Client, ok), + %% + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). server_echos_active_once( Data, ClientOpts, ServerOpts, ClientNode, ServerNode, Hostname) -> @@ -513,6 +631,31 @@ client_echos_passive( ssl_test_lib:close(Server), ssl_test_lib:close(Client). + +client_echos_passive_chunk( + Data, ClientOpts, ServerOpts, ClientNode, ServerNode, Hostname) -> + Length = byte_size(Data), + Server = + ssl_test_lib:start_server( + [{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, sender, [Data]}}, + {options, [{active, false}, {mode, binary} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = + ssl_test_lib:start_client( + [{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, echoer_chunk, [Length]}}, + {options, [{active, false}, {mode, binary} | ClientOpts]}]), + %% + ssl_test_lib:check_result(Server, ok, Client, ok), + %% + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + + client_echos_active_once( Data, ClientOpts, ServerOpts, ClientNode, ServerNode, Hostname) -> Length = byte_size(Data), @@ -615,6 +758,10 @@ echoer(Socket, Size) -> ct:log("Echoer recv: ~p~n", [ssl:getopts(Socket, [active])]), echo_recv(Socket, Size * 100). +echoer_chunk(Socket, Size) -> + ct:log("Echoer recv: ~p~n", [ssl:getopts(Socket, [active])]), + echo_recv_chunk(Socket, Size, Size * 100). + echoer_active_once(Socket, Size) -> ct:log("Echoer active once: ~p~n", [ssl:getopts(Socket, [active])]), echo_active_once(Socket, Size * 100). @@ -632,6 +779,16 @@ echo_recv(Socket, Size) -> ok = ssl:send(Socket, Data), echo_recv(Socket, Size - byte_size(Data)). + +%% Receive Size bytes +echo_recv_chunk(_Socket, _, 0) -> + ok; +echo_recv_chunk(Socket, ChunkSize, Size) -> + {ok, Data} = ssl:recv(Socket, ChunkSize), + ok = ssl:send(Socket, Data), + echo_recv_chunk(Socket, ChunkSize, Size - ChunkSize). + + %% Receive Size bytes echo_active_once(_Socket, 0) -> ok; diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl index 3b161a0c8a..c791f438d0 100644 --- a/lib/ssl/test/ssl_test_lib.erl +++ b/lib/ssl/test/ssl_test_lib.erl @@ -45,9 +45,18 @@ run_where(_, ipv6) -> Host = rpc:call(ServerNode, net_adm, localhost, []), {ClientNode, ServerNode, Host}. -node_to_hostip(Node) -> +node_to_hostip(Node, Role) -> [_ , Host] = string:tokens(atom_to_list(Node), "@"), {ok, Address} = inet:getaddr(Host, inet), + %% Convert client addresses in 127.0.0.0/24 subnet to the atom 'localhost'. + %% This is a workaround for testcase problems caused by the fact that + %% inet:peername/1 and inet:getaddr/2 return different addresses when + %% running on localhost. + normalize_loopback(Address, Role). + +normalize_loopback({127,_,_,_}, client) -> + localhost; +normalize_loopback(Address, _) -> Address. start_server(Args) -> @@ -159,6 +168,7 @@ connect(ListenSocket, Node, N, _, Timeout, SslOpts, [_|_] =ContOpts) -> case ssl:handshake(AcceptSocket, SslOpts, Timeout) of {ok, Socket0, Ext} -> + [_|_] = maps:get(sni, Ext), ct:log("Ext ~p:~n", [Ext]), ct:log("~p:~p~nssl:handshake_continue(~p,~p,~p)~n", [?MODULE,?LINE, Socket0, ContOpts,Timeout]), case ssl:handshake_continue(Socket0, ContOpts, Timeout) of @@ -392,14 +402,16 @@ close(Pid, Timeout) -> exit(Pid, kill) end. -check_result(Server, ServerMsg, Client, ClientMsg) -> +check_result(Server, ServerMsg, Client, ClientMsg) -> + {ClientIP, ClientPort} = get_ip_port(ServerMsg), receive {Server, ServerMsg} -> check_result(Client, ClientMsg); - + %% Workaround to accept local addresses (127.0.0.0/24) + {Server, {ok, {{127,_,_,_}, ClientPort}}} when ClientIP =:= localhost -> + check_result(Client, ClientMsg); {Client, ClientMsg} -> check_result(Server, ServerMsg); - {Port, {data,Debug}} when is_port(Port) -> ct:log("~p:~p~n Openssl ~s~n",[?MODULE,?LINE, Debug]), check_result(Server, ServerMsg, Client, ClientMsg); @@ -412,10 +424,14 @@ check_result(Server, ServerMsg, Client, ClientMsg) -> ct:fail(Reason) end. -check_result(Pid, Msg) -> +check_result(Pid, Msg) -> + {ClientIP, ClientPort} = get_ip_port(Msg), receive {Pid, Msg} -> ok; + %% Workaround to accept local addresses (127.0.0.0/24) + {Pid, {ok, {{127,_,_,_}, ClientPort}}} when ClientIP =:= localhost -> + ok; {Port, {data,Debug}} when is_port(Port) -> ct:log("~p:~p~n Openssl ~s~n",[?MODULE,?LINE, Debug]), check_result(Pid,Msg); @@ -427,37 +443,63 @@ check_result(Pid, Msg) -> {got, Unexpected}}, ct:fail(Reason) end. + + +get_ip_port({ok,{ClientIP, ClientPort}}) -> + {ClientIP, ClientPort}; +get_ip_port(_) -> + {undefined, undefined}. + + check_server_alert(Pid, Alert) -> receive - {Pid, {error, {tls_alert, {Alert, _}}}} -> + {Pid, {error, {tls_alert, {Alert, STxt}}}} -> + check_server_txt(STxt), + ok; + {Pid, {error, closed}} -> ok end. check_server_alert(Server, Client, Alert) -> receive - {Server, {error, {tls_alert, {Alert, _}}}} -> - receive - {Client, {error, {tls_alert, {Alert, _}}}} -> - ok; - {Client, {error, closed}} -> - ok - end + {Server, {error, {tls_alert, {Alert, STxt}}}} -> + check_server_txt(STxt), + check_client_alert(Client, Alert) end. check_client_alert(Pid, Alert) -> receive - {Pid, {error, {tls_alert, {Alert, _}}}} -> + {Pid, {error, {tls_alert, {Alert, CTxt}}}} -> + check_client_txt(CTxt), + ok; + {Pid, {ssl_error, _, {tls_alert, {Alert, CTxt}}}} -> + check_client_txt(CTxt), + ok; + {Pid, {error, closed}} -> ok end. check_client_alert(Server, Client, Alert) -> receive - {Client, {error, {tls_alert, {Alert, _}}}} -> - receive - {Server, {error, {tls_alert, {Alert, _}}}} -> - ok; - {Server, {error, closed}} -> - ok - end + {Client, {error, {tls_alert, {Alert, CTxt}}}} -> + check_client_txt(CTxt), + check_server_alert(Server, Alert); + {Client, {ssl_error, _, {tls_alert, {Alert, CTxt}}}} -> + check_client_txt(CTxt), + ok; + {Client, {error, closed}} -> + ok end. +check_server_txt("TLS server" ++ _) -> + ok; +check_server_txt("DTLS server" ++ _) -> + ok; +check_server_txt(Txt) -> + ct:fail({expected_server, {got, Txt}}). +check_client_txt("TLS client" ++ _) -> + ok; +check_client_txt("DTLS client" ++ _) -> + ok; +check_client_txt(Txt) -> + ct:fail({expected_server, {got, Txt}}). wait_for_result(Server, ServerMsg, Client, ClientMsg) -> receive @@ -1084,7 +1126,15 @@ run_client_error(Opts) -> Options = proplists:get_value(options, Opts), ct:log("~p:~p~nssl:connect(~p, ~p, ~p)~n", [?MODULE,?LINE, Host, Port, Options]), Error = Transport:connect(Host, Port, Options), - Pid ! {self(), Error}. + case Error of + {error, _} -> + Pid ! {self(), Error}; + {ok, _Socket} -> + receive + {ssl_error, _, {tls_alert, _}} = SslError -> + Pid ! {self(), SslError} + end + end. accepters(N) -> accepters([], N). @@ -1623,6 +1673,8 @@ is_tls_version('dtlsv1.2') -> true; is_tls_version('dtlsv1') -> true; +is_tls_version('tlsv1.3') -> + true; is_tls_version('tlsv1.2') -> true; is_tls_version('tlsv1.1') -> @@ -2149,7 +2201,8 @@ clean_env() -> application:unset_env(ssl, session_cache_server_max), application:unset_env(ssl, ssl_pem_cache_clean), application:unset_env(ssl, bypass_pem_cache), - application:unset_env(ssl, alert_timeout). + application:unset_env(ssl, alert_timeout), + application:unset_env(ssl, internal_active_n). clean_start() -> ssl:stop(), @@ -2443,3 +2496,11 @@ digest() -> _ -> {digest, sha1} end. + +kill_openssl() -> + case os:type() of + {unix, _} -> + os:cmd("pkill openssl"); + {win32, _} -> + os:cmd("cmd.exe /C \"taskkill /IM openssl.exe /F\"") + end. diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl index 07abddbcf7..31d9af5298 100644 --- a/lib/ssl/test/ssl_to_openssl_SUITE.erl +++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl @@ -179,8 +179,8 @@ init_per_suite(Config0) -> end_per_suite(_Config) -> ssl:stop(), - application:stop(crypto). - + application:stop(crypto), + ssl_test_lib:kill_openssl(). init_per_group(GroupName, Config) -> case ssl_test_lib:is_tls_version(GroupName) of diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk index 3c66ffd852..df38aea017 100644 --- a/lib/ssl/vsn.mk +++ b/lib/ssl/vsn.mk @@ -1 +1 @@ -SSL_VSN = 9.3 +SSL_VSN = 9.3.4 diff --git a/lib/stdlib/doc/src/binary.xml b/lib/stdlib/doc/src/binary.xml index f3d4edd30f..fd991f258b 100644 --- a/lib/stdlib/doc/src/binary.xml +++ b/lib/stdlib/doc/src/binary.xml @@ -505,15 +505,16 @@ store(Binary, GBSet) -> <<1,1,1,1,1 ... 2> byte_size(A). 100 -3> binary:referenced_byte_size(A) +3> binary:referenced_byte_size(A). 100 -4> <<_:10/binary,B:10/binary,_/binary>> = A. +4> <<B:10/binary, C:90/binary>> = A. <<1,1,1,1,1 ... -5> byte_size(B). -10 -6> binary:referenced_byte_size(B) -100</code> - +5> {byte_size(B), binary:referenced_byte_size(B)}. +{10,10} +6> {byte_size(C), binary:referenced_byte_size(C)}. +{90,100}</code> + <p>In the above example, the small binary <c>B</c> was copied while the + larger binary <c>C</c> references binary <c>A</c>.</p> <note> <p>Binary data is shared among processes. If another process still references the larger binary, copying the part this diff --git a/lib/stdlib/doc/src/gen_statem.xml b/lib/stdlib/doc/src/gen_statem.xml index 6f6849a19d..ef548ad643 100644 --- a/lib/stdlib/doc/src/gen_statem.xml +++ b/lib/stdlib/doc/src/gen_statem.xml @@ -40,7 +40,7 @@ <p> This reference manual describes types generated from the types in the <c>gen_statem</c> source code, so they are correct. - However, the generated descriptions also reflect the type hiearchy, + However, the generated descriptions also reflect the type hierarchy, which makes them kind of hard to read. </p> <p> diff --git a/lib/stdlib/doc/src/notes.xml b/lib/stdlib/doc/src/notes.xml index 605a9f224d..66624c43be 100644 --- a/lib/stdlib/doc/src/notes.xml +++ b/lib/stdlib/doc/src/notes.xml @@ -31,6 +31,40 @@ </header> <p>This document describes the changes made to the STDLIB application.</p> +<section><title>STDLIB 3.9.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> Fix a bug that could cause a loop when formatting + terms using the control sequences <c>p</c> or <c>P</c> + and limiting the output with the option + <c>chars_limit</c>. </p> + <p> + Own Id: OTP-15875 Aux Id: ERL-967 </p> + </item> + </list> + </section> + +</section> + +<section><title>STDLIB 3.9.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> Fix a bug that could cause a failure when formatting + binaries using the control sequences <c>p</c> or <c>P</c> + and limiting the output with the option + <c>chars_limit</c>. </p> + <p> + Own Id: OTP-15847 Aux Id: ERL-957 </p> + </item> + </list> + </section> + +</section> + <section><title>STDLIB 3.9</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -272,6 +306,40 @@ </section> +<section><title>STDLIB 3.8.2.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> Fix a bug that could cause a loop when formatting + terms using the control sequences <c>p</c> or <c>P</c> + and limiting the output with the option + <c>chars_limit</c>. </p> + <p> + Own Id: OTP-15875 Aux Id: ERL-967 </p> + </item> + </list> + </section> + +</section> + +<section><title>STDLIB 3.8.2.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> Fix a bug that could cause a failure when formatting + binaries using the control sequences <c>p</c> or <c>P</c> + and limiting the output with the option + <c>chars_limit</c>. </p> + <p> + Own Id: OTP-15847 Aux Id: ERL-957 </p> + </item> + </list> + </section> + +</section> + <section><title>STDLIB 3.8.2</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/stdlib/src/io_lib_pretty.erl b/lib/stdlib/src/io_lib_pretty.erl index b1a5991bf0..77f02eafe0 100644 --- a/lib/stdlib/src/io_lib_pretty.erl +++ b/lib/stdlib/src/io_lib_pretty.erl @@ -462,7 +462,9 @@ find_upper(Lower, Term, T, Dl, Dd, D, RF, Enc, Str) -> case If of {_, _, _Dots=0, _} -> % even if Len > T If; - {_, Len, _, _} when Len =< T, D1 < D orelse D < 0 -> + {_, _Len=T, _, _} -> % increasing the depth is meaningless + If; + {_, Len, _, _} when Len < T, D1 < D orelse D < 0 -> find_upper(If, Term, T, D1, Dd2, D, RF, Enc, Str); _ -> search_depth(Lower, If, Term, T, Dl, D1, RF, Enc, Str) @@ -780,6 +782,8 @@ printable_bin0(Bin, D, T, Enc) -> end, printable_bin(Bin, Len, D, Enc). +printable_bin(_Bin, 0, _D, _Enc) -> + false; printable_bin(Bin, Len, D, latin1) -> N = erlang:min(20, Len), L = binary_to_list(Bin, 1, N), diff --git a/lib/stdlib/src/ordsets.erl b/lib/stdlib/src/ordsets.erl index 176047079b..95b83ded8c 100644 --- a/lib/stdlib/src/ordsets.erl +++ b/lib/stdlib/src/ordsets.erl @@ -150,13 +150,8 @@ union(Es1, []) -> Es1. OrdsetList :: [ordset(T)], Ordset :: ordset(T). -union([S1,S2|Ss]) -> - union1(union(S1, S2), Ss); -union([S]) -> S; -union([]) -> []. - -union1(S1, [S2|Ss]) -> union1(union(S1, S2), Ss); -union1(S1, []) -> S1. +union(OrdsetList) -> + lists:umerge(OrdsetList). %% intersection(OrdSet1, OrdSet2) -> OrdSet. %% Return the intersection of OrdSet1 and OrdSet2. diff --git a/lib/stdlib/src/re.erl b/lib/stdlib/src/re.erl index 726b409d4d..197564b895 100644 --- a/lib/stdlib/src/re.erl +++ b/lib/stdlib/src/re.erl @@ -33,6 +33,8 @@ %%% BIFs +-export([internal_run/4]). + -export([version/0, compile/1, compile/2, run/2, run/3, inspect/2]). -spec version() -> binary(). @@ -100,6 +102,40 @@ run(_, _) -> run(_, _, _) -> erlang:nif_error(undef). +-spec internal_run(Subject, RE, Options, FirstCall) -> {match, Captured} | + match | + nomatch | + {error, ErrType} when + Subject :: iodata() | unicode:charlist(), + RE :: mp() | iodata() | unicode:charlist(), + Options :: [Option], + Option :: anchored | global | notbol | noteol | notempty + | notempty_atstart | report_errors + | {offset, non_neg_integer()} | + {match_limit, non_neg_integer()} | + {match_limit_recursion, non_neg_integer()} | + {newline, NLSpec :: nl_spec()} | + bsr_anycrlf | bsr_unicode | {capture, ValueSpec} | + {capture, ValueSpec, Type} | CompileOpt, + Type :: index | list | binary, + ValueSpec :: all | all_but_first | all_names | first | none | ValueList, + ValueList :: [ValueID], + ValueID :: integer() | string() | atom(), + CompileOpt :: compile_option(), + Captured :: [CaptureData] | [[CaptureData]], + CaptureData :: {integer(), integer()} + | ListConversionData + | binary(), + ListConversionData :: string() + | {error, string(), binary()} + | {incomplete, string(), binary()}, + ErrType :: match_limit | match_limit_recursion | {compile, CompileErr}, + CompileErr :: {ErrString :: string(), Position :: non_neg_integer()}, + FirstCall :: boolean(). + +internal_run(_, _, _, _) -> + erlang:nif_error(undef). + -spec inspect(MP,Item) -> {namelist, [ binary() ]} when MP :: mp(), Item :: namelist. @@ -765,17 +801,17 @@ do_grun(FlatSubject,Subject,Unicode,CRLF,RE,{Options0,NeedClean}) -> try postprocess(loopexec(FlatSubject,RE,InitialOffset, byte_size(FlatSubject), - Unicode,CRLF,StrippedOptions), + Unicode,CRLF,StrippedOptions,true), SelectReturn,ConvertReturn,FlatSubject,Unicode) catch throw:ErrTuple -> ErrTuple end. -loopexec(_,_,X,Y,_,_,_) when X > Y -> +loopexec(_,_,X,Y,_,_,_,_) when X > Y -> {match,[]}; -loopexec(Subject,RE,X,Y,Unicode,CRLF,Options) -> - case re:run(Subject,RE,[{offset,X}]++Options) of +loopexec(Subject,RE,X,Y,Unicode,CRLF,Options, First) -> + case re:internal_run(Subject,RE,[{offset,X}]++Options,First) of {error, Err} -> throw({error,Err}); nomatch -> @@ -784,11 +820,11 @@ loopexec(Subject,RE,X,Y,Unicode,CRLF,Options) -> {match,Rest} = case B>0 of true -> - loopexec(Subject,RE,A+B,Y,Unicode,CRLF,Options); + loopexec(Subject,RE,A+B,Y,Unicode,CRLF,Options,false); false -> {match,M} = - case re:run(Subject,RE,[{offset,X},notempty_atstart, - anchored]++Options) of + case re:internal_run(Subject,RE,[{offset,X},notempty_atstart, + anchored]++Options,false) of nomatch -> {match,[]}; {match,Other} -> @@ -801,7 +837,7 @@ loopexec(Subject,RE,X,Y,Unicode,CRLF,Options) -> forward(Subject,A,1,Unicode,CRLF) end, {match,MM} = loopexec(Subject,RE,NewA,Y, - Unicode,CRLF,Options), + Unicode,CRLF,Options,false), case M of [] -> {match,MM}; diff --git a/lib/stdlib/src/stdlib.app.src b/lib/stdlib/src/stdlib.app.src index ecb514e9f3..e6c42b9aac 100644 --- a/lib/stdlib/src/stdlib.app.src +++ b/lib/stdlib/src/stdlib.app.src @@ -108,7 +108,7 @@ dets]}, {applications, [kernel]}, {env, []}, - {runtime_dependencies, ["sasl-3.0","kernel-6.0","erts-10.4","crypto-3.3", + {runtime_dependencies, ["sasl-3.0","kernel-6.0","erts-@OTP-15831:OTP-15836:OTP-15889@","crypto-3.3", "compiler-5.0"]} ]}. diff --git a/lib/stdlib/src/stdlib.appup.src b/lib/stdlib/src/stdlib.appup.src index 7038cc159c..0c270e9dd5 100644 --- a/lib/stdlib/src/stdlib.appup.src +++ b/lib/stdlib/src/stdlib.appup.src @@ -38,7 +38,10 @@ {<<"^3\\.8$">>,[restart_new_emulator]}, {<<"^3\\.8\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, {<<"^3\\.8\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, - {<<"^3\\.8\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]}], + {<<"^3\\.8\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^3\\.9$">>,[restart_new_emulator]}, + {<<"^3\\.9\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, + {<<"^3\\.9\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}], [{<<"^3\\.5$">>,[restart_new_emulator]}, {<<"^3\\.5\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, {<<"^3\\.5\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, @@ -50,4 +53,7 @@ {<<"^3\\.8$">>,[restart_new_emulator]}, {<<"^3\\.8\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, {<<"^3\\.8\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, - {<<"^3\\.8\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]}]}. + {<<"^3\\.8\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]}, + {<<"^3\\.9$">>,[restart_new_emulator]}, + {<<"^3\\.9\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}, + {<<"^3\\.9\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}]}. diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl index dd49288417..09238ae2b4 100644 --- a/lib/stdlib/test/ets_SUITE.erl +++ b/lib/stdlib/test/ets_SUITE.erl @@ -75,7 +75,8 @@ -export([throughput_benchmark/0, throughput_benchmark/1, test_throughput_benchmark/1, - long_throughput_benchmark/1]). + long_throughput_benchmark/1, + lookup_catree_par_vs_seq_init_benchmark/0]). -export([exit_large_table_owner/1, exit_many_large_table_owner/1, exit_many_tables_owner/1, @@ -6728,6 +6729,14 @@ do_work(WorksDoneSoFar, Table, ProbHelpTab, Range, Operations) -> end. prefill_table(T, KeyRange, Num, ObjFun) -> + Parent = self(), + spawn_link(fun() -> + prefill_table_helper(T, KeyRange, Num, ObjFun), + Parent ! done + end), + receive done -> ok end. + +prefill_table_helper(T, KeyRange, Num, ObjFun) -> Seed = rand:uniform(KeyRange), %%io:format("prefill_table: Seed = ~p\n", [Seed]), RState = unique_rand_start(KeyRange, Seed), @@ -6740,11 +6749,77 @@ prefill_table_loop(T, RS0, N, ObjFun) -> ets:insert(T, ObjFun(Key)), prefill_table_loop(T, RS1, N-1, ObjFun). +inserter_proc_starter(T, ToInsert, Parent) -> + receive + start -> ok + end, + inserter_proc(T, ToInsert, [], Parent, false). + +inserter_proc(T, [], Inserted, Parent, _) -> + inserter_proc(T, Inserted, [], Parent, true); +inserter_proc(T, [I | ToInsert], Inserted, Parent, CanStop) -> + Stop = + case CanStop of + true -> + receive + stop -> Parent ! stopped + after 0 -> no_stop + end; + false -> no_stop + end, + case Stop of + no_stop -> + ets:insert(T, I), + inserter_proc(T, ToInsert, [I | Inserted], Parent, CanStop); + _ -> ok + end. + +prefill_table_parallel(T, KeyRange, Num, ObjFun) -> + Parent = self(), + spawn_link(fun() -> + prefill_table_parallel_helper(T, KeyRange, Num, ObjFun), + Parent ! done + end), + receive done -> ok end. + +prefill_table_parallel_helper(T, KeyRange, Num, ObjFun) -> + NrOfSchedulers = erlang:system_info(schedulers), + Seed = rand:uniform(KeyRange), + %%io:format("prefill_table: Seed = ~p\n", [Seed]), + RState = unique_rand_start(KeyRange, Seed), + InsertMap = prefill_insert_map_loop(T, RState, Num, ObjFun, #{}, NrOfSchedulers), + Self = self(), + Pids = [ + begin + InserterFun = + fun() -> + inserter_proc_starter(T, ToInsert, Self) + end, + spawn_link(InserterFun) + end + || ToInsert <- maps:values(InsertMap)], + [Pid ! start || Pid <- Pids], + timer:sleep(1000), + [Pid ! stop || Pid <- Pids], + [receive stopped -> ok end || _Pid <- Pids]. + +prefill_insert_map_loop(_, _, 0, _, InsertMap, _NrOfSchedulers) -> + InsertMap; +prefill_insert_map_loop(T, RS0, N, ObjFun, InsertMap, NrOfSchedulers) -> + {Key, RS1} = unique_rand_next(RS0), + Sched = N rem NrOfSchedulers, + PrevInserts = maps:get(Sched, InsertMap, []), + NewPrevInserts = [ObjFun(Key) | PrevInserts], + NewInsertMap = maps:put(Sched, NewPrevInserts, InsertMap), + prefill_insert_map_loop(T, RS1, N-1, ObjFun, NewInsertMap, NrOfSchedulers). + -record(ets_throughput_bench_config, {benchmark_duration_ms = 3000, recover_time_ms = 1000, thread_counts = not_set, key_ranges = [1000000], + init_functions = [fun prefill_table/4], + nr_of_repeats = 1, scenarios = [ [ @@ -6838,7 +6913,7 @@ prefill_table_loop(T, RS0, N, ObjFun) -> notify_res_fun = fun(_Name, _Throughput) -> ok end, print_result_paths_fun = fun(ResultPath, _LatestResultPath) -> - Comment = + Comment = io_lib:format("<a href=\"file:///~s\">Result visualization</a>",[ResultPath]), {comment, Comment} end @@ -6848,7 +6923,7 @@ stdout_notify_res(ResultPath, LatestResultPath) -> io:format("Result Location: /~s~n", [ResultPath]), io:format("Latest Result Location: ~s~n", [LatestResultPath]). -throughput_benchmark() -> +throughput_benchmark() -> throughput_benchmark( #ets_throughput_bench_config{ print_result_paths_fun = fun stdout_notify_res/2}). @@ -6856,9 +6931,11 @@ throughput_benchmark() -> throughput_benchmark( #ets_throughput_bench_config{ benchmark_duration_ms = BenchmarkDurationMs, - recover_time_ms = RecoverTimeMs, - thread_counts = ThreadCountsOpt, - key_ranges = KeyRanges, + recover_time_ms = RecoverTimeMs, + thread_counts = ThreadCountsOpt, + key_ranges = KeyRanges, + init_functions = InitFuns, + nr_of_repeats = NrOfRepeats, scenarios = Scenarios, table_types = TableTypes, etsmem_fun = ETSMemFun, @@ -6872,21 +6949,21 @@ throughput_benchmark( Start = rand:uniform(KeyRange), Last = lists:foldl( - fun(_, Prev) -> + fun(_, Prev) -> case Prev of '$end_of_table'-> ok; _ -> try ets:next(T, Prev) of Normal -> Normal catch - error:badarg -> + error:badarg -> % sets (not ordered_sets) cannot handle when the argument % to next is not in the set rand:uniform(KeyRange) end end end, - Start, + Start, lists:seq(1, SeqSize)), case Last =:= -1 of true -> io:format("Will never be printed"); @@ -6898,26 +6975,26 @@ throughput_benchmark( Start = rand:uniform(KeyRange), Last = Start + SeqSize, case -1 =:= ets:select_count(T, - ets:fun2ms(fun({X}) when X > Start andalso X =< Last -> true end)) of + ets:fun2ms(fun({X}) when X > Start andalso X =< Last -> true end)) of true -> io:format("Will never be printed"); false -> ok end end, %% Mapping benchmark operation names to their corresponding functions that do them - Operations = + Operations = #{insert => - fun(T,KeyRange) -> + fun(T,KeyRange) -> Num = rand:uniform(KeyRange), ets:insert(T, {Num}) end, delete => - fun(T,KeyRange) -> + fun(T,KeyRange) -> Num = rand:uniform(KeyRange), ets:delete(T, Num) end, lookup => - fun(T,KeyRange) -> + fun(T,KeyRange) -> Num = rand:uniform(KeyRange), ets:lookup(T, Num) end, @@ -6928,8 +7005,8 @@ throughput_benchmark( nextseq1000 => fun(T,KeyRange) -> NextSeqOp(T,KeyRange,1000) end, selectAll => - fun(T,_KeyRange) -> - case -1 =:= ets:select_count(T, ets:fun2ms(fun(_X) -> true end)) of + fun(T,_KeyRange) -> + case -1 =:= ets:select_count(T, ets:fun2ms(fun(_X) -> true end)) of true -> io:format("Will never be printed"); false -> ok end @@ -6951,7 +7028,7 @@ throughput_benchmark( NewCurrent = Current + OpPropability, [{NewCurrent, OpName}| Calculate(Res, NewCurrent)] end, - RenderScenario = + RenderScenario = fun R([], StringSoFar) -> StringSoFar; R([{Fraction, Operation}], StringSoFar) -> @@ -6978,7 +7055,7 @@ throughput_benchmark( false -> ok end end, - DataHolder = + DataHolder = fun DataHolderFun(Data)-> receive {get_data, Pid} -> Pid ! {ets_bench_data, Data}; @@ -6992,18 +7069,21 @@ throughput_benchmark( DataHolderPid ! io_lib:format(Str, List) end, GetData = - fun () -> + fun () -> DataHolderPid ! {get_data, self()}, receive {ets_bench_data, Data} -> Data end end, %% Function that runs a benchmark instance and returns the number %% of operations that were performed RunBenchmark = - fun({NrOfProcs, TableConfig, Scenario, Range, Duration}) -> + fun({NrOfProcs, TableConfig, Scenario, Range, Duration, InitFun}) -> ProbHelpTab = CalculateOpsProbHelpTab(Scenario, 0), Table = ets:new(t, TableConfig), Nobj = Range div 2, - prefill_table(Table, Range, Nobj, fun(K) -> {K} end), + case InitFun of + not_set -> prefill_table(Table, Range, Nobj, fun(K) -> {K} end); + _ -> InitFun(Table, Range, Nobj, fun(K) -> {K} end) + end, Nobj = ets:info(Table, size), SafeFixTableIfRequired(Table, Scenario, true), ParentPid = self(), @@ -7016,12 +7096,14 @@ throughput_benchmark( end, ChildPids = lists:map(fun(_N) ->spawn_link(Worker)end, lists:seq(1, NrOfProcs)), + erlang:garbage_collect(), + timer:sleep(RecoverTimeMs), lists:foreach(fun(Pid) -> Pid ! start end, ChildPids), timer:sleep(Duration), lists:foreach(fun(Pid) -> Pid ! stop end, ChildPids), TotalWorksDone = lists:foldl( - fun(_, Sum) -> - receive + fun(_, Sum) -> + receive Count -> Sum + Count end end, 0, ChildPids), @@ -7032,27 +7114,32 @@ throughput_benchmark( RunBenchmarkInSepProcess = fun(ParameterTuple) -> P = self(), - spawn_link(fun()-> P ! {bench_result, RunBenchmark(ParameterTuple)} end), - Result = receive {bench_result, Res} -> Res end, - timer:sleep(RecoverTimeMs), - Result + Results = + [begin + spawn_link(fun()-> P ! {bench_result, RunBenchmark(ParameterTuple)} end), + receive {bench_result, Res} -> Res end + end || _ <- lists:seq(1, NrOfRepeats)], + lists:sum(Results) / NrOfRepeats end, RunBenchmarkAndReport = fun(ThreadCount, TableType, Scenario, KeyRange, - Duration) -> + Duration, + InitFunName, + InitFun) -> Result = RunBenchmarkInSepProcess({ThreadCount, TableType, Scenario, KeyRange, - Duration}), + Duration, + InitFun}), Throughput = Result/(Duration/1000.0), PrintData("; ~f",[Throughput]), - Name = io_lib:format("Scenario: ~w, Key Range Size: ~w, " + Name = io_lib:format("Scenario: ~s, ~w, Key Range Size: ~w, " "# of Processes: ~w, Table Type: ~w", - [Scenario, KeyRange, ThreadCount, TableType]), + [InitFunName, Scenario, KeyRange, ThreadCount, TableType]), NotifyResFun(Name, Throughput) end, ThreadCounts = @@ -7087,17 +7174,29 @@ throughput_benchmark( PrintData("$~n",[]), lists:foreach( fun(TableType) -> - PrintData("~w ",[TableType]), lists:foreach( - fun(ThreadCount) -> - RunBenchmarkAndReport(ThreadCount, - TableType, - Scenario, - KeyRange, - BenchmarkDurationMs) + fun(InitFunArg) -> + {InitFunName, InitFun} = + case InitFunArg of + {FunName, Fun} -> {FunName, Fun}; + Fun -> {"", Fun} + end, + PrintData("~s,~w ",[InitFunName,TableType]), + lists:foreach( + fun(ThreadCount) -> + RunBenchmarkAndReport(ThreadCount, + TableType, + Scenario, + KeyRange, + BenchmarkDurationMs, + InitFunName, + InitFun) + end, + ThreadCounts), + PrintData("$~n",[]) end, - ThreadCounts), - PrintData("$~n",[]) + InitFuns) + end, TableTypes) end, @@ -7121,7 +7220,7 @@ throughput_benchmark( test_throughput_benchmark(Config) when is_list(Config) -> throughput_benchmark( #ets_throughput_bench_config{ - benchmark_duration_ms = 100, + benchmark_duration_ms = 100, recover_time_ms = 0, thread_counts = [1, erlang:system_info(schedulers)], key_ranges = [50000], @@ -7136,7 +7235,7 @@ long_throughput_benchmark(Config) when is_list(Config) -> recover_time_ms = 1000, thread_counts = [1, N div 2, N], key_ranges = [1000000], - scenarios = + scenarios = [ [ {0.5, insert}, @@ -7171,15 +7270,15 @@ long_throughput_benchmark(Config) when is_list(Config) -> {0.01, partial_select1000} ] ], - table_types = + table_types = [ [ordered_set, public, {write_concurrency, true}, {read_concurrency, true}], [set, public, {write_concurrency, true}, {read_concurrency, true}] ], etsmem_fun = fun etsmem/0, verify_etsmem_fun = fun verify_etsmem/1, - notify_res_fun = - fun(Name, Throughput) -> + notify_res_fun = + fun(Name, Throughput) -> SummaryTable = proplists:get_value(ets_benchmark_result_summary_tab, Config), AddToSummaryCounter = @@ -7209,13 +7308,47 @@ long_throughput_benchmark(Config) when is_list(Config) -> total_throughput_ordered_set) end, ct_event:notify( - #event{name = benchmark_data, + #event{name = benchmark_data, data = [{suite,"ets_bench"}, {name, Name}, {value,Throughput}]}) end }). +%% This function compares the lookup operation's performance for +%% ordered_set ETS tables with and without write_concurrency enabled +%% when the data structures have been populated in parallel and +%% sequentially. +%% +%% The main purpose of this function is to check that the +%% implementation of ordered_set with write_concurrency (CA tree) +%% adapts its structure to contention even when only lookup operations +%% are used. +lookup_catree_par_vs_seq_init_benchmark() -> + N = erlang:system_info(schedulers), + throughput_benchmark( + #ets_throughput_bench_config{ + benchmark_duration_ms = 600000, + recover_time_ms = 1000, + thread_counts = [1, N div 2, N], + key_ranges = [1000000], + init_functions = [{"seq_init", fun prefill_table/4}, + {"par_init", fun prefill_table_parallel/4}], + nr_of_repeats = 1, + scenarios = + [ + [ + {1.0, lookup} + ] + ], + table_types = + [ + [ordered_set, public, {write_concurrency, true}], + [ordered_set, public] + ], + print_result_paths_fun = fun stdout_notify_res/2 + }). + add_lists(L1,L2) -> add_lists(L1,L2,[]). add_lists([],[],Acc) -> diff --git a/lib/stdlib/test/io_SUITE.erl b/lib/stdlib/test/io_SUITE.erl index 9b6d8d7401..4eb5b1772c 100644 --- a/lib/stdlib/test/io_SUITE.erl +++ b/lib/stdlib/test/io_SUITE.erl @@ -32,7 +32,7 @@ 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_15103/1, otp_15076/1, - otp_15159/1, otp_15639/1, otp_15705/1]). + otp_15159/1, otp_15639/1, otp_15705/1, otp_15847/1, otp_15875/1]). -export([pretty/2, trf/3]). @@ -65,7 +65,7 @@ all() -> 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_15103, otp_15076, otp_15159, - otp_15639, otp_15705]. + otp_15639, otp_15705, otp_15847, otp_15875]. %% Error cases for output. error_1(Config) when is_list(Config) -> @@ -2708,3 +2708,13 @@ otp_15705(_Config) -> "|кирилли́чес|" = trf("|~10ts|", [U], -1), ok. + +otp_15847(_Config) -> + T = {someRecord,<<"1234567890">>,some,more}, + "{someRecord,<<...>>,...}" = + pretty(T, [{chars_limit,20}, {encoding,latin1}]), + ok. + +otp_15875(_Config) -> + S = io_lib:format("~tp", [[{0, [<<"00">>]}]], [{chars_limit, 18}]), + "[{0,[<<48,...>>]}]" = lists:flatten(S). diff --git a/lib/stdlib/test/lists_SUITE.erl b/lib/stdlib/test/lists_SUITE.erl index 5dab6f6697..c3c54710eb 100644 --- a/lib/stdlib/test/lists_SUITE.erl +++ b/lib/stdlib/test/lists_SUITE.erl @@ -2600,6 +2600,15 @@ subtract(Config) when is_list(Config) -> [1,2,3,4,5,6,7,8,9,9999,10000,20,21,22] = sub(lists:seq(1, 10000)++[20,21,22], lists:seq(10, 9998)), + %% ERL-986; an integer overflow relating to term comparison + %% caused subtraction to be inconsistent. + Ids = [2985095936,47540628,135460048,1266126295,240535295, + 115724671,161800351,4187206564,4178142725,234897063, + 14773162,6662515191,133150693,378034895,1874402262, + 3507611978,22850922,415521280,253360400,71683243], + + [] = id(Ids) -- id(Ids), + %% Floats/integers. [42.0,42.0] = sub([42.0,42,42.0], [42,42,42]), [1,2,3,4,43.0] = sub([1,2,3,4,5,42.0,43.0], [42.0,5]), @@ -2627,6 +2636,8 @@ subtract(Config) when is_list(Config) -> ok. +id(I) -> I. + sub_non_matching(A, B) -> A = sub(A, B). diff --git a/lib/stdlib/test/re_SUITE.erl b/lib/stdlib/test/re_SUITE.erl index c9ef9da990..06d8fe9255 100644 --- a/lib/stdlib/test/re_SUITE.erl +++ b/lib/stdlib/test/re_SUITE.erl @@ -28,7 +28,8 @@ pcre_compile_workspace_overflow/1,re_infinite_loop/1, re_backwards_accented/1,opt_dupnames/1,opt_all_names/1,inspect/1, opt_no_start_optimize/1,opt_never_utf/1,opt_ucp/1, - match_limit/1,sub_binaries/1,copt/1]). + match_limit/1,sub_binaries/1,copt/1,global_unicode_validation/1, + yield_on_subject_validation/1]). -include_lib("common_test/include/ct.hrl"). -include_lib("kernel/include/file.hrl"). @@ -45,7 +46,8 @@ all() -> pcre_compile_workspace_overflow, re_infinite_loop, re_backwards_accented, opt_dupnames, opt_all_names, inspect, opt_no_start_optimize,opt_never_utf,opt_ucp, - match_limit, sub_binaries, re_version]. + match_limit, sub_binaries, re_version, global_unicode_validation, + yield_on_subject_validation]. groups() -> []. @@ -200,7 +202,58 @@ re_version(_Config) -> {match,[Version]} = re:run(Version,"^[0-9]\\.[0-9]{2} 20[0-9]{2}-[0-9]{2}-[0-9]{2}",[{capture,all,binary}]), ok. +global_unicode_validation(Config) when is_list(Config) -> + %% Test that unicode validation of the subject is not done + %% for every match found... + Bin = binary:copy(<<"abc\n">>,100000), + {TimeAscii, _} = take_time(fun () -> + re:run(Bin, <<"b">>, [global]) + end), + {TimeUnicode, _} = take_time(fun () -> + re:run(Bin, <<"b">>, [unicode,global]) + end), + if TimeAscii == 0; TimeUnicode == 0 -> + {comment, "Not good enough resolution to compare results"}; + true -> + %% The time the operations takes should be in the + %% same order of magnitude. If validation of the + %% whole subject occurs for every match, the unicode + %% variant will take way longer time... + true = TimeUnicode div TimeAscii < 10 + end. + +take_time(Fun) -> + Start = erlang:monotonic_time(nanosecond), + Res = Fun(), + End = erlang:monotonic_time(nanosecond), + {End-Start, Res}. + +yield_on_subject_validation(Config) when is_list(Config) -> + Go = make_ref(), + Bin = binary:copy(<<"abc\n">>,100000), + {P, M} = spawn_opt(fun () -> + receive Go -> ok end, + {match,[{1,1}]} = re:run(Bin, <<"b">>, [unicode]) + end, + [link, monitor]), + 1 = erlang:trace(P, true, [running]), + P ! Go, + N = count_re_run_trap_out(P, M), + true = N >= 5, + ok. +count_re_run_trap_out(P, M) when is_reference(M) -> + receive {'DOWN',M,process,P,normal} -> ok end, + TD = erlang:trace_delivered(P), + receive {trace_delivered, P, TD} -> ok end, + count_re_run_trap_out(P, 0); +count_re_run_trap_out(P, N) when is_integer(N) -> + receive + {trace,P,out,{erlang,re_run_trap,3}} -> + count_re_run_trap_out(P, N+1) + after 0 -> + N + end. %% Test compile options given directly to run. combined_options(Config) when is_list(Config) -> diff --git a/lib/stdlib/test/re_SUITE_data/testoutput1 b/lib/stdlib/test/re_SUITE_data/testoutput1 index eff8ecc948..e6147e60b9 100644 --- a/lib/stdlib/test/re_SUITE_data/testoutput1 +++ b/lib/stdlib/test/re_SUITE_data/testoutput1 @@ -9446,4 +9446,28 @@ No match >XXX< 0: X +/ (?<word> \w+ )* \. /xi + pokus. + 0: pokus. + 1: pokus + +/(?(DEFINE) (?<word> \w+ ) ) (?&word)* \./xi + pokus. + 0: pokus. + +/(?(DEFINE) (?<word> \w+ ) ) ( (?&word)* ) \./xi + pokus. + 0: pokus. + 1: <unset> + 2: pokus + +/(?&word)* (?(DEFINE) (?<word> \w+ ) ) \./xi + pokus. + 0: pokus. + +/(?&word)* \. (?<word> \w+ )/xi + pokus.hokus + 0: pokus.hokus + 1: hokus + /-- End of testinput1 --/ diff --git a/lib/stdlib/test/re_SUITE_data/testoutput2 b/lib/stdlib/test/re_SUITE_data/testoutput2 index 61ed8d9d4e..4ccda27201 100644 --- a/lib/stdlib/test/re_SUITE_data/testoutput2 +++ b/lib/stdlib/test/re_SUITE_data/testoutput2 @@ -14721,4 +14721,8 @@ No need char 0: ab 1: a +/(?(?=^))b/ + abc + 0: b + /-- End of testinput2 --/ diff --git a/lib/stdlib/test/re_SUITE_data/testoutput4 b/lib/stdlib/test/re_SUITE_data/testoutput4 index d43c12392d..69e812cd35 100644 --- a/lib/stdlib/test/re_SUITE_data/testoutput4 +++ b/lib/stdlib/test/re_SUITE_data/testoutput4 @@ -1277,4 +1277,8 @@ No match \\C(\\W?ſ)'?{{ No match +/[^\x{100}-\x{ffff}]*[\x80-\xff]/8 + \x{99}\x{99}\x{99} + 0: \x{99}\x{99}\x{99} + /-- End of testinput4 --/ diff --git a/lib/stdlib/test/re_testoutput1_replacement_test.erl b/lib/stdlib/test/re_testoutput1_replacement_test.erl index f14df547ef..bb43047757 100644 --- a/lib/stdlib/test/re_testoutput1_replacement_test.erl +++ b/lib/stdlib/test/re_testoutput1_replacement_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2017. All Rights Reserved. +%% Copyright Ericsson AB 2008-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -20014,4 +20014,31 @@ run56() -> <<" MumdPEeFred:099">> = iolist_to_binary(re:replace(" Fred:099","(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[,;:])(?=.{8,16})(?!.*[\\s])","\\1&M&umdPE&e",[global])), <<" ugxGKrBXEcHyG">> = iolist_to_binary(re:replace(" X","(?=.*X)X$","ugxGKrB&EcHyG",[])), <<" ugxGKrBXEcHyG">> = iolist_to_binary(re:replace(" X","(?=.*X)X$","ugxGKrB&EcHyG",[global])), + <<">MHFvXX<">> = iolist_to_binary(re:replace(">XXX<","X+(?#comment)?","MHFv",[])), + <<">MHFvMHFvMHFv<">> = iolist_to_binary(re:replace(">XXX<","X+(?#comment)?","MHFv",[global])), + <<"lTpokusldxfHXOpokuswsrRorpokus.">> = iolist_to_binary(re:replace("pokus."," (?<word> \\w+ )* \\. ","lT\\1ldxfHXO\\1wsrRor&",[extended, + caseless])), + <<"lTpokusldxfHXOpokuswsrRorpokus.">> = iolist_to_binary(re:replace("pokus."," (?<word> \\w+ )* \\. ","lT\\1ldxfHXO\\1wsrRor&",[extended, + caseless, + global])), + <<"Oeapokus.xo">> = iolist_to_binary(re:replace("pokus.","(?(DEFINE) (?<word> \\w+ ) ) (?&word)* \\.","Oea&xo",[extended, + caseless])), + <<"Oeapokus.xo">> = iolist_to_binary(re:replace("pokus.","(?(DEFINE) (?<word> \\w+ ) ) (?&word)* \\.","Oea&xo",[extended, + caseless, + global])), + <<"Wpokus.pity">> = iolist_to_binary(re:replace("pokus.","(?(DEFINE) (?<word> \\w+ ) ) ( (?&word)* ) \\.","W&pity",[extended, + caseless])), + <<"Wpokus.pity">> = iolist_to_binary(re:replace("pokus.","(?(DEFINE) (?<word> \\w+ ) ) ( (?&word)* ) \\.","W&pity",[extended, + caseless, + global])), + <<"iujmNtBvmcyi">> = iolist_to_binary(re:replace("pokus.","(?&word)* (?(DEFINE) (?<word> \\w+ ) ) \\.","iuj\\1m\\1NtBvmcyi\\1",[extended, + caseless])), + <<"iujmNtBvmcyi">> = iolist_to_binary(re:replace("pokus.","(?&word)* (?(DEFINE) (?<word> \\w+ ) ) \\.","iuj\\1m\\1NtBvmcyi\\1",[extended, + caseless, + global])), + <<"Ipokus.hokusbQpokus.hokusB">> = iolist_to_binary(re:replace("pokus.hokus","(?&word)* \\. (?<word> \\w+ )","I&bQ&B",[extended, + caseless])), + <<"Ipokus.hokusbQpokus.hokusB">> = iolist_to_binary(re:replace("pokus.hokus","(?&word)* \\. (?<word> \\w+ )","I&bQ&B",[extended, + caseless, + global])), ok. diff --git a/lib/stdlib/test/re_testoutput1_split_test.erl b/lib/stdlib/test/re_testoutput1_split_test.erl index 8218cd9bd2..fcffa89e3f 100644 --- a/lib/stdlib/test/re_testoutput1_split_test.erl +++ b/lib/stdlib/test/re_testoutput1_split_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2017. All Rights Reserved. +%% Copyright Ericsson AB 2008-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -84,1360 +84,1360 @@ run() -> run56(), ok. run0() -> - <<"">> = iolist_to_binary(join(re:split("the quick brown fox","the quick brown fox",[trim]))), + <<"">> = iolist_to_binary(join(re:split("the quick brown fox","the quick brown fox",[trim]))), <<":">> = iolist_to_binary(join(re:split("the quick brown fox","the quick brown fox",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("the quick brown fox","the quick brown fox",[]))), - <<"The quick brown FOX">> = iolist_to_binary(join(re:split("The quick brown FOX","the quick brown fox",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("the quick brown fox","the quick brown fox",[]))), + <<"The quick brown FOX">> = iolist_to_binary(join(re:split("The quick brown FOX","the quick brown fox",[trim]))), <<"The quick brown FOX">> = iolist_to_binary(join(re:split("The quick brown FOX","the quick brown fox",[{parts, - 2}]))), - <<"The quick brown FOX">> = iolist_to_binary(join(re:split("The quick brown FOX","the quick brown fox",[]))), - <<"What do you know about :?">> = iolist_to_binary(join(re:split("What do you know about the quick brown fox?","the quick brown fox",[trim]))), + 2}]))), + <<"The quick brown FOX">> = iolist_to_binary(join(re:split("The quick brown FOX","the quick brown fox",[]))), + <<"What do you know about :?">> = iolist_to_binary(join(re:split("What do you know about the quick brown fox?","the quick brown fox",[trim]))), <<"What do you know about :?">> = iolist_to_binary(join(re:split("What do you know about the quick brown fox?","the quick brown fox",[{parts, - 2}]))), - <<"What do you know about :?">> = iolist_to_binary(join(re:split("What do you know about the quick brown fox?","the quick brown fox",[]))), - <<"What do you know about THE QUICK BROWN FOX?">> = iolist_to_binary(join(re:split("What do you know about THE QUICK BROWN FOX?","the quick brown fox",[trim]))), + 2}]))), + <<"What do you know about :?">> = iolist_to_binary(join(re:split("What do you know about the quick brown fox?","the quick brown fox",[]))), + <<"What do you know about THE QUICK BROWN FOX?">> = iolist_to_binary(join(re:split("What do you know about THE QUICK BROWN FOX?","the quick brown fox",[trim]))), <<"What do you know about THE QUICK BROWN FOX?">> = iolist_to_binary(join(re:split("What do you know about THE QUICK BROWN FOX?","the quick brown fox",[{parts, - 2}]))), - <<"What do you know about THE QUICK BROWN FOX?">> = iolist_to_binary(join(re:split("What do you know about THE QUICK BROWN FOX?","the quick brown fox",[]))), + 2}]))), + <<"What do you know about THE QUICK BROWN FOX?">> = iolist_to_binary(join(re:split("What do you know about THE QUICK BROWN FOX?","the quick brown fox",[]))), <<"">> = iolist_to_binary(join(re:split("the quick brown fox","The quick brown fox",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("the quick brown fox","The quick brown fox",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("the quick brown fox","The quick brown fox",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("the quick brown fox","The quick brown fox",[caseless]))), <<"">> = iolist_to_binary(join(re:split("The quick brown FOX","The quick brown fox",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("The quick brown FOX","The quick brown fox",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("The quick brown FOX","The quick brown fox",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("The quick brown FOX","The quick brown fox",[caseless]))), <<"What do you know about :?">> = iolist_to_binary(join(re:split("What do you know about the quick brown fox?","The quick brown fox",[caseless, - trim]))), + trim]))), <<"What do you know about :?">> = iolist_to_binary(join(re:split("What do you know about the quick brown fox?","The quick brown fox",[caseless, {parts, - 2}]))), - <<"What do you know about :?">> = iolist_to_binary(join(re:split("What do you know about the quick brown fox?","The quick brown fox",[caseless]))), + 2}]))), + <<"What do you know about :?">> = iolist_to_binary(join(re:split("What do you know about the quick brown fox?","The quick brown fox",[caseless]))), <<"What do you know about :?">> = iolist_to_binary(join(re:split("What do you know about THE QUICK BROWN FOX?","The quick brown fox",[caseless, - trim]))), + trim]))), <<"What do you know about :?">> = iolist_to_binary(join(re:split("What do you know about THE QUICK BROWN FOX?","The quick brown fox",[caseless, {parts, - 2}]))), - <<"What do you know about :?">> = iolist_to_binary(join(re:split("What do you know about THE QUICK BROWN FOX?","The quick brown fox",[caseless]))), + 2}]))), + <<"What do you know about :?">> = iolist_to_binary(join(re:split("What do you know about THE QUICK BROWN FOX?","The quick brown fox",[caseless]))), <<"">> = iolist_to_binary(join(re:split("abcd -
9;$\\?caxyz","abcd\\t\\n\\r\\f\\a\\e\\071\\x3b\\$\\\\\\?caxyz",[trim]))), +
9;$\\?caxyz","abcd\\t\\n\\r\\f\\a\\e\\071\\x3b\\$\\\\\\?caxyz",[trim]))), <<":">> = iolist_to_binary(join(re:split("abcd
9;$\\?caxyz","abcd\\t\\n\\r\\f\\a\\e\\071\\x3b\\$\\\\\\?caxyz",[{parts, - 2}]))), + 2}]))), <<":">> = iolist_to_binary(join(re:split("abcd -
9;$\\?caxyz","abcd\\t\\n\\r\\f\\a\\e\\071\\x3b\\$\\\\\\?caxyz",[]))), - <<"">> = iolist_to_binary(join(re:split("abxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), +
9;$\\?caxyz","abcd\\t\\n\\r\\f\\a\\e\\071\\x3b\\$\\\\\\?caxyz",[]))), + <<"">> = iolist_to_binary(join(re:split("abxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("abxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("abxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("abxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("abxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("aabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("aabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("aabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("aaabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("aaabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("abcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("abcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("abcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("aabcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("aabcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("aabcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aabcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aabcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaabcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaabcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaabcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaabcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("abxyzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaabcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("abxyzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("abxyzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abxyzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("aabxyzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abxyzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("aabxyzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("aabxyzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aabxyzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("aaabxyzzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aabxyzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("aaabxyzzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaabxyzzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaabxyzzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaabxyzzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaabxyzzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaabxyzzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaabxyzzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaabxyzzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("abcxyzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaabxyzzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("abcxyzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("abcxyzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abcxyzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("aabcxyzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abcxyzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("aabcxyzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("aabcxyzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aabcxyzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("aaabcxyzzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aabcxyzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("aaabcxyzzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaabcxyzzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaabcxyzzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaabcxyzzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbbxyyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbbxyyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbbxyyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbbxyyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypABzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbbxyyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypABzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypABzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypABzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypABBzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypABzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypABBzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypABBzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypABBzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<">>>">> = iolist_to_binary(join(re:split(">>>aaabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypABBzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<">>>">> = iolist_to_binary(join(re:split(">>>aaabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<">>>:">> = iolist_to_binary(join(re:split(">>>aaabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<">>>:">> = iolist_to_binary(join(re:split(">>>aaabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<">">> = iolist_to_binary(join(re:split(">aaaabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<">>>:">> = iolist_to_binary(join(re:split(">>>aaabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<">">> = iolist_to_binary(join(re:split(">aaaabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<">:">> = iolist_to_binary(join(re:split(">aaaabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<">:">> = iolist_to_binary(join(re:split(">aaaabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<">>>>">> = iolist_to_binary(join(re:split(">>>>abcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<">:">> = iolist_to_binary(join(re:split(">aaaabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<">>>>">> = iolist_to_binary(join(re:split(">>>>abcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<">>>>:">> = iolist_to_binary(join(re:split(">>>>abcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<">>>>:">> = iolist_to_binary(join(re:split(">>>>abcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<">>>>:">> = iolist_to_binary(join(re:split(">>>>abcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"abxyzpqrrabbxyyyypqAzz">> = iolist_to_binary(join(re:split("abxyzpqrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"abxyzpqrrabbxyyyypqAzz">> = iolist_to_binary(join(re:split("abxyzpqrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<"abxyzpqrrabbxyyyypqAzz">> = iolist_to_binary(join(re:split("abxyzpqrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<"abxyzpqrrabbxyyyypqAzz">> = iolist_to_binary(join(re:split("abxyzpqrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"abxyzpqrrrrabbxyyyypqAzz">> = iolist_to_binary(join(re:split("abxyzpqrrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<"abxyzpqrrabbxyyyypqAzz">> = iolist_to_binary(join(re:split("abxyzpqrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"abxyzpqrrrrabbxyyyypqAzz">> = iolist_to_binary(join(re:split("abxyzpqrrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<"abxyzpqrrrrabbxyyyypqAzz">> = iolist_to_binary(join(re:split("abxyzpqrrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<"abxyzpqrrrrabbxyyyypqAzz">> = iolist_to_binary(join(re:split("abxyzpqrrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"abxyzpqrrrabxyyyypqAzz">> = iolist_to_binary(join(re:split("abxyzpqrrrabxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<"abxyzpqrrrrabbxyyyypqAzz">> = iolist_to_binary(join(re:split("abxyzpqrrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"abxyzpqrrrabxyyyypqAzz">> = iolist_to_binary(join(re:split("abxyzpqrrrabxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<"abxyzpqrrrabxyyyypqAzz">> = iolist_to_binary(join(re:split("abxyzpqrrrabxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<"abxyzpqrrrabxyyyypqAzz">> = iolist_to_binary(join(re:split("abxyzpqrrrabxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"aaaabcxyzzzzpqrrrabbbxyyyyyypqAzz">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbbxyyyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<"abxyzpqrrrabxyyyypqAzz">> = iolist_to_binary(join(re:split("abxyzpqrrrabxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"aaaabcxyzzzzpqrrrabbbxyyyyyypqAzz">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbbxyyyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<"aaaabcxyzzzzpqrrrabbbxyyyyyypqAzz">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbbxyyyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<"aaaabcxyzzzzpqrrrabbbxyyyyyypqAzz">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbbxyyyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"aaaabcxyzzzzpqrrrabbbxyyypqAzz">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbbxyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<"aaaabcxyzzzzpqrrrabbbxyyyyyypqAzz">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbbxyyyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"aaaabcxyzzzzpqrrrabbbxyyypqAzz">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbbxyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<"aaaabcxyzzzzpqrrrabbbxyyypqAzz">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbbxyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<"aaaabcxyzzzzpqrrrabbbxyyypqAzz">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbbxyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"aaabcxyzpqrrrabbxyyyypqqqqqqqAzz">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqqqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<"aaaabcxyzzzzpqrrrabbbxyyypqAzz">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbbxyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"aaabcxyzpqrrrabbxyyyypqqqqqqqAzz">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqqqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<"aaabcxyzpqrrrabbxyyyypqqqqqqqAzz">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqqqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<"aaabcxyzpqrrrabbxyyyypqqqqqqqAzz">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqqqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<":abc">> = iolist_to_binary(join(re:split("abczz","^(abc){1,2}zz",[trim]))), + 2}]))), + <<"aaabcxyzpqrrrabbxyyyypqqqqqqqAzz">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqqqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<":abc">> = iolist_to_binary(join(re:split("abczz","^(abc){1,2}zz",[trim]))), <<":abc:">> = iolist_to_binary(join(re:split("abczz","^(abc){1,2}zz",[{parts, - 2}]))), - <<":abc:">> = iolist_to_binary(join(re:split("abczz","^(abc){1,2}zz",[]))), - <<":abc">> = iolist_to_binary(join(re:split("abcabczz","^(abc){1,2}zz",[trim]))), + 2}]))), + <<":abc:">> = iolist_to_binary(join(re:split("abczz","^(abc){1,2}zz",[]))), + <<":abc">> = iolist_to_binary(join(re:split("abcabczz","^(abc){1,2}zz",[trim]))), <<":abc:">> = iolist_to_binary(join(re:split("abcabczz","^(abc){1,2}zz",[{parts, - 2}]))), - <<":abc:">> = iolist_to_binary(join(re:split("abcabczz","^(abc){1,2}zz",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(abc){1,2}zz",[trim]))), + 2}]))), + <<":abc:">> = iolist_to_binary(join(re:split("abcabczz","^(abc){1,2}zz",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(abc){1,2}zz",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(abc){1,2}zz",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(abc){1,2}zz",[]))), - <<"zz">> = iolist_to_binary(join(re:split("zz","^(abc){1,2}zz",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(abc){1,2}zz",[]))), + <<"zz">> = iolist_to_binary(join(re:split("zz","^(abc){1,2}zz",[trim]))), <<"zz">> = iolist_to_binary(join(re:split("zz","^(abc){1,2}zz",[{parts, - 2}]))), - <<"zz">> = iolist_to_binary(join(re:split("zz","^(abc){1,2}zz",[]))), - <<"abcabcabczz">> = iolist_to_binary(join(re:split("abcabcabczz","^(abc){1,2}zz",[trim]))), + 2}]))), + <<"zz">> = iolist_to_binary(join(re:split("zz","^(abc){1,2}zz",[]))), + <<"abcabcabczz">> = iolist_to_binary(join(re:split("abcabcabczz","^(abc){1,2}zz",[trim]))), <<"abcabcabczz">> = iolist_to_binary(join(re:split("abcabcabczz","^(abc){1,2}zz",[{parts, - 2}]))), - <<"abcabcabczz">> = iolist_to_binary(join(re:split("abcabcabczz","^(abc){1,2}zz",[]))), - <<">>abczz">> = iolist_to_binary(join(re:split(">>abczz","^(abc){1,2}zz",[trim]))), + 2}]))), + <<"abcabcabczz">> = iolist_to_binary(join(re:split("abcabcabczz","^(abc){1,2}zz",[]))), + <<">>abczz">> = iolist_to_binary(join(re:split(">>abczz","^(abc){1,2}zz",[trim]))), <<">>abczz">> = iolist_to_binary(join(re:split(">>abczz","^(abc){1,2}zz",[{parts, - 2}]))), - <<">>abczz">> = iolist_to_binary(join(re:split(">>abczz","^(abc){1,2}zz",[]))), - <<":b">> = iolist_to_binary(join(re:split("bc","^(b+?|a){1,2}?c",[trim]))), + 2}]))), + <<">>abczz">> = iolist_to_binary(join(re:split(">>abczz","^(abc){1,2}zz",[]))), + <<":b">> = iolist_to_binary(join(re:split("bc","^(b+?|a){1,2}?c",[trim]))), <<":b:">> = iolist_to_binary(join(re:split("bc","^(b+?|a){1,2}?c",[{parts, - 2}]))), - <<":b:">> = iolist_to_binary(join(re:split("bc","^(b+?|a){1,2}?c",[]))), - <<":b">> = iolist_to_binary(join(re:split("bbc","^(b+?|a){1,2}?c",[trim]))), + 2}]))), + <<":b:">> = iolist_to_binary(join(re:split("bc","^(b+?|a){1,2}?c",[]))), + <<":b">> = iolist_to_binary(join(re:split("bbc","^(b+?|a){1,2}?c",[trim]))), <<":b:">> = iolist_to_binary(join(re:split("bbc","^(b+?|a){1,2}?c",[{parts, - 2}]))), - <<":b:">> = iolist_to_binary(join(re:split("bbc","^(b+?|a){1,2}?c",[]))), - <<":bb">> = iolist_to_binary(join(re:split("bbbc","^(b+?|a){1,2}?c",[trim]))), + 2}]))), + <<":b:">> = iolist_to_binary(join(re:split("bbc","^(b+?|a){1,2}?c",[]))), + <<":bb">> = iolist_to_binary(join(re:split("bbbc","^(b+?|a){1,2}?c",[trim]))), <<":bb:">> = iolist_to_binary(join(re:split("bbbc","^(b+?|a){1,2}?c",[{parts, - 2}]))), - <<":bb:">> = iolist_to_binary(join(re:split("bbbc","^(b+?|a){1,2}?c",[]))), - <<":a">> = iolist_to_binary(join(re:split("bac","^(b+?|a){1,2}?c",[trim]))), + 2}]))), + <<":bb:">> = iolist_to_binary(join(re:split("bbbc","^(b+?|a){1,2}?c",[]))), + <<":a">> = iolist_to_binary(join(re:split("bac","^(b+?|a){1,2}?c",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("bac","^(b+?|a){1,2}?c",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("bac","^(b+?|a){1,2}?c",[]))), - <<":a">> = iolist_to_binary(join(re:split("bbac","^(b+?|a){1,2}?c",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("bac","^(b+?|a){1,2}?c",[]))), + <<":a">> = iolist_to_binary(join(re:split("bbac","^(b+?|a){1,2}?c",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("bbac","^(b+?|a){1,2}?c",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("bbac","^(b+?|a){1,2}?c",[]))), - <<":a">> = iolist_to_binary(join(re:split("aac","^(b+?|a){1,2}?c",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("bbac","^(b+?|a){1,2}?c",[]))), + <<":a">> = iolist_to_binary(join(re:split("aac","^(b+?|a){1,2}?c",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aac","^(b+?|a){1,2}?c",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aac","^(b+?|a){1,2}?c",[]))), - <<":bbbbbbbbbbb">> = iolist_to_binary(join(re:split("abbbbbbbbbbbc","^(b+?|a){1,2}?c",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aac","^(b+?|a){1,2}?c",[]))), + <<":bbbbbbbbbbb">> = iolist_to_binary(join(re:split("abbbbbbbbbbbc","^(b+?|a){1,2}?c",[trim]))), <<":bbbbbbbbbbb:">> = iolist_to_binary(join(re:split("abbbbbbbbbbbc","^(b+?|a){1,2}?c",[{parts, - 2}]))), - <<":bbbbbbbbbbb:">> = iolist_to_binary(join(re:split("abbbbbbbbbbbc","^(b+?|a){1,2}?c",[]))), - <<":a">> = iolist_to_binary(join(re:split("bbbbbbbbbbbac","^(b+?|a){1,2}?c",[trim]))), + 2}]))), + <<":bbbbbbbbbbb:">> = iolist_to_binary(join(re:split("abbbbbbbbbbbc","^(b+?|a){1,2}?c",[]))), + <<":a">> = iolist_to_binary(join(re:split("bbbbbbbbbbbac","^(b+?|a){1,2}?c",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("bbbbbbbbbbbac","^(b+?|a){1,2}?c",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("bbbbbbbbbbbac","^(b+?|a){1,2}?c",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(b+?|a){1,2}?c",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("bbbbbbbbbbbac","^(b+?|a){1,2}?c",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(b+?|a){1,2}?c",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(b+?|a){1,2}?c",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(b+?|a){1,2}?c",[]))), - <<"aaac">> = iolist_to_binary(join(re:split("aaac","^(b+?|a){1,2}?c",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(b+?|a){1,2}?c",[]))), + <<"aaac">> = iolist_to_binary(join(re:split("aaac","^(b+?|a){1,2}?c",[trim]))), <<"aaac">> = iolist_to_binary(join(re:split("aaac","^(b+?|a){1,2}?c",[{parts, - 2}]))), - <<"aaac">> = iolist_to_binary(join(re:split("aaac","^(b+?|a){1,2}?c",[]))), - <<"abbbbbbbbbbbac">> = iolist_to_binary(join(re:split("abbbbbbbbbbbac","^(b+?|a){1,2}?c",[trim]))), + 2}]))), + <<"aaac">> = iolist_to_binary(join(re:split("aaac","^(b+?|a){1,2}?c",[]))), + <<"abbbbbbbbbbbac">> = iolist_to_binary(join(re:split("abbbbbbbbbbbac","^(b+?|a){1,2}?c",[trim]))), <<"abbbbbbbbbbbac">> = iolist_to_binary(join(re:split("abbbbbbbbbbbac","^(b+?|a){1,2}?c",[{parts, - 2}]))), - <<"abbbbbbbbbbbac">> = iolist_to_binary(join(re:split("abbbbbbbbbbbac","^(b+?|a){1,2}?c",[]))), - <<":b">> = iolist_to_binary(join(re:split("bc","^(b+|a){1,2}c",[trim]))), + 2}]))), + <<"abbbbbbbbbbbac">> = iolist_to_binary(join(re:split("abbbbbbbbbbbac","^(b+?|a){1,2}?c",[]))), + <<":b">> = iolist_to_binary(join(re:split("bc","^(b+|a){1,2}c",[trim]))), <<":b:">> = iolist_to_binary(join(re:split("bc","^(b+|a){1,2}c",[{parts, - 2}]))), - <<":b:">> = iolist_to_binary(join(re:split("bc","^(b+|a){1,2}c",[]))), - <<":bb">> = iolist_to_binary(join(re:split("bbc","^(b+|a){1,2}c",[trim]))), + 2}]))), + <<":b:">> = iolist_to_binary(join(re:split("bc","^(b+|a){1,2}c",[]))), + <<":bb">> = iolist_to_binary(join(re:split("bbc","^(b+|a){1,2}c",[trim]))), <<":bb:">> = iolist_to_binary(join(re:split("bbc","^(b+|a){1,2}c",[{parts, - 2}]))), - <<":bb:">> = iolist_to_binary(join(re:split("bbc","^(b+|a){1,2}c",[]))), - <<":bbb">> = iolist_to_binary(join(re:split("bbbc","^(b+|a){1,2}c",[trim]))), + 2}]))), + <<":bb:">> = iolist_to_binary(join(re:split("bbc","^(b+|a){1,2}c",[]))), + <<":bbb">> = iolist_to_binary(join(re:split("bbbc","^(b+|a){1,2}c",[trim]))), <<":bbb:">> = iolist_to_binary(join(re:split("bbbc","^(b+|a){1,2}c",[{parts, - 2}]))), - <<":bbb:">> = iolist_to_binary(join(re:split("bbbc","^(b+|a){1,2}c",[]))), - <<":a">> = iolist_to_binary(join(re:split("bac","^(b+|a){1,2}c",[trim]))), + 2}]))), + <<":bbb:">> = iolist_to_binary(join(re:split("bbbc","^(b+|a){1,2}c",[]))), + <<":a">> = iolist_to_binary(join(re:split("bac","^(b+|a){1,2}c",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("bac","^(b+|a){1,2}c",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("bac","^(b+|a){1,2}c",[]))), - <<":a">> = iolist_to_binary(join(re:split("bbac","^(b+|a){1,2}c",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("bac","^(b+|a){1,2}c",[]))), + <<":a">> = iolist_to_binary(join(re:split("bbac","^(b+|a){1,2}c",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("bbac","^(b+|a){1,2}c",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("bbac","^(b+|a){1,2}c",[]))), - <<":a">> = iolist_to_binary(join(re:split("aac","^(b+|a){1,2}c",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("bbac","^(b+|a){1,2}c",[]))), + <<":a">> = iolist_to_binary(join(re:split("aac","^(b+|a){1,2}c",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aac","^(b+|a){1,2}c",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aac","^(b+|a){1,2}c",[]))), - <<":bbbbbbbbbbb">> = iolist_to_binary(join(re:split("abbbbbbbbbbbc","^(b+|a){1,2}c",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aac","^(b+|a){1,2}c",[]))), + <<":bbbbbbbbbbb">> = iolist_to_binary(join(re:split("abbbbbbbbbbbc","^(b+|a){1,2}c",[trim]))), <<":bbbbbbbbbbb:">> = iolist_to_binary(join(re:split("abbbbbbbbbbbc","^(b+|a){1,2}c",[{parts, - 2}]))), - <<":bbbbbbbbbbb:">> = iolist_to_binary(join(re:split("abbbbbbbbbbbc","^(b+|a){1,2}c",[]))), - <<":a">> = iolist_to_binary(join(re:split("bbbbbbbbbbbac","^(b+|a){1,2}c",[trim]))), + 2}]))), + <<":bbbbbbbbbbb:">> = iolist_to_binary(join(re:split("abbbbbbbbbbbc","^(b+|a){1,2}c",[]))), + <<":a">> = iolist_to_binary(join(re:split("bbbbbbbbbbbac","^(b+|a){1,2}c",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("bbbbbbbbbbbac","^(b+|a){1,2}c",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("bbbbbbbbbbbac","^(b+|a){1,2}c",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(b+|a){1,2}c",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("bbbbbbbbbbbac","^(b+|a){1,2}c",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(b+|a){1,2}c",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(b+|a){1,2}c",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(b+|a){1,2}c",[]))), - <<"aaac">> = iolist_to_binary(join(re:split("aaac","^(b+|a){1,2}c",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(b+|a){1,2}c",[]))), + <<"aaac">> = iolist_to_binary(join(re:split("aaac","^(b+|a){1,2}c",[trim]))), <<"aaac">> = iolist_to_binary(join(re:split("aaac","^(b+|a){1,2}c",[{parts, - 2}]))), - <<"aaac">> = iolist_to_binary(join(re:split("aaac","^(b+|a){1,2}c",[]))), - <<"abbbbbbbbbbbac">> = iolist_to_binary(join(re:split("abbbbbbbbbbbac","^(b+|a){1,2}c",[trim]))), + 2}]))), + <<"aaac">> = iolist_to_binary(join(re:split("aaac","^(b+|a){1,2}c",[]))), + <<"abbbbbbbbbbbac">> = iolist_to_binary(join(re:split("abbbbbbbbbbbac","^(b+|a){1,2}c",[trim]))), <<"abbbbbbbbbbbac">> = iolist_to_binary(join(re:split("abbbbbbbbbbbac","^(b+|a){1,2}c",[{parts, - 2}]))), - <<"abbbbbbbbbbbac">> = iolist_to_binary(join(re:split("abbbbbbbbbbbac","^(b+|a){1,2}c",[]))), - <<":b">> = iolist_to_binary(join(re:split("bbc","^(b+|a){1,2}?bc",[trim]))), + 2}]))), + <<"abbbbbbbbbbbac">> = iolist_to_binary(join(re:split("abbbbbbbbbbbac","^(b+|a){1,2}c",[]))), + <<":b">> = iolist_to_binary(join(re:split("bbc","^(b+|a){1,2}?bc",[trim]))), <<":b:">> = iolist_to_binary(join(re:split("bbc","^(b+|a){1,2}?bc",[{parts, - 2}]))), - <<":b:">> = iolist_to_binary(join(re:split("bbc","^(b+|a){1,2}?bc",[]))), - <<":ba">> = iolist_to_binary(join(re:split("babc","^(b*|ba){1,2}?bc",[trim]))), + 2}]))), + <<":b:">> = iolist_to_binary(join(re:split("bbc","^(b+|a){1,2}?bc",[]))), + <<":ba">> = iolist_to_binary(join(re:split("babc","^(b*|ba){1,2}?bc",[trim]))), <<":ba:">> = iolist_to_binary(join(re:split("babc","^(b*|ba){1,2}?bc",[{parts, - 2}]))), - <<":ba:">> = iolist_to_binary(join(re:split("babc","^(b*|ba){1,2}?bc",[]))), - <<":ba">> = iolist_to_binary(join(re:split("bbabc","^(b*|ba){1,2}?bc",[trim]))), + 2}]))), + <<":ba:">> = iolist_to_binary(join(re:split("babc","^(b*|ba){1,2}?bc",[]))), + <<":ba">> = iolist_to_binary(join(re:split("bbabc","^(b*|ba){1,2}?bc",[trim]))), <<":ba:">> = iolist_to_binary(join(re:split("bbabc","^(b*|ba){1,2}?bc",[{parts, - 2}]))), - <<":ba:">> = iolist_to_binary(join(re:split("bbabc","^(b*|ba){1,2}?bc",[]))), - <<":ba">> = iolist_to_binary(join(re:split("bababc","^(b*|ba){1,2}?bc",[trim]))), + 2}]))), + <<":ba:">> = iolist_to_binary(join(re:split("bbabc","^(b*|ba){1,2}?bc",[]))), + <<":ba">> = iolist_to_binary(join(re:split("bababc","^(b*|ba){1,2}?bc",[trim]))), <<":ba:">> = iolist_to_binary(join(re:split("bababc","^(b*|ba){1,2}?bc",[{parts, - 2}]))), - <<":ba:">> = iolist_to_binary(join(re:split("bababc","^(b*|ba){1,2}?bc",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(b*|ba){1,2}?bc",[trim]))), + 2}]))), + <<":ba:">> = iolist_to_binary(join(re:split("bababc","^(b*|ba){1,2}?bc",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(b*|ba){1,2}?bc",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(b*|ba){1,2}?bc",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(b*|ba){1,2}?bc",[]))), - <<"bababbc">> = iolist_to_binary(join(re:split("bababbc","^(b*|ba){1,2}?bc",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(b*|ba){1,2}?bc",[]))), + <<"bababbc">> = iolist_to_binary(join(re:split("bababbc","^(b*|ba){1,2}?bc",[trim]))), <<"bababbc">> = iolist_to_binary(join(re:split("bababbc","^(b*|ba){1,2}?bc",[{parts, - 2}]))), - <<"bababbc">> = iolist_to_binary(join(re:split("bababbc","^(b*|ba){1,2}?bc",[]))), - <<"babababc">> = iolist_to_binary(join(re:split("babababc","^(b*|ba){1,2}?bc",[trim]))), + 2}]))), + <<"bababbc">> = iolist_to_binary(join(re:split("bababbc","^(b*|ba){1,2}?bc",[]))), + <<"babababc">> = iolist_to_binary(join(re:split("babababc","^(b*|ba){1,2}?bc",[trim]))), <<"babababc">> = iolist_to_binary(join(re:split("babababc","^(b*|ba){1,2}?bc",[{parts, - 2}]))), - <<"babababc">> = iolist_to_binary(join(re:split("babababc","^(b*|ba){1,2}?bc",[]))), - <<":ba">> = iolist_to_binary(join(re:split("babc","^(ba|b*){1,2}?bc",[trim]))), + 2}]))), + <<"babababc">> = iolist_to_binary(join(re:split("babababc","^(b*|ba){1,2}?bc",[]))), + <<":ba">> = iolist_to_binary(join(re:split("babc","^(ba|b*){1,2}?bc",[trim]))), <<":ba:">> = iolist_to_binary(join(re:split("babc","^(ba|b*){1,2}?bc",[{parts, - 2}]))), - <<":ba:">> = iolist_to_binary(join(re:split("babc","^(ba|b*){1,2}?bc",[]))), - <<":ba">> = iolist_to_binary(join(re:split("bbabc","^(ba|b*){1,2}?bc",[trim]))), + 2}]))), + <<":ba:">> = iolist_to_binary(join(re:split("babc","^(ba|b*){1,2}?bc",[]))), + <<":ba">> = iolist_to_binary(join(re:split("bbabc","^(ba|b*){1,2}?bc",[trim]))), <<":ba:">> = iolist_to_binary(join(re:split("bbabc","^(ba|b*){1,2}?bc",[{parts, - 2}]))), - <<":ba:">> = iolist_to_binary(join(re:split("bbabc","^(ba|b*){1,2}?bc",[]))), - <<":ba">> = iolist_to_binary(join(re:split("bababc","^(ba|b*){1,2}?bc",[trim]))), + 2}]))), + <<":ba:">> = iolist_to_binary(join(re:split("bbabc","^(ba|b*){1,2}?bc",[]))), + <<":ba">> = iolist_to_binary(join(re:split("bababc","^(ba|b*){1,2}?bc",[trim]))), <<":ba:">> = iolist_to_binary(join(re:split("bababc","^(ba|b*){1,2}?bc",[{parts, - 2}]))), - <<":ba:">> = iolist_to_binary(join(re:split("bababc","^(ba|b*){1,2}?bc",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(ba|b*){1,2}?bc",[trim]))), + 2}]))), + <<":ba:">> = iolist_to_binary(join(re:split("bababc","^(ba|b*){1,2}?bc",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(ba|b*){1,2}?bc",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(ba|b*){1,2}?bc",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(ba|b*){1,2}?bc",[]))), - <<"bababbc">> = iolist_to_binary(join(re:split("bababbc","^(ba|b*){1,2}?bc",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(ba|b*){1,2}?bc",[]))), + <<"bababbc">> = iolist_to_binary(join(re:split("bababbc","^(ba|b*){1,2}?bc",[trim]))), <<"bababbc">> = iolist_to_binary(join(re:split("bababbc","^(ba|b*){1,2}?bc",[{parts, - 2}]))), - <<"bababbc">> = iolist_to_binary(join(re:split("bababbc","^(ba|b*){1,2}?bc",[]))), - <<"babababc">> = iolist_to_binary(join(re:split("babababc","^(ba|b*){1,2}?bc",[trim]))), + 2}]))), + <<"bababbc">> = iolist_to_binary(join(re:split("bababbc","^(ba|b*){1,2}?bc",[]))), + <<"babababc">> = iolist_to_binary(join(re:split("babababc","^(ba|b*){1,2}?bc",[trim]))), <<"babababc">> = iolist_to_binary(join(re:split("babababc","^(ba|b*){1,2}?bc",[{parts, - 2}]))), - <<"babababc">> = iolist_to_binary(join(re:split("babababc","^(ba|b*){1,2}?bc",[]))), - <<"">> = iolist_to_binary(join(re:split(";z","^\\ca\\cA\\c[;\\c:",[trim]))), + 2}]))), + <<"babababc">> = iolist_to_binary(join(re:split("babababc","^(ba|b*){1,2}?bc",[]))), + <<"">> = iolist_to_binary(join(re:split(";z","^\\ca\\cA\\c[;\\c:",[trim]))), <<":">> = iolist_to_binary(join(re:split(";z","^\\ca\\cA\\c[;\\c:",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split(";z","^\\ca\\cA\\c[;\\c:",[]))), - <<":thing">> = iolist_to_binary(join(re:split("athing","^[ab\\]cde]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split(";z","^\\ca\\cA\\c[;\\c:",[]))), + <<":thing">> = iolist_to_binary(join(re:split("athing","^[ab\\]cde]",[trim]))), <<":thing">> = iolist_to_binary(join(re:split("athing","^[ab\\]cde]",[{parts, - 2}]))), - <<":thing">> = iolist_to_binary(join(re:split("athing","^[ab\\]cde]",[]))), - <<":thing">> = iolist_to_binary(join(re:split("bthing","^[ab\\]cde]",[trim]))), + 2}]))), + <<":thing">> = iolist_to_binary(join(re:split("athing","^[ab\\]cde]",[]))), + <<":thing">> = iolist_to_binary(join(re:split("bthing","^[ab\\]cde]",[trim]))), <<":thing">> = iolist_to_binary(join(re:split("bthing","^[ab\\]cde]",[{parts, - 2}]))), - <<":thing">> = iolist_to_binary(join(re:split("bthing","^[ab\\]cde]",[]))), - <<":thing">> = iolist_to_binary(join(re:split("]thing","^[ab\\]cde]",[trim]))), + 2}]))), + <<":thing">> = iolist_to_binary(join(re:split("bthing","^[ab\\]cde]",[]))), + <<":thing">> = iolist_to_binary(join(re:split("]thing","^[ab\\]cde]",[trim]))), <<":thing">> = iolist_to_binary(join(re:split("]thing","^[ab\\]cde]",[{parts, - 2}]))), - <<":thing">> = iolist_to_binary(join(re:split("]thing","^[ab\\]cde]",[]))), - <<":thing">> = iolist_to_binary(join(re:split("cthing","^[ab\\]cde]",[trim]))), + 2}]))), + <<":thing">> = iolist_to_binary(join(re:split("]thing","^[ab\\]cde]",[]))), + <<":thing">> = iolist_to_binary(join(re:split("cthing","^[ab\\]cde]",[trim]))), <<":thing">> = iolist_to_binary(join(re:split("cthing","^[ab\\]cde]",[{parts, - 2}]))), - <<":thing">> = iolist_to_binary(join(re:split("cthing","^[ab\\]cde]",[]))), - <<":thing">> = iolist_to_binary(join(re:split("dthing","^[ab\\]cde]",[trim]))), + 2}]))), + <<":thing">> = iolist_to_binary(join(re:split("cthing","^[ab\\]cde]",[]))), + <<":thing">> = iolist_to_binary(join(re:split("dthing","^[ab\\]cde]",[trim]))), <<":thing">> = iolist_to_binary(join(re:split("dthing","^[ab\\]cde]",[{parts, - 2}]))), - <<":thing">> = iolist_to_binary(join(re:split("dthing","^[ab\\]cde]",[]))), - <<":thing">> = iolist_to_binary(join(re:split("ething","^[ab\\]cde]",[trim]))), + 2}]))), + <<":thing">> = iolist_to_binary(join(re:split("dthing","^[ab\\]cde]",[]))), + <<":thing">> = iolist_to_binary(join(re:split("ething","^[ab\\]cde]",[trim]))), <<":thing">> = iolist_to_binary(join(re:split("ething","^[ab\\]cde]",[{parts, - 2}]))), - <<":thing">> = iolist_to_binary(join(re:split("ething","^[ab\\]cde]",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[ab\\]cde]",[trim]))), + 2}]))), + <<":thing">> = iolist_to_binary(join(re:split("ething","^[ab\\]cde]",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[ab\\]cde]",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[ab\\]cde]",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[ab\\]cde]",[]))), - <<"fthing">> = iolist_to_binary(join(re:split("fthing","^[ab\\]cde]",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[ab\\]cde]",[]))), + <<"fthing">> = iolist_to_binary(join(re:split("fthing","^[ab\\]cde]",[trim]))), <<"fthing">> = iolist_to_binary(join(re:split("fthing","^[ab\\]cde]",[{parts, - 2}]))), - <<"fthing">> = iolist_to_binary(join(re:split("fthing","^[ab\\]cde]",[]))), - <<"[thing">> = iolist_to_binary(join(re:split("[thing","^[ab\\]cde]",[trim]))), + 2}]))), + <<"fthing">> = iolist_to_binary(join(re:split("fthing","^[ab\\]cde]",[]))), + <<"[thing">> = iolist_to_binary(join(re:split("[thing","^[ab\\]cde]",[trim]))), <<"[thing">> = iolist_to_binary(join(re:split("[thing","^[ab\\]cde]",[{parts, - 2}]))), - <<"[thing">> = iolist_to_binary(join(re:split("[thing","^[ab\\]cde]",[]))), - <<"\\thing">> = iolist_to_binary(join(re:split("\\thing","^[ab\\]cde]",[trim]))), + 2}]))), + <<"[thing">> = iolist_to_binary(join(re:split("[thing","^[ab\\]cde]",[]))), + <<"\\thing">> = iolist_to_binary(join(re:split("\\thing","^[ab\\]cde]",[trim]))), <<"\\thing">> = iolist_to_binary(join(re:split("\\thing","^[ab\\]cde]",[{parts, - 2}]))), - <<"\\thing">> = iolist_to_binary(join(re:split("\\thing","^[ab\\]cde]",[]))), - <<":thing">> = iolist_to_binary(join(re:split("]thing","^[]cde]",[trim]))), + 2}]))), + <<"\\thing">> = iolist_to_binary(join(re:split("\\thing","^[ab\\]cde]",[]))), + <<":thing">> = iolist_to_binary(join(re:split("]thing","^[]cde]",[trim]))), <<":thing">> = iolist_to_binary(join(re:split("]thing","^[]cde]",[{parts, - 2}]))), - <<":thing">> = iolist_to_binary(join(re:split("]thing","^[]cde]",[]))), - <<":thing">> = iolist_to_binary(join(re:split("cthing","^[]cde]",[trim]))), + 2}]))), + <<":thing">> = iolist_to_binary(join(re:split("]thing","^[]cde]",[]))), + <<":thing">> = iolist_to_binary(join(re:split("cthing","^[]cde]",[trim]))), <<":thing">> = iolist_to_binary(join(re:split("cthing","^[]cde]",[{parts, - 2}]))), - <<":thing">> = iolist_to_binary(join(re:split("cthing","^[]cde]",[]))), - <<":thing">> = iolist_to_binary(join(re:split("dthing","^[]cde]",[trim]))), + 2}]))), + <<":thing">> = iolist_to_binary(join(re:split("cthing","^[]cde]",[]))), + <<":thing">> = iolist_to_binary(join(re:split("dthing","^[]cde]",[trim]))), <<":thing">> = iolist_to_binary(join(re:split("dthing","^[]cde]",[{parts, - 2}]))), - <<":thing">> = iolist_to_binary(join(re:split("dthing","^[]cde]",[]))), - <<":thing">> = iolist_to_binary(join(re:split("ething","^[]cde]",[trim]))), + 2}]))), + <<":thing">> = iolist_to_binary(join(re:split("dthing","^[]cde]",[]))), + <<":thing">> = iolist_to_binary(join(re:split("ething","^[]cde]",[trim]))), <<":thing">> = iolist_to_binary(join(re:split("ething","^[]cde]",[{parts, - 2}]))), - <<":thing">> = iolist_to_binary(join(re:split("ething","^[]cde]",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[]cde]",[trim]))), + 2}]))), + <<":thing">> = iolist_to_binary(join(re:split("ething","^[]cde]",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[]cde]",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[]cde]",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[]cde]",[]))), - <<"athing">> = iolist_to_binary(join(re:split("athing","^[]cde]",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[]cde]",[]))), + <<"athing">> = iolist_to_binary(join(re:split("athing","^[]cde]",[trim]))), <<"athing">> = iolist_to_binary(join(re:split("athing","^[]cde]",[{parts, - 2}]))), - <<"athing">> = iolist_to_binary(join(re:split("athing","^[]cde]",[]))), - <<"fthing">> = iolist_to_binary(join(re:split("fthing","^[]cde]",[trim]))), + 2}]))), + <<"athing">> = iolist_to_binary(join(re:split("athing","^[]cde]",[]))), + <<"fthing">> = iolist_to_binary(join(re:split("fthing","^[]cde]",[trim]))), <<"fthing">> = iolist_to_binary(join(re:split("fthing","^[]cde]",[{parts, - 2}]))), - <<"fthing">> = iolist_to_binary(join(re:split("fthing","^[]cde]",[]))), - <<":thing">> = iolist_to_binary(join(re:split("fthing","^[^ab\\]cde]",[trim]))), + 2}]))), + <<"fthing">> = iolist_to_binary(join(re:split("fthing","^[]cde]",[]))), + <<":thing">> = iolist_to_binary(join(re:split("fthing","^[^ab\\]cde]",[trim]))), <<":thing">> = iolist_to_binary(join(re:split("fthing","^[^ab\\]cde]",[{parts, - 2}]))), - <<":thing">> = iolist_to_binary(join(re:split("fthing","^[^ab\\]cde]",[]))), - <<":thing">> = iolist_to_binary(join(re:split("[thing","^[^ab\\]cde]",[trim]))), + 2}]))), + <<":thing">> = iolist_to_binary(join(re:split("fthing","^[^ab\\]cde]",[]))), + <<":thing">> = iolist_to_binary(join(re:split("[thing","^[^ab\\]cde]",[trim]))), <<":thing">> = iolist_to_binary(join(re:split("[thing","^[^ab\\]cde]",[{parts, - 2}]))), - <<":thing">> = iolist_to_binary(join(re:split("[thing","^[^ab\\]cde]",[]))), - <<":thing">> = iolist_to_binary(join(re:split("\\thing","^[^ab\\]cde]",[trim]))), + 2}]))), + <<":thing">> = iolist_to_binary(join(re:split("[thing","^[^ab\\]cde]",[]))), + <<":thing">> = iolist_to_binary(join(re:split("\\thing","^[^ab\\]cde]",[trim]))), <<":thing">> = iolist_to_binary(join(re:split("\\thing","^[^ab\\]cde]",[{parts, - 2}]))), - <<":thing">> = iolist_to_binary(join(re:split("\\thing","^[^ab\\]cde]",[]))), - <<":** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[^ab\\]cde]",[trim]))), + 2}]))), + <<":thing">> = iolist_to_binary(join(re:split("\\thing","^[^ab\\]cde]",[]))), + <<":** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[^ab\\]cde]",[trim]))), <<":** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[^ab\\]cde]",[{parts, - 2}]))), - <<":** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[^ab\\]cde]",[]))), - <<"athing">> = iolist_to_binary(join(re:split("athing","^[^ab\\]cde]",[trim]))), + 2}]))), + <<":** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[^ab\\]cde]",[]))), + <<"athing">> = iolist_to_binary(join(re:split("athing","^[^ab\\]cde]",[trim]))), <<"athing">> = iolist_to_binary(join(re:split("athing","^[^ab\\]cde]",[{parts, - 2}]))), - <<"athing">> = iolist_to_binary(join(re:split("athing","^[^ab\\]cde]",[]))), - <<"bthing">> = iolist_to_binary(join(re:split("bthing","^[^ab\\]cde]",[trim]))), + 2}]))), + <<"athing">> = iolist_to_binary(join(re:split("athing","^[^ab\\]cde]",[]))), + <<"bthing">> = iolist_to_binary(join(re:split("bthing","^[^ab\\]cde]",[trim]))), <<"bthing">> = iolist_to_binary(join(re:split("bthing","^[^ab\\]cde]",[{parts, - 2}]))), - <<"bthing">> = iolist_to_binary(join(re:split("bthing","^[^ab\\]cde]",[]))), - <<"]thing">> = iolist_to_binary(join(re:split("]thing","^[^ab\\]cde]",[trim]))), + 2}]))), + <<"bthing">> = iolist_to_binary(join(re:split("bthing","^[^ab\\]cde]",[]))), + <<"]thing">> = iolist_to_binary(join(re:split("]thing","^[^ab\\]cde]",[trim]))), <<"]thing">> = iolist_to_binary(join(re:split("]thing","^[^ab\\]cde]",[{parts, - 2}]))), - <<"]thing">> = iolist_to_binary(join(re:split("]thing","^[^ab\\]cde]",[]))), - <<"cthing">> = iolist_to_binary(join(re:split("cthing","^[^ab\\]cde]",[trim]))), + 2}]))), + <<"]thing">> = iolist_to_binary(join(re:split("]thing","^[^ab\\]cde]",[]))), + <<"cthing">> = iolist_to_binary(join(re:split("cthing","^[^ab\\]cde]",[trim]))), <<"cthing">> = iolist_to_binary(join(re:split("cthing","^[^ab\\]cde]",[{parts, - 2}]))), - <<"cthing">> = iolist_to_binary(join(re:split("cthing","^[^ab\\]cde]",[]))), - <<"dthing">> = iolist_to_binary(join(re:split("dthing","^[^ab\\]cde]",[trim]))), + 2}]))), + <<"cthing">> = iolist_to_binary(join(re:split("cthing","^[^ab\\]cde]",[]))), + <<"dthing">> = iolist_to_binary(join(re:split("dthing","^[^ab\\]cde]",[trim]))), <<"dthing">> = iolist_to_binary(join(re:split("dthing","^[^ab\\]cde]",[{parts, - 2}]))), - <<"dthing">> = iolist_to_binary(join(re:split("dthing","^[^ab\\]cde]",[]))), - <<"ething">> = iolist_to_binary(join(re:split("ething","^[^ab\\]cde]",[trim]))), + 2}]))), + <<"dthing">> = iolist_to_binary(join(re:split("dthing","^[^ab\\]cde]",[]))), + <<"ething">> = iolist_to_binary(join(re:split("ething","^[^ab\\]cde]",[trim]))), <<"ething">> = iolist_to_binary(join(re:split("ething","^[^ab\\]cde]",[{parts, - 2}]))), - <<"ething">> = iolist_to_binary(join(re:split("ething","^[^ab\\]cde]",[]))), - <<":thing">> = iolist_to_binary(join(re:split("athing","^[^]cde]",[trim]))), + 2}]))), + <<"ething">> = iolist_to_binary(join(re:split("ething","^[^ab\\]cde]",[]))), + <<":thing">> = iolist_to_binary(join(re:split("athing","^[^]cde]",[trim]))), <<":thing">> = iolist_to_binary(join(re:split("athing","^[^]cde]",[{parts, - 2}]))), - <<":thing">> = iolist_to_binary(join(re:split("athing","^[^]cde]",[]))), - <<":thing">> = iolist_to_binary(join(re:split("fthing","^[^]cde]",[trim]))), + 2}]))), + <<":thing">> = iolist_to_binary(join(re:split("athing","^[^]cde]",[]))), + <<":thing">> = iolist_to_binary(join(re:split("fthing","^[^]cde]",[trim]))), <<":thing">> = iolist_to_binary(join(re:split("fthing","^[^]cde]",[{parts, - 2}]))), - <<":thing">> = iolist_to_binary(join(re:split("fthing","^[^]cde]",[]))), - <<":** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[^]cde]",[trim]))), + 2}]))), + <<":thing">> = iolist_to_binary(join(re:split("fthing","^[^]cde]",[]))), + <<":** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[^]cde]",[trim]))), <<":** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[^]cde]",[{parts, - 2}]))), - <<":** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[^]cde]",[]))), - <<"]thing">> = iolist_to_binary(join(re:split("]thing","^[^]cde]",[trim]))), + 2}]))), + <<":** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[^]cde]",[]))), + <<"]thing">> = iolist_to_binary(join(re:split("]thing","^[^]cde]",[trim]))), <<"]thing">> = iolist_to_binary(join(re:split("]thing","^[^]cde]",[{parts, - 2}]))), - <<"]thing">> = iolist_to_binary(join(re:split("]thing","^[^]cde]",[]))), - <<"cthing">> = iolist_to_binary(join(re:split("cthing","^[^]cde]",[trim]))), + 2}]))), + <<"]thing">> = iolist_to_binary(join(re:split("]thing","^[^]cde]",[]))), + <<"cthing">> = iolist_to_binary(join(re:split("cthing","^[^]cde]",[trim]))), <<"cthing">> = iolist_to_binary(join(re:split("cthing","^[^]cde]",[{parts, - 2}]))), - <<"cthing">> = iolist_to_binary(join(re:split("cthing","^[^]cde]",[]))), - <<"dthing">> = iolist_to_binary(join(re:split("dthing","^[^]cde]",[trim]))), + 2}]))), + <<"cthing">> = iolist_to_binary(join(re:split("cthing","^[^]cde]",[]))), + <<"dthing">> = iolist_to_binary(join(re:split("dthing","^[^]cde]",[trim]))), <<"dthing">> = iolist_to_binary(join(re:split("dthing","^[^]cde]",[{parts, - 2}]))), - <<"dthing">> = iolist_to_binary(join(re:split("dthing","^[^]cde]",[]))), - <<"ething">> = iolist_to_binary(join(re:split("ething","^[^]cde]",[trim]))), + 2}]))), + <<"dthing">> = iolist_to_binary(join(re:split("dthing","^[^]cde]",[]))), + <<"ething">> = iolist_to_binary(join(re:split("ething","^[^]cde]",[trim]))), <<"ething">> = iolist_to_binary(join(re:split("ething","^[^]cde]",[{parts, - 2}]))), - <<"ething">> = iolist_to_binary(join(re:split("ething","^[^]cde]",[]))), - <<"">> = iolist_to_binary(join(re:split("0","^[0-9]+$",[trim]))), + 2}]))), + <<"ething">> = iolist_to_binary(join(re:split("ething","^[^]cde]",[]))), + <<"">> = iolist_to_binary(join(re:split("0","^[0-9]+$",[trim]))), <<":">> = iolist_to_binary(join(re:split("0","^[0-9]+$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("0","^[0-9]+$",[]))), - <<"">> = iolist_to_binary(join(re:split("1","^[0-9]+$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("0","^[0-9]+$",[]))), + <<"">> = iolist_to_binary(join(re:split("1","^[0-9]+$",[trim]))), <<":">> = iolist_to_binary(join(re:split("1","^[0-9]+$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("1","^[0-9]+$",[]))), - <<"">> = iolist_to_binary(join(re:split("2","^[0-9]+$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("1","^[0-9]+$",[]))), + <<"">> = iolist_to_binary(join(re:split("2","^[0-9]+$",[trim]))), <<":">> = iolist_to_binary(join(re:split("2","^[0-9]+$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("2","^[0-9]+$",[]))), - <<"">> = iolist_to_binary(join(re:split("3","^[0-9]+$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("2","^[0-9]+$",[]))), + <<"">> = iolist_to_binary(join(re:split("3","^[0-9]+$",[trim]))), <<":">> = iolist_to_binary(join(re:split("3","^[0-9]+$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("3","^[0-9]+$",[]))), - <<"">> = iolist_to_binary(join(re:split("4","^[0-9]+$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("3","^[0-9]+$",[]))), + <<"">> = iolist_to_binary(join(re:split("4","^[0-9]+$",[trim]))), <<":">> = iolist_to_binary(join(re:split("4","^[0-9]+$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("4","^[0-9]+$",[]))), - <<"">> = iolist_to_binary(join(re:split("5","^[0-9]+$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("4","^[0-9]+$",[]))), + <<"">> = iolist_to_binary(join(re:split("5","^[0-9]+$",[trim]))), <<":">> = iolist_to_binary(join(re:split("5","^[0-9]+$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("5","^[0-9]+$",[]))), - <<"">> = iolist_to_binary(join(re:split("6","^[0-9]+$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("5","^[0-9]+$",[]))), + <<"">> = iolist_to_binary(join(re:split("6","^[0-9]+$",[trim]))), <<":">> = iolist_to_binary(join(re:split("6","^[0-9]+$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("6","^[0-9]+$",[]))), - <<"">> = iolist_to_binary(join(re:split("7","^[0-9]+$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("6","^[0-9]+$",[]))), + <<"">> = iolist_to_binary(join(re:split("7","^[0-9]+$",[trim]))), <<":">> = iolist_to_binary(join(re:split("7","^[0-9]+$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("7","^[0-9]+$",[]))), - <<"">> = iolist_to_binary(join(re:split("8","^[0-9]+$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("7","^[0-9]+$",[]))), + <<"">> = iolist_to_binary(join(re:split("8","^[0-9]+$",[trim]))), <<":">> = iolist_to_binary(join(re:split("8","^[0-9]+$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("8","^[0-9]+$",[]))), - <<"">> = iolist_to_binary(join(re:split("9","^[0-9]+$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("8","^[0-9]+$",[]))), + <<"">> = iolist_to_binary(join(re:split("9","^[0-9]+$",[trim]))), <<":">> = iolist_to_binary(join(re:split("9","^[0-9]+$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("9","^[0-9]+$",[]))), - <<"">> = iolist_to_binary(join(re:split("10","^[0-9]+$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("9","^[0-9]+$",[]))), + <<"">> = iolist_to_binary(join(re:split("10","^[0-9]+$",[trim]))), <<":">> = iolist_to_binary(join(re:split("10","^[0-9]+$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("10","^[0-9]+$",[]))), - <<"">> = iolist_to_binary(join(re:split("100","^[0-9]+$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("10","^[0-9]+$",[]))), + <<"">> = iolist_to_binary(join(re:split("100","^[0-9]+$",[trim]))), <<":">> = iolist_to_binary(join(re:split("100","^[0-9]+$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("100","^[0-9]+$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[0-9]+$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("100","^[0-9]+$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[0-9]+$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[0-9]+$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[0-9]+$",[]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","^[0-9]+$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[0-9]+$",[]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","^[0-9]+$",[trim]))), <<"abc">> = iolist_to_binary(join(re:split("abc","^[0-9]+$",[{parts, - 2}]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","^[0-9]+$",[]))), - <<"">> = iolist_to_binary(join(re:split("enter","^.*nter",[trim]))), + 2}]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","^[0-9]+$",[]))), + <<"">> = iolist_to_binary(join(re:split("enter","^.*nter",[trim]))), <<":">> = iolist_to_binary(join(re:split("enter","^.*nter",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("enter","^.*nter",[]))), - <<"">> = iolist_to_binary(join(re:split("inter","^.*nter",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("enter","^.*nter",[]))), + <<"">> = iolist_to_binary(join(re:split("inter","^.*nter",[trim]))), <<":">> = iolist_to_binary(join(re:split("inter","^.*nter",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("inter","^.*nter",[]))), - <<"">> = iolist_to_binary(join(re:split("uponter","^.*nter",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("inter","^.*nter",[]))), + <<"">> = iolist_to_binary(join(re:split("uponter","^.*nter",[trim]))), <<":">> = iolist_to_binary(join(re:split("uponter","^.*nter",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("uponter","^.*nter",[]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("uponter","^.*nter",[]))), ok. run1() -> - <<"">> = iolist_to_binary(join(re:split("xxx0","^xxx[0-9]+$",[trim]))), + <<"">> = iolist_to_binary(join(re:split("xxx0","^xxx[0-9]+$",[trim]))), <<":">> = iolist_to_binary(join(re:split("xxx0","^xxx[0-9]+$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("xxx0","^xxx[0-9]+$",[]))), - <<"">> = iolist_to_binary(join(re:split("xxx1234","^xxx[0-9]+$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("xxx0","^xxx[0-9]+$",[]))), + <<"">> = iolist_to_binary(join(re:split("xxx1234","^xxx[0-9]+$",[trim]))), <<":">> = iolist_to_binary(join(re:split("xxx1234","^xxx[0-9]+$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("xxx1234","^xxx[0-9]+$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^xxx[0-9]+$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("xxx1234","^xxx[0-9]+$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^xxx[0-9]+$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^xxx[0-9]+$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^xxx[0-9]+$",[]))), - <<"xxx">> = iolist_to_binary(join(re:split("xxx","^xxx[0-9]+$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^xxx[0-9]+$",[]))), + <<"xxx">> = iolist_to_binary(join(re:split("xxx","^xxx[0-9]+$",[trim]))), <<"xxx">> = iolist_to_binary(join(re:split("xxx","^xxx[0-9]+$",[{parts, - 2}]))), - <<"xxx">> = iolist_to_binary(join(re:split("xxx","^xxx[0-9]+$",[]))), - <<"">> = iolist_to_binary(join(re:split("x123","^.+[0-9][0-9][0-9]$",[trim]))), + 2}]))), + <<"xxx">> = iolist_to_binary(join(re:split("xxx","^xxx[0-9]+$",[]))), + <<"">> = iolist_to_binary(join(re:split("x123","^.+[0-9][0-9][0-9]$",[trim]))), <<":">> = iolist_to_binary(join(re:split("x123","^.+[0-9][0-9][0-9]$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("x123","^.+[0-9][0-9][0-9]$",[]))), - <<"">> = iolist_to_binary(join(re:split("xx123","^.+[0-9][0-9][0-9]$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("x123","^.+[0-9][0-9][0-9]$",[]))), + <<"">> = iolist_to_binary(join(re:split("xx123","^.+[0-9][0-9][0-9]$",[trim]))), <<":">> = iolist_to_binary(join(re:split("xx123","^.+[0-9][0-9][0-9]$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("xx123","^.+[0-9][0-9][0-9]$",[]))), - <<"">> = iolist_to_binary(join(re:split("123456","^.+[0-9][0-9][0-9]$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("xx123","^.+[0-9][0-9][0-9]$",[]))), + <<"">> = iolist_to_binary(join(re:split("123456","^.+[0-9][0-9][0-9]$",[trim]))), <<":">> = iolist_to_binary(join(re:split("123456","^.+[0-9][0-9][0-9]$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("123456","^.+[0-9][0-9][0-9]$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^.+[0-9][0-9][0-9]$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("123456","^.+[0-9][0-9][0-9]$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^.+[0-9][0-9][0-9]$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^.+[0-9][0-9][0-9]$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^.+[0-9][0-9][0-9]$",[]))), - <<"123">> = iolist_to_binary(join(re:split("123","^.+[0-9][0-9][0-9]$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^.+[0-9][0-9][0-9]$",[]))), + <<"123">> = iolist_to_binary(join(re:split("123","^.+[0-9][0-9][0-9]$",[trim]))), <<"123">> = iolist_to_binary(join(re:split("123","^.+[0-9][0-9][0-9]$",[{parts, - 2}]))), - <<"123">> = iolist_to_binary(join(re:split("123","^.+[0-9][0-9][0-9]$",[]))), - <<"">> = iolist_to_binary(join(re:split("x1234","^.+[0-9][0-9][0-9]$",[trim]))), + 2}]))), + <<"123">> = iolist_to_binary(join(re:split("123","^.+[0-9][0-9][0-9]$",[]))), + <<"">> = iolist_to_binary(join(re:split("x1234","^.+[0-9][0-9][0-9]$",[trim]))), <<":">> = iolist_to_binary(join(re:split("x1234","^.+[0-9][0-9][0-9]$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("x1234","^.+[0-9][0-9][0-9]$",[]))), - <<"">> = iolist_to_binary(join(re:split("x123","^.+?[0-9][0-9][0-9]$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("x1234","^.+[0-9][0-9][0-9]$",[]))), + <<"">> = iolist_to_binary(join(re:split("x123","^.+?[0-9][0-9][0-9]$",[trim]))), <<":">> = iolist_to_binary(join(re:split("x123","^.+?[0-9][0-9][0-9]$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("x123","^.+?[0-9][0-9][0-9]$",[]))), - <<"">> = iolist_to_binary(join(re:split("xx123","^.+?[0-9][0-9][0-9]$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("x123","^.+?[0-9][0-9][0-9]$",[]))), + <<"">> = iolist_to_binary(join(re:split("xx123","^.+?[0-9][0-9][0-9]$",[trim]))), <<":">> = iolist_to_binary(join(re:split("xx123","^.+?[0-9][0-9][0-9]$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("xx123","^.+?[0-9][0-9][0-9]$",[]))), - <<"">> = iolist_to_binary(join(re:split("123456","^.+?[0-9][0-9][0-9]$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("xx123","^.+?[0-9][0-9][0-9]$",[]))), + <<"">> = iolist_to_binary(join(re:split("123456","^.+?[0-9][0-9][0-9]$",[trim]))), <<":">> = iolist_to_binary(join(re:split("123456","^.+?[0-9][0-9][0-9]$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("123456","^.+?[0-9][0-9][0-9]$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^.+?[0-9][0-9][0-9]$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("123456","^.+?[0-9][0-9][0-9]$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^.+?[0-9][0-9][0-9]$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^.+?[0-9][0-9][0-9]$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^.+?[0-9][0-9][0-9]$",[]))), - <<"123">> = iolist_to_binary(join(re:split("123","^.+?[0-9][0-9][0-9]$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^.+?[0-9][0-9][0-9]$",[]))), + <<"123">> = iolist_to_binary(join(re:split("123","^.+?[0-9][0-9][0-9]$",[trim]))), <<"123">> = iolist_to_binary(join(re:split("123","^.+?[0-9][0-9][0-9]$",[{parts, - 2}]))), - <<"123">> = iolist_to_binary(join(re:split("123","^.+?[0-9][0-9][0-9]$",[]))), - <<"">> = iolist_to_binary(join(re:split("x1234","^.+?[0-9][0-9][0-9]$",[trim]))), + 2}]))), + <<"123">> = iolist_to_binary(join(re:split("123","^.+?[0-9][0-9][0-9]$",[]))), + <<"">> = iolist_to_binary(join(re:split("x1234","^.+?[0-9][0-9][0-9]$",[trim]))), <<":">> = iolist_to_binary(join(re:split("x1234","^.+?[0-9][0-9][0-9]$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("x1234","^.+?[0-9][0-9][0-9]$",[]))), - <<":abc:pqr">> = iolist_to_binary(join(re:split("abc!pqr=apquxz.ixr.zzz.ac.uk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("x1234","^.+?[0-9][0-9][0-9]$",[]))), + <<":abc:pqr">> = iolist_to_binary(join(re:split("abc!pqr=apquxz.ixr.zzz.ac.uk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[trim]))), <<":abc:pqr:">> = iolist_to_binary(join(re:split("abc!pqr=apquxz.ixr.zzz.ac.uk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[{parts, - 2}]))), - <<":abc:pqr:">> = iolist_to_binary(join(re:split("abc!pqr=apquxz.ixr.zzz.ac.uk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[trim]))), + 2}]))), + <<":abc:pqr:">> = iolist_to_binary(join(re:split("abc!pqr=apquxz.ixr.zzz.ac.uk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[]))), - <<"!pqr=apquxz.ixr.zzz.ac.uk">> = iolist_to_binary(join(re:split("!pqr=apquxz.ixr.zzz.ac.uk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[]))), + <<"!pqr=apquxz.ixr.zzz.ac.uk">> = iolist_to_binary(join(re:split("!pqr=apquxz.ixr.zzz.ac.uk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[trim]))), <<"!pqr=apquxz.ixr.zzz.ac.uk">> = iolist_to_binary(join(re:split("!pqr=apquxz.ixr.zzz.ac.uk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[{parts, - 2}]))), - <<"!pqr=apquxz.ixr.zzz.ac.uk">> = iolist_to_binary(join(re:split("!pqr=apquxz.ixr.zzz.ac.uk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[]))), - <<"abc!=apquxz.ixr.zzz.ac.uk">> = iolist_to_binary(join(re:split("abc!=apquxz.ixr.zzz.ac.uk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[trim]))), + 2}]))), + <<"!pqr=apquxz.ixr.zzz.ac.uk">> = iolist_to_binary(join(re:split("!pqr=apquxz.ixr.zzz.ac.uk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[]))), + <<"abc!=apquxz.ixr.zzz.ac.uk">> = iolist_to_binary(join(re:split("abc!=apquxz.ixr.zzz.ac.uk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[trim]))), <<"abc!=apquxz.ixr.zzz.ac.uk">> = iolist_to_binary(join(re:split("abc!=apquxz.ixr.zzz.ac.uk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[{parts, - 2}]))), - <<"abc!=apquxz.ixr.zzz.ac.uk">> = iolist_to_binary(join(re:split("abc!=apquxz.ixr.zzz.ac.uk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[]))), - <<"abc!pqr=apquxz:ixr.zzz.ac.uk">> = iolist_to_binary(join(re:split("abc!pqr=apquxz:ixr.zzz.ac.uk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[trim]))), + 2}]))), + <<"abc!=apquxz.ixr.zzz.ac.uk">> = iolist_to_binary(join(re:split("abc!=apquxz.ixr.zzz.ac.uk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[]))), + <<"abc!pqr=apquxz:ixr.zzz.ac.uk">> = iolist_to_binary(join(re:split("abc!pqr=apquxz:ixr.zzz.ac.uk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[trim]))), <<"abc!pqr=apquxz:ixr.zzz.ac.uk">> = iolist_to_binary(join(re:split("abc!pqr=apquxz:ixr.zzz.ac.uk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[{parts, - 2}]))), - <<"abc!pqr=apquxz:ixr.zzz.ac.uk">> = iolist_to_binary(join(re:split("abc!pqr=apquxz:ixr.zzz.ac.uk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[]))), - <<"abc!pqr=apquxz.ixr.zzz.ac.ukk">> = iolist_to_binary(join(re:split("abc!pqr=apquxz.ixr.zzz.ac.ukk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[trim]))), + 2}]))), + <<"abc!pqr=apquxz:ixr.zzz.ac.uk">> = iolist_to_binary(join(re:split("abc!pqr=apquxz:ixr.zzz.ac.uk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[]))), + <<"abc!pqr=apquxz.ixr.zzz.ac.ukk">> = iolist_to_binary(join(re:split("abc!pqr=apquxz.ixr.zzz.ac.ukk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[trim]))), <<"abc!pqr=apquxz.ixr.zzz.ac.ukk">> = iolist_to_binary(join(re:split("abc!pqr=apquxz.ixr.zzz.ac.ukk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[{parts, - 2}]))), - <<"abc!pqr=apquxz.ixr.zzz.ac.ukk">> = iolist_to_binary(join(re:split("abc!pqr=apquxz.ixr.zzz.ac.ukk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[]))), - <<"Well, we need a colon: somewhere">> = iolist_to_binary(join(re:split("Well, we need a colon: somewhere",":",[trim]))), + 2}]))), + <<"abc!pqr=apquxz.ixr.zzz.ac.ukk">> = iolist_to_binary(join(re:split("abc!pqr=apquxz.ixr.zzz.ac.ukk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[]))), + <<"Well, we need a colon: somewhere">> = iolist_to_binary(join(re:split("Well, we need a colon: somewhere",":",[trim]))), <<"Well, we need a colon: somewhere">> = iolist_to_binary(join(re:split("Well, we need a colon: somewhere",":",[{parts, - 2}]))), - <<"Well, we need a colon: somewhere">> = iolist_to_binary(join(re:split("Well, we need a colon: somewhere",":",[]))), - <<"*** Fail if we don't">> = iolist_to_binary(join(re:split("*** Fail if we don't",":",[trim]))), + 2}]))), + <<"Well, we need a colon: somewhere">> = iolist_to_binary(join(re:split("Well, we need a colon: somewhere",":",[]))), + <<"*** Fail if we don't">> = iolist_to_binary(join(re:split("*** Fail if we don't",":",[trim]))), <<"*** Fail if we don't">> = iolist_to_binary(join(re:split("*** Fail if we don't",":",[{parts, - 2}]))), - <<"*** Fail if we don't">> = iolist_to_binary(join(re:split("*** Fail if we don't",":",[]))), + 2}]))), + <<"*** Fail if we don't">> = iolist_to_binary(join(re:split("*** Fail if we don't",":",[]))), <<":0abc">> = iolist_to_binary(join(re:split("0abc","([\\da-f:]+)$",[caseless, - trim]))), + trim]))), <<":0abc:">> = iolist_to_binary(join(re:split("0abc","([\\da-f:]+)$",[caseless, {parts, - 2}]))), - <<":0abc:">> = iolist_to_binary(join(re:split("0abc","([\\da-f:]+)$",[caseless]))), + 2}]))), + <<":0abc:">> = iolist_to_binary(join(re:split("0abc","([\\da-f:]+)$",[caseless]))), <<":abc">> = iolist_to_binary(join(re:split("abc","([\\da-f:]+)$",[caseless, - trim]))), + trim]))), <<":abc:">> = iolist_to_binary(join(re:split("abc","([\\da-f:]+)$",[caseless, {parts, - 2}]))), - <<":abc:">> = iolist_to_binary(join(re:split("abc","([\\da-f:]+)$",[caseless]))), + 2}]))), + <<":abc:">> = iolist_to_binary(join(re:split("abc","([\\da-f:]+)$",[caseless]))), <<":fed">> = iolist_to_binary(join(re:split("fed","([\\da-f:]+)$",[caseless, - trim]))), + trim]))), <<":fed:">> = iolist_to_binary(join(re:split("fed","([\\da-f:]+)$",[caseless, {parts, - 2}]))), - <<":fed:">> = iolist_to_binary(join(re:split("fed","([\\da-f:]+)$",[caseless]))), + 2}]))), + <<":fed:">> = iolist_to_binary(join(re:split("fed","([\\da-f:]+)$",[caseless]))), <<":E">> = iolist_to_binary(join(re:split("E","([\\da-f:]+)$",[caseless, - trim]))), + trim]))), <<":E:">> = iolist_to_binary(join(re:split("E","([\\da-f:]+)$",[caseless, {parts, - 2}]))), - <<":E:">> = iolist_to_binary(join(re:split("E","([\\da-f:]+)$",[caseless]))), + 2}]))), + <<":E:">> = iolist_to_binary(join(re:split("E","([\\da-f:]+)$",[caseless]))), <<":::">> = iolist_to_binary(join(re:split("::","([\\da-f:]+)$",[caseless, - trim]))), + trim]))), <<"::::">> = iolist_to_binary(join(re:split("::","([\\da-f:]+)$",[caseless, {parts, - 2}]))), - <<"::::">> = iolist_to_binary(join(re:split("::","([\\da-f:]+)$",[caseless]))), + 2}]))), + <<"::::">> = iolist_to_binary(join(re:split("::","([\\da-f:]+)$",[caseless]))), <<":5f03:12C0::932e">> = iolist_to_binary(join(re:split("5f03:12C0::932e","([\\da-f:]+)$",[caseless, - trim]))), + trim]))), <<":5f03:12C0::932e:">> = iolist_to_binary(join(re:split("5f03:12C0::932e","([\\da-f:]+)$",[caseless, {parts, - 2}]))), - <<":5f03:12C0::932e:">> = iolist_to_binary(join(re:split("5f03:12C0::932e","([\\da-f:]+)$",[caseless]))), + 2}]))), + <<":5f03:12C0::932e:">> = iolist_to_binary(join(re:split("5f03:12C0::932e","([\\da-f:]+)$",[caseless]))), <<"fed :def">> = iolist_to_binary(join(re:split("fed def","([\\da-f:]+)$",[caseless, - trim]))), + trim]))), <<"fed :def:">> = iolist_to_binary(join(re:split("fed def","([\\da-f:]+)$",[caseless, {parts, - 2}]))), - <<"fed :def:">> = iolist_to_binary(join(re:split("fed def","([\\da-f:]+)$",[caseless]))), + 2}]))), + <<"fed :def:">> = iolist_to_binary(join(re:split("fed def","([\\da-f:]+)$",[caseless]))), <<"Any old stu:ff">> = iolist_to_binary(join(re:split("Any old stuff","([\\da-f:]+)$",[caseless, - trim]))), + trim]))), <<"Any old stu:ff:">> = iolist_to_binary(join(re:split("Any old stuff","([\\da-f:]+)$",[caseless, {parts, - 2}]))), - <<"Any old stu:ff:">> = iolist_to_binary(join(re:split("Any old stuff","([\\da-f:]+)$",[caseless]))), + 2}]))), + <<"Any old stu:ff:">> = iolist_to_binary(join(re:split("Any old stuff","([\\da-f:]+)$",[caseless]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","([\\da-f:]+)$",[caseless, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","([\\da-f:]+)$",[caseless, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","([\\da-f:]+)$",[caseless]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","([\\da-f:]+)$",[caseless]))), <<"0zzz">> = iolist_to_binary(join(re:split("0zzz","([\\da-f:]+)$",[caseless, - trim]))), + trim]))), <<"0zzz">> = iolist_to_binary(join(re:split("0zzz","([\\da-f:]+)$",[caseless, {parts, - 2}]))), - <<"0zzz">> = iolist_to_binary(join(re:split("0zzz","([\\da-f:]+)$",[caseless]))), + 2}]))), + <<"0zzz">> = iolist_to_binary(join(re:split("0zzz","([\\da-f:]+)$",[caseless]))), <<"gzzz">> = iolist_to_binary(join(re:split("gzzz","([\\da-f:]+)$",[caseless, - trim]))), + trim]))), <<"gzzz">> = iolist_to_binary(join(re:split("gzzz","([\\da-f:]+)$",[caseless, {parts, - 2}]))), - <<"gzzz">> = iolist_to_binary(join(re:split("gzzz","([\\da-f:]+)$",[caseless]))), + 2}]))), + <<"gzzz">> = iolist_to_binary(join(re:split("gzzz","([\\da-f:]+)$",[caseless]))), <<"fed ">> = iolist_to_binary(join(re:split("fed ","([\\da-f:]+)$",[caseless, - trim]))), + trim]))), <<"fed ">> = iolist_to_binary(join(re:split("fed ","([\\da-f:]+)$",[caseless, {parts, - 2}]))), - <<"fed ">> = iolist_to_binary(join(re:split("fed ","([\\da-f:]+)$",[caseless]))), + 2}]))), + <<"fed ">> = iolist_to_binary(join(re:split("fed ","([\\da-f:]+)$",[caseless]))), <<"Any old rubbish">> = iolist_to_binary(join(re:split("Any old rubbish","([\\da-f:]+)$",[caseless, - trim]))), + trim]))), <<"Any old rubbish">> = iolist_to_binary(join(re:split("Any old rubbish","([\\da-f:]+)$",[caseless, {parts, - 2}]))), - <<"Any old rubbish">> = iolist_to_binary(join(re:split("Any old rubbish","([\\da-f:]+)$",[caseless]))), - <<":1:2:3">> = iolist_to_binary(join(re:split(".1.2.3","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[trim]))), + 2}]))), + <<"Any old rubbish">> = iolist_to_binary(join(re:split("Any old rubbish","([\\da-f:]+)$",[caseless]))), + <<":1:2:3">> = iolist_to_binary(join(re:split(".1.2.3","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[trim]))), <<":1:2:3:">> = iolist_to_binary(join(re:split(".1.2.3","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[{parts, - 2}]))), - <<":1:2:3:">> = iolist_to_binary(join(re:split(".1.2.3","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[]))), - <<":12:123:0">> = iolist_to_binary(join(re:split("A.12.123.0","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[trim]))), + 2}]))), + <<":1:2:3:">> = iolist_to_binary(join(re:split(".1.2.3","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[]))), + <<":12:123:0">> = iolist_to_binary(join(re:split("A.12.123.0","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[trim]))), <<":12:123:0:">> = iolist_to_binary(join(re:split("A.12.123.0","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[{parts, - 2}]))), - <<":12:123:0:">> = iolist_to_binary(join(re:split("A.12.123.0","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[trim]))), + 2}]))), + <<":12:123:0:">> = iolist_to_binary(join(re:split("A.12.123.0","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[]))), - <<".1.2.3333">> = iolist_to_binary(join(re:split(".1.2.3333","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[]))), + <<".1.2.3333">> = iolist_to_binary(join(re:split(".1.2.3333","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[trim]))), <<".1.2.3333">> = iolist_to_binary(join(re:split(".1.2.3333","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[{parts, - 2}]))), - <<".1.2.3333">> = iolist_to_binary(join(re:split(".1.2.3333","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[]))), - <<"1.2.3">> = iolist_to_binary(join(re:split("1.2.3","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[trim]))), + 2}]))), + <<".1.2.3333">> = iolist_to_binary(join(re:split(".1.2.3333","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[]))), + <<"1.2.3">> = iolist_to_binary(join(re:split("1.2.3","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[trim]))), <<"1.2.3">> = iolist_to_binary(join(re:split("1.2.3","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[{parts, - 2}]))), - <<"1.2.3">> = iolist_to_binary(join(re:split("1.2.3","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[]))), - <<"1234.2.3">> = iolist_to_binary(join(re:split("1234.2.3","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[trim]))), + 2}]))), + <<"1.2.3">> = iolist_to_binary(join(re:split("1.2.3","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[]))), + <<"1234.2.3">> = iolist_to_binary(join(re:split("1234.2.3","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[trim]))), <<"1234.2.3">> = iolist_to_binary(join(re:split("1234.2.3","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[{parts, - 2}]))), - <<"1234.2.3">> = iolist_to_binary(join(re:split("1234.2.3","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[]))), - <<":1:non-sp1:non-sp2">> = iolist_to_binary(join(re:split("1 IN SOA non-sp1 non-sp2(","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[trim]))), + 2}]))), + <<"1234.2.3">> = iolist_to_binary(join(re:split("1234.2.3","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[]))), + <<":1:non-sp1:non-sp2">> = iolist_to_binary(join(re:split("1 IN SOA non-sp1 non-sp2(","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[trim]))), <<":1:non-sp1:non-sp2:">> = iolist_to_binary(join(re:split("1 IN SOA non-sp1 non-sp2(","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[{parts, - 2}]))), - <<":1:non-sp1:non-sp2:">> = iolist_to_binary(join(re:split("1 IN SOA non-sp1 non-sp2(","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[]))), - <<":1:non-sp1:non-sp2">> = iolist_to_binary(join(re:split("1 IN SOA non-sp1 non-sp2 (","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[trim]))), + 2}]))), + <<":1:non-sp1:non-sp2:">> = iolist_to_binary(join(re:split("1 IN SOA non-sp1 non-sp2(","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[]))), + <<":1:non-sp1:non-sp2">> = iolist_to_binary(join(re:split("1 IN SOA non-sp1 non-sp2 (","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[trim]))), <<":1:non-sp1:non-sp2:">> = iolist_to_binary(join(re:split("1 IN SOA non-sp1 non-sp2 (","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[{parts, - 2}]))), - <<":1:non-sp1:non-sp2:">> = iolist_to_binary(join(re:split("1 IN SOA non-sp1 non-sp2 (","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[trim]))), + 2}]))), + <<":1:non-sp1:non-sp2:">> = iolist_to_binary(join(re:split("1 IN SOA non-sp1 non-sp2 (","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[]))), - <<"1IN SOA non-sp1 non-sp2(">> = iolist_to_binary(join(re:split("1IN SOA non-sp1 non-sp2(","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[]))), + <<"1IN SOA non-sp1 non-sp2(">> = iolist_to_binary(join(re:split("1IN SOA non-sp1 non-sp2(","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[trim]))), <<"1IN SOA non-sp1 non-sp2(">> = iolist_to_binary(join(re:split("1IN SOA non-sp1 non-sp2(","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[{parts, - 2}]))), - <<"1IN SOA non-sp1 non-sp2(">> = iolist_to_binary(join(re:split("1IN SOA non-sp1 non-sp2(","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[]))), - <<"">> = iolist_to_binary(join(re:split("a.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[trim]))), + 2}]))), + <<"1IN SOA non-sp1 non-sp2(">> = iolist_to_binary(join(re:split("1IN SOA non-sp1 non-sp2(","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[]))), + <<"">> = iolist_to_binary(join(re:split("a.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[trim]))), <<"::">> = iolist_to_binary(join(re:split("a.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("a.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[]))), - <<"">> = iolist_to_binary(join(re:split("Z.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("a.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[]))), + <<"">> = iolist_to_binary(join(re:split("Z.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[trim]))), <<"::">> = iolist_to_binary(join(re:split("Z.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("Z.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[]))), - <<"">> = iolist_to_binary(join(re:split("2.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("Z.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[]))), + <<"">> = iolist_to_binary(join(re:split("2.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[trim]))), <<"::">> = iolist_to_binary(join(re:split("2.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("2.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[]))), - <<":.pq-r">> = iolist_to_binary(join(re:split("ab-c.pq-r.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("2.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[]))), + <<":.pq-r">> = iolist_to_binary(join(re:split("ab-c.pq-r.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[trim]))), <<":.pq-r:">> = iolist_to_binary(join(re:split("ab-c.pq-r.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[{parts, - 2}]))), - <<":.pq-r:">> = iolist_to_binary(join(re:split("ab-c.pq-r.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[]))), - <<":.uk">> = iolist_to_binary(join(re:split("sxk.zzz.ac.uk.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[trim]))), + 2}]))), + <<":.pq-r:">> = iolist_to_binary(join(re:split("ab-c.pq-r.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[]))), + <<":.uk">> = iolist_to_binary(join(re:split("sxk.zzz.ac.uk.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[trim]))), <<":.uk:">> = iolist_to_binary(join(re:split("sxk.zzz.ac.uk.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[{parts, - 2}]))), - <<":.uk:">> = iolist_to_binary(join(re:split("sxk.zzz.ac.uk.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[]))), - <<":.y-">> = iolist_to_binary(join(re:split("x-.y-.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[trim]))), + 2}]))), + <<":.uk:">> = iolist_to_binary(join(re:split("sxk.zzz.ac.uk.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[]))), + <<":.y-">> = iolist_to_binary(join(re:split("x-.y-.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[trim]))), <<":.y-:">> = iolist_to_binary(join(re:split("x-.y-.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[{parts, - 2}]))), - <<":.y-:">> = iolist_to_binary(join(re:split("x-.y-.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[trim]))), + 2}]))), + <<":.y-:">> = iolist_to_binary(join(re:split("x-.y-.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[]))), - <<"-abc.peq.">> = iolist_to_binary(join(re:split("-abc.peq.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[]))), + <<"-abc.peq.">> = iolist_to_binary(join(re:split("-abc.peq.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[trim]))), <<"-abc.peq.">> = iolist_to_binary(join(re:split("-abc.peq.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[{parts, - 2}]))), - <<"-abc.peq.">> = iolist_to_binary(join(re:split("-abc.peq.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[]))), - <<"">> = iolist_to_binary(join(re:split("*.a","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[trim]))), + 2}]))), + <<"-abc.peq.">> = iolist_to_binary(join(re:split("-abc.peq.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[]))), + <<"">> = iolist_to_binary(join(re:split("*.a","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[trim]))), <<"::::">> = iolist_to_binary(join(re:split("*.a","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[{parts, - 2}]))), - <<"::::">> = iolist_to_binary(join(re:split("*.a","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[]))), - <<":0-a">> = iolist_to_binary(join(re:split("*.b0-a","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[trim]))), + 2}]))), + <<"::::">> = iolist_to_binary(join(re:split("*.a","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[]))), + <<":0-a">> = iolist_to_binary(join(re:split("*.b0-a","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[trim]))), <<":0-a:::">> = iolist_to_binary(join(re:split("*.b0-a","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[{parts, - 2}]))), - <<":0-a:::">> = iolist_to_binary(join(re:split("*.b0-a","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[]))), - <<":3-b:.c">> = iolist_to_binary(join(re:split("*.c3-b.c","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[trim]))), + 2}]))), + <<":0-a:::">> = iolist_to_binary(join(re:split("*.b0-a","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[]))), + <<":3-b:.c">> = iolist_to_binary(join(re:split("*.c3-b.c","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[trim]))), <<":3-b:.c::">> = iolist_to_binary(join(re:split("*.c3-b.c","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[{parts, - 2}]))), - <<":3-b:.c::">> = iolist_to_binary(join(re:split("*.c3-b.c","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[]))), - <<":-a:.b-c:-c">> = iolist_to_binary(join(re:split("*.c-a.b-c","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[trim]))), + 2}]))), + <<":3-b:.c::">> = iolist_to_binary(join(re:split("*.c3-b.c","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[]))), + <<":-a:.b-c:-c">> = iolist_to_binary(join(re:split("*.c-a.b-c","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[trim]))), <<":-a:.b-c:-c:">> = iolist_to_binary(join(re:split("*.c-a.b-c","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[{parts, - 2}]))), - <<":-a:.b-c:-c:">> = iolist_to_binary(join(re:split("*.c-a.b-c","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[trim]))), + 2}]))), + <<":-a:.b-c:-c:">> = iolist_to_binary(join(re:split("*.c-a.b-c","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[]))), - <<"*.0">> = iolist_to_binary(join(re:split("*.0","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[]))), + <<"*.0">> = iolist_to_binary(join(re:split("*.0","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[trim]))), <<"*.0">> = iolist_to_binary(join(re:split("*.0","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[{parts, - 2}]))), - <<"*.0">> = iolist_to_binary(join(re:split("*.0","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[]))), - <<"*.a-">> = iolist_to_binary(join(re:split("*.a-","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[trim]))), + 2}]))), + <<"*.0">> = iolist_to_binary(join(re:split("*.0","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[]))), + <<"*.a-">> = iolist_to_binary(join(re:split("*.a-","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[trim]))), <<"*.a-">> = iolist_to_binary(join(re:split("*.a-","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[{parts, - 2}]))), - <<"*.a-">> = iolist_to_binary(join(re:split("*.a-","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[]))), - <<"*.a-b.c-">> = iolist_to_binary(join(re:split("*.a-b.c-","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[trim]))), + 2}]))), + <<"*.a-">> = iolist_to_binary(join(re:split("*.a-","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[]))), + <<"*.a-b.c-">> = iolist_to_binary(join(re:split("*.a-b.c-","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[trim]))), <<"*.a-b.c-">> = iolist_to_binary(join(re:split("*.a-b.c-","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[{parts, - 2}]))), - <<"*.a-b.c-">> = iolist_to_binary(join(re:split("*.a-b.c-","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[]))), - <<"*.c-a.0-c">> = iolist_to_binary(join(re:split("*.c-a.0-c","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[trim]))), + 2}]))), + <<"*.a-b.c-">> = iolist_to_binary(join(re:split("*.a-b.c-","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[]))), + <<"*.c-a.0-c">> = iolist_to_binary(join(re:split("*.c-a.0-c","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[trim]))), <<"*.c-a.0-c">> = iolist_to_binary(join(re:split("*.c-a.0-c","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[{parts, - 2}]))), - <<"*.c-a.0-c">> = iolist_to_binary(join(re:split("*.c-a.0-c","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[]))), - <<":de:abd:e">> = iolist_to_binary(join(re:split("abde","^(?=ab(de))(abd)(e)",[trim]))), + 2}]))), + <<"*.c-a.0-c">> = iolist_to_binary(join(re:split("*.c-a.0-c","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[]))), + <<":de:abd:e">> = iolist_to_binary(join(re:split("abde","^(?=ab(de))(abd)(e)",[trim]))), <<":de:abd:e:">> = iolist_to_binary(join(re:split("abde","^(?=ab(de))(abd)(e)",[{parts, - 2}]))), - <<":de:abd:e:">> = iolist_to_binary(join(re:split("abde","^(?=ab(de))(abd)(e)",[]))), - <<"::abd:f">> = iolist_to_binary(join(re:split("abdf","^(?!(ab)de|x)(abd)(f)",[trim]))), + 2}]))), + <<":de:abd:e:">> = iolist_to_binary(join(re:split("abde","^(?=ab(de))(abd)(e)",[]))), + <<"::abd:f">> = iolist_to_binary(join(re:split("abdf","^(?!(ab)de|x)(abd)(f)",[trim]))), <<"::abd:f:">> = iolist_to_binary(join(re:split("abdf","^(?!(ab)de|x)(abd)(f)",[{parts, - 2}]))), - <<"::abd:f:">> = iolist_to_binary(join(re:split("abdf","^(?!(ab)de|x)(abd)(f)",[]))), - <<":abcd:cd:ab:cd">> = iolist_to_binary(join(re:split("abcd","^(?=(ab(cd)))(ab)",[trim]))), + 2}]))), + <<"::abd:f:">> = iolist_to_binary(join(re:split("abdf","^(?!(ab)de|x)(abd)(f)",[]))), + <<":abcd:cd:ab:cd">> = iolist_to_binary(join(re:split("abcd","^(?=(ab(cd)))(ab)",[trim]))), <<":abcd:cd:ab:cd">> = iolist_to_binary(join(re:split("abcd","^(?=(ab(cd)))(ab)",[{parts, - 2}]))), - <<":abcd:cd:ab:cd">> = iolist_to_binary(join(re:split("abcd","^(?=(ab(cd)))(ab)",[]))), + 2}]))), + <<":abcd:cd:ab:cd">> = iolist_to_binary(join(re:split("abcd","^(?=(ab(cd)))(ab)",[]))), <<":.d">> = iolist_to_binary(join(re:split("a.b.c.d","^[\\da-f](\\.[\\da-f])*$",[caseless, - trim]))), + trim]))), <<":.d:">> = iolist_to_binary(join(re:split("a.b.c.d","^[\\da-f](\\.[\\da-f])*$",[caseless, {parts, - 2}]))), - <<":.d:">> = iolist_to_binary(join(re:split("a.b.c.d","^[\\da-f](\\.[\\da-f])*$",[caseless]))), + 2}]))), + <<":.d:">> = iolist_to_binary(join(re:split("a.b.c.d","^[\\da-f](\\.[\\da-f])*$",[caseless]))), <<":.D">> = iolist_to_binary(join(re:split("A.B.C.D","^[\\da-f](\\.[\\da-f])*$",[caseless, - trim]))), + trim]))), <<":.D:">> = iolist_to_binary(join(re:split("A.B.C.D","^[\\da-f](\\.[\\da-f])*$",[caseless, {parts, - 2}]))), - <<":.D:">> = iolist_to_binary(join(re:split("A.B.C.D","^[\\da-f](\\.[\\da-f])*$",[caseless]))), + 2}]))), + <<":.D:">> = iolist_to_binary(join(re:split("A.B.C.D","^[\\da-f](\\.[\\da-f])*$",[caseless]))), <<":.C">> = iolist_to_binary(join(re:split("a.b.c.1.2.3.C","^[\\da-f](\\.[\\da-f])*$",[caseless, - trim]))), + trim]))), <<":.C:">> = iolist_to_binary(join(re:split("a.b.c.1.2.3.C","^[\\da-f](\\.[\\da-f])*$",[caseless, {parts, - 2}]))), - <<":.C:">> = iolist_to_binary(join(re:split("a.b.c.1.2.3.C","^[\\da-f](\\.[\\da-f])*$",[caseless]))), - <<"">> = iolist_to_binary(join(re:split("\"1234\"","^\\\".*\\\"\\s*(;.*)?$",[trim]))), + 2}]))), + <<":.C:">> = iolist_to_binary(join(re:split("a.b.c.1.2.3.C","^[\\da-f](\\.[\\da-f])*$",[caseless]))), + <<"">> = iolist_to_binary(join(re:split("\"1234\"","^\\\".*\\\"\\s*(;.*)?$",[trim]))), <<"::">> = iolist_to_binary(join(re:split("\"1234\"","^\\\".*\\\"\\s*(;.*)?$",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("\"1234\"","^\\\".*\\\"\\s*(;.*)?$",[]))), - <<":;">> = iolist_to_binary(join(re:split("\"abcd\" ;","^\\\".*\\\"\\s*(;.*)?$",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("\"1234\"","^\\\".*\\\"\\s*(;.*)?$",[]))), + <<":;">> = iolist_to_binary(join(re:split("\"abcd\" ;","^\\\".*\\\"\\s*(;.*)?$",[trim]))), <<":;:">> = iolist_to_binary(join(re:split("\"abcd\" ;","^\\\".*\\\"\\s*(;.*)?$",[{parts, - 2}]))), - <<":;:">> = iolist_to_binary(join(re:split("\"abcd\" ;","^\\\".*\\\"\\s*(;.*)?$",[]))), - <<":; rhubarb">> = iolist_to_binary(join(re:split("\"\" ; rhubarb","^\\\".*\\\"\\s*(;.*)?$",[trim]))), + 2}]))), + <<":;:">> = iolist_to_binary(join(re:split("\"abcd\" ;","^\\\".*\\\"\\s*(;.*)?$",[]))), + <<":; rhubarb">> = iolist_to_binary(join(re:split("\"\" ; rhubarb","^\\\".*\\\"\\s*(;.*)?$",[trim]))), <<":; rhubarb:">> = iolist_to_binary(join(re:split("\"\" ; rhubarb","^\\\".*\\\"\\s*(;.*)?$",[{parts, - 2}]))), - <<":; rhubarb:">> = iolist_to_binary(join(re:split("\"\" ; rhubarb","^\\\".*\\\"\\s*(;.*)?$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\\".*\\\"\\s*(;.*)?$",[trim]))), + 2}]))), + <<":; rhubarb:">> = iolist_to_binary(join(re:split("\"\" ; rhubarb","^\\\".*\\\"\\s*(;.*)?$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\\".*\\\"\\s*(;.*)?$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\\".*\\\"\\s*(;.*)?$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\\".*\\\"\\s*(;.*)?$",[]))), - <<"\"1234\" : things">> = iolist_to_binary(join(re:split("\"1234\" : things","^\\\".*\\\"\\s*(;.*)?$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\\".*\\\"\\s*(;.*)?$",[]))), + <<"\"1234\" : things">> = iolist_to_binary(join(re:split("\"1234\" : things","^\\\".*\\\"\\s*(;.*)?$",[trim]))), <<"\"1234\" : things">> = iolist_to_binary(join(re:split("\"1234\" : things","^\\\".*\\\"\\s*(;.*)?$",[{parts, - 2}]))), - <<"\"1234\" : things">> = iolist_to_binary(join(re:split("\"1234\" : things","^\\\".*\\\"\\s*(;.*)?$",[]))), - <<"">> = iolist_to_binary(join(re:split("","^$",[trim]))), + 2}]))), + <<"\"1234\" : things">> = iolist_to_binary(join(re:split("\"1234\" : things","^\\\".*\\\"\\s*(;.*)?$",[]))), + <<"">> = iolist_to_binary(join(re:split("","^$",[trim]))), <<"">> = iolist_to_binary(join(re:split("","^$",[{parts, - 2}]))), - <<"">> = iolist_to_binary(join(re:split("","^$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^$",[trim]))), + 2}]))), + <<"">> = iolist_to_binary(join(re:split("","^$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^$",[]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^$",[]))), <<"">> = iolist_to_binary(join(re:split("ab c"," ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[extended, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ab c"," ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[extended, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ab c"," ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[extended]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ab c"," ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[extended]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers"," ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[extended, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers"," ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[extended, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers"," ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[extended]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers"," ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[extended]))), <<"abc">> = iolist_to_binary(join(re:split("abc"," ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[extended, - trim]))), + trim]))), <<"abc">> = iolist_to_binary(join(re:split("abc"," ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[extended, {parts, - 2}]))), - <<"abc">> = iolist_to_binary(join(re:split("abc"," ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[extended]))), + 2}]))), + <<"abc">> = iolist_to_binary(join(re:split("abc"," ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[extended]))), <<"ab cde">> = iolist_to_binary(join(re:split("ab cde"," ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[extended, - trim]))), + trim]))), <<"ab cde">> = iolist_to_binary(join(re:split("ab cde"," ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[extended, {parts, - 2}]))), - <<"ab cde">> = iolist_to_binary(join(re:split("ab cde"," ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[extended]))), - <<"">> = iolist_to_binary(join(re:split("ab c","(?x) ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[trim]))), + 2}]))), + <<"ab cde">> = iolist_to_binary(join(re:split("ab cde"," ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[extended]))), + <<"">> = iolist_to_binary(join(re:split("ab c","(?x) ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[trim]))), <<":">> = iolist_to_binary(join(re:split("ab c","(?x) ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ab c","(?x) ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?x) ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ab c","(?x) ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?x) ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?x) ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?x) ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","(?x) ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?x) ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","(?x) ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[trim]))), <<"abc">> = iolist_to_binary(join(re:split("abc","(?x) ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[{parts, - 2}]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","(?x) ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[]))), - <<"ab cde">> = iolist_to_binary(join(re:split("ab cde","(?x) ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[trim]))), + 2}]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","(?x) ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[]))), + <<"ab cde">> = iolist_to_binary(join(re:split("ab cde","(?x) ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[trim]))), <<"ab cde">> = iolist_to_binary(join(re:split("ab cde","(?x) ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[{parts, - 2}]))), - <<"ab cde">> = iolist_to_binary(join(re:split("ab cde","(?x) ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[]))), + 2}]))), + <<"ab cde">> = iolist_to_binary(join(re:split("ab cde","(?x) ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[]))), <<"">> = iolist_to_binary(join(re:split("a bcd","^ a\\ b[c ]d $",[extended, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("a bcd","^ a\\ b[c ]d $",[extended, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a bcd","^ a\\ b[c ]d $",[extended]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a bcd","^ a\\ b[c ]d $",[extended]))), <<"">> = iolist_to_binary(join(re:split("a b d","^ a\\ b[c ]d $",[extended, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("a b d","^ a\\ b[c ]d $",[extended, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a b d","^ a\\ b[c ]d $",[extended]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a b d","^ a\\ b[c ]d $",[extended]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^ a\\ b[c ]d $",[extended, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^ a\\ b[c ]d $",[extended, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^ a\\ b[c ]d $",[extended]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^ a\\ b[c ]d $",[extended]))), <<"abcd">> = iolist_to_binary(join(re:split("abcd","^ a\\ b[c ]d $",[extended, - trim]))), + trim]))), <<"abcd">> = iolist_to_binary(join(re:split("abcd","^ a\\ b[c ]d $",[extended, {parts, - 2}]))), - <<"abcd">> = iolist_to_binary(join(re:split("abcd","^ a\\ b[c ]d $",[extended]))), + 2}]))), + <<"abcd">> = iolist_to_binary(join(re:split("abcd","^ a\\ b[c ]d $",[extended]))), <<"ab d">> = iolist_to_binary(join(re:split("ab d","^ a\\ b[c ]d $",[extended, - trim]))), + trim]))), <<"ab d">> = iolist_to_binary(join(re:split("ab d","^ a\\ b[c ]d $",[extended, {parts, - 2}]))), - <<"ab d">> = iolist_to_binary(join(re:split("ab d","^ a\\ b[c ]d $",[extended]))), - <<":abc:bc:c:def:ef:f:hij:ij:j:klm:lm:m">> = iolist_to_binary(join(re:split("abcdefhijklm","^(a(b(c)))(d(e(f)))(h(i(j)))(k(l(m)))$",[trim]))), + 2}]))), + <<"ab d">> = iolist_to_binary(join(re:split("ab d","^ a\\ b[c ]d $",[extended]))), + <<":abc:bc:c:def:ef:f:hij:ij:j:klm:lm:m">> = iolist_to_binary(join(re:split("abcdefhijklm","^(a(b(c)))(d(e(f)))(h(i(j)))(k(l(m)))$",[trim]))), <<":abc:bc:c:def:ef:f:hij:ij:j:klm:lm:m:">> = iolist_to_binary(join(re:split("abcdefhijklm","^(a(b(c)))(d(e(f)))(h(i(j)))(k(l(m)))$",[{parts, - 2}]))), - <<":abc:bc:c:def:ef:f:hij:ij:j:klm:lm:m:">> = iolist_to_binary(join(re:split("abcdefhijklm","^(a(b(c)))(d(e(f)))(h(i(j)))(k(l(m)))$",[]))), + 2}]))), + <<":abc:bc:c:def:ef:f:hij:ij:j:klm:lm:m:">> = iolist_to_binary(join(re:split("abcdefhijklm","^(a(b(c)))(d(e(f)))(h(i(j)))(k(l(m)))$",[]))), ok. run2() -> - <<":bc:c:ef:f:ij:j:lm:m">> = iolist_to_binary(join(re:split("abcdefhijklm","^(?:a(b(c)))(?:d(e(f)))(?:h(i(j)))(?:k(l(m)))$",[trim]))), + <<":bc:c:ef:f:ij:j:lm:m">> = iolist_to_binary(join(re:split("abcdefhijklm","^(?:a(b(c)))(?:d(e(f)))(?:h(i(j)))(?:k(l(m)))$",[trim]))), <<":bc:c:ef:f:ij:j:lm:m:">> = iolist_to_binary(join(re:split("abcdefhijklm","^(?:a(b(c)))(?:d(e(f)))(?:h(i(j)))(?:k(l(m)))$",[{parts, - 2}]))), - <<":bc:c:ef:f:ij:j:lm:m:">> = iolist_to_binary(join(re:split("abcdefhijklm","^(?:a(b(c)))(?:d(e(f)))(?:h(i(j)))(?:k(l(m)))$",[]))), + 2}]))), + <<":bc:c:ef:f:ij:j:lm:m:">> = iolist_to_binary(join(re:split("abcdefhijklm","^(?:a(b(c)))(?:d(e(f)))(?:h(i(j)))(?:k(l(m)))$",[]))), <<"">> = iolist_to_binary(join(re:split("a+ Z0+ -","^[\\w][\\W][\\s][\\S][\\d][\\D][\\b][\\n][\\c]][\\022]",[trim]))), +","^[\\w][\\W][\\s][\\S][\\d][\\D][\\b][\\n][\\c]][\\022]",[trim]))), <<":">> = iolist_to_binary(join(re:split("a+ Z0+ ","^[\\w][\\W][\\s][\\S][\\d][\\D][\\b][\\n][\\c]][\\022]",[{parts, - 2}]))), + 2}]))), <<":">> = iolist_to_binary(join(re:split("a+ Z0+ -","^[\\w][\\W][\\s][\\S][\\d][\\D][\\b][\\n][\\c]][\\022]",[]))), - <<"">> = iolist_to_binary(join(re:split(".^$(*+)|{?,?}","^[.^$|()*+?{,}]+",[trim]))), +","^[\\w][\\W][\\s][\\S][\\d][\\D][\\b][\\n][\\c]][\\022]",[]))), + <<"">> = iolist_to_binary(join(re:split(".^$(*+)|{?,?}","^[.^$|()*+?{,}]+",[trim]))), <<":">> = iolist_to_binary(join(re:split(".^$(*+)|{?,?}","^[.^$|()*+?{,}]+",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split(".^$(*+)|{?,?}","^[.^$|()*+?{,}]+",[]))), - <<"">> = iolist_to_binary(join(re:split("z","^a*\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split(".^$(*+)|{?,?}","^[.^$|()*+?{,}]+",[]))), + <<"">> = iolist_to_binary(join(re:split("z","^a*\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("z","^a*\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("z","^a*\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("az","^a*\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("z","^a*\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("az","^a*\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("az","^a*\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("az","^a*\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaz","^a*\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("az","^a*\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaz","^a*\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaz","^a*\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaz","^a*\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("a","^a*\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaz","^a*\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("a","^a*\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("a","^a*\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a","^a*\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("aa","^a*\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a","^a*\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("aa","^a*\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("aa","^a*\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aa","^a*\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaa","^a*\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aa","^a*\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaa","^a*\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaa","^a*\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaa","^a*\\w",[]))), - <<":+">> = iolist_to_binary(join(re:split("a+","^a*\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaa","^a*\\w",[]))), + <<":+">> = iolist_to_binary(join(re:split("a+","^a*\\w",[trim]))), <<":+">> = iolist_to_binary(join(re:split("a+","^a*\\w",[{parts, - 2}]))), - <<":+">> = iolist_to_binary(join(re:split("a+","^a*\\w",[]))), - <<":+">> = iolist_to_binary(join(re:split("aa+","^a*\\w",[trim]))), + 2}]))), + <<":+">> = iolist_to_binary(join(re:split("a+","^a*\\w",[]))), + <<":+">> = iolist_to_binary(join(re:split("aa+","^a*\\w",[trim]))), <<":+">> = iolist_to_binary(join(re:split("aa+","^a*\\w",[{parts, - 2}]))), - <<":+">> = iolist_to_binary(join(re:split("aa+","^a*\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("z","^a*?\\w",[trim]))), + 2}]))), + <<":+">> = iolist_to_binary(join(re:split("aa+","^a*\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("z","^a*?\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("z","^a*?\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("z","^a*?\\w",[]))), - <<":z">> = iolist_to_binary(join(re:split("az","^a*?\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("z","^a*?\\w",[]))), + <<":z">> = iolist_to_binary(join(re:split("az","^a*?\\w",[trim]))), <<":z">> = iolist_to_binary(join(re:split("az","^a*?\\w",[{parts, - 2}]))), - <<":z">> = iolist_to_binary(join(re:split("az","^a*?\\w",[]))), - <<":aaz">> = iolist_to_binary(join(re:split("aaaz","^a*?\\w",[trim]))), + 2}]))), + <<":z">> = iolist_to_binary(join(re:split("az","^a*?\\w",[]))), + <<":aaz">> = iolist_to_binary(join(re:split("aaaz","^a*?\\w",[trim]))), <<":aaz">> = iolist_to_binary(join(re:split("aaaz","^a*?\\w",[{parts, - 2}]))), - <<":aaz">> = iolist_to_binary(join(re:split("aaaz","^a*?\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("a","^a*?\\w",[trim]))), + 2}]))), + <<":aaz">> = iolist_to_binary(join(re:split("aaaz","^a*?\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("a","^a*?\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("a","^a*?\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a","^a*?\\w",[]))), - <<":a">> = iolist_to_binary(join(re:split("aa","^a*?\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a","^a*?\\w",[]))), + <<":a">> = iolist_to_binary(join(re:split("aa","^a*?\\w",[trim]))), <<":a">> = iolist_to_binary(join(re:split("aa","^a*?\\w",[{parts, - 2}]))), - <<":a">> = iolist_to_binary(join(re:split("aa","^a*?\\w",[]))), - <<":aaa">> = iolist_to_binary(join(re:split("aaaa","^a*?\\w",[trim]))), + 2}]))), + <<":a">> = iolist_to_binary(join(re:split("aa","^a*?\\w",[]))), + <<":aaa">> = iolist_to_binary(join(re:split("aaaa","^a*?\\w",[trim]))), <<":aaa">> = iolist_to_binary(join(re:split("aaaa","^a*?\\w",[{parts, - 2}]))), - <<":aaa">> = iolist_to_binary(join(re:split("aaaa","^a*?\\w",[]))), - <<":+">> = iolist_to_binary(join(re:split("a+","^a*?\\w",[trim]))), + 2}]))), + <<":aaa">> = iolist_to_binary(join(re:split("aaaa","^a*?\\w",[]))), + <<":+">> = iolist_to_binary(join(re:split("a+","^a*?\\w",[trim]))), <<":+">> = iolist_to_binary(join(re:split("a+","^a*?\\w",[{parts, - 2}]))), - <<":+">> = iolist_to_binary(join(re:split("a+","^a*?\\w",[]))), - <<":a+">> = iolist_to_binary(join(re:split("aa+","^a*?\\w",[trim]))), + 2}]))), + <<":+">> = iolist_to_binary(join(re:split("a+","^a*?\\w",[]))), + <<":a+">> = iolist_to_binary(join(re:split("aa+","^a*?\\w",[trim]))), <<":a+">> = iolist_to_binary(join(re:split("aa+","^a*?\\w",[{parts, - 2}]))), - <<":a+">> = iolist_to_binary(join(re:split("aa+","^a*?\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("az","^a+\\w",[trim]))), + 2}]))), + <<":a+">> = iolist_to_binary(join(re:split("aa+","^a*?\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("az","^a+\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("az","^a+\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("az","^a+\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaz","^a+\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("az","^a+\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaz","^a+\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaz","^a+\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaz","^a+\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("aa","^a+\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaz","^a+\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("aa","^a+\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("aa","^a+\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aa","^a+\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaa","^a+\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aa","^a+\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaa","^a+\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaa","^a+\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaa","^a+\\w",[]))), - <<":+">> = iolist_to_binary(join(re:split("aa+","^a+\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaa","^a+\\w",[]))), + <<":+">> = iolist_to_binary(join(re:split("aa+","^a+\\w",[trim]))), <<":+">> = iolist_to_binary(join(re:split("aa+","^a+\\w",[{parts, - 2}]))), - <<":+">> = iolist_to_binary(join(re:split("aa+","^a+\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("az","^a+?\\w",[trim]))), + 2}]))), + <<":+">> = iolist_to_binary(join(re:split("aa+","^a+\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("az","^a+?\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("az","^a+?\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("az","^a+?\\w",[]))), - <<":az">> = iolist_to_binary(join(re:split("aaaz","^a+?\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("az","^a+?\\w",[]))), + <<":az">> = iolist_to_binary(join(re:split("aaaz","^a+?\\w",[trim]))), <<":az">> = iolist_to_binary(join(re:split("aaaz","^a+?\\w",[{parts, - 2}]))), - <<":az">> = iolist_to_binary(join(re:split("aaaz","^a+?\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("aa","^a+?\\w",[trim]))), + 2}]))), + <<":az">> = iolist_to_binary(join(re:split("aaaz","^a+?\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("aa","^a+?\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("aa","^a+?\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aa","^a+?\\w",[]))), - <<":aa">> = iolist_to_binary(join(re:split("aaaa","^a+?\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aa","^a+?\\w",[]))), + <<":aa">> = iolist_to_binary(join(re:split("aaaa","^a+?\\w",[trim]))), <<":aa">> = iolist_to_binary(join(re:split("aaaa","^a+?\\w",[{parts, - 2}]))), - <<":aa">> = iolist_to_binary(join(re:split("aaaa","^a+?\\w",[]))), - <<":+">> = iolist_to_binary(join(re:split("aa+","^a+?\\w",[trim]))), + 2}]))), + <<":aa">> = iolist_to_binary(join(re:split("aaaa","^a+?\\w",[]))), + <<":+">> = iolist_to_binary(join(re:split("aa+","^a+?\\w",[trim]))), <<":+">> = iolist_to_binary(join(re:split("aa+","^a+?\\w",[{parts, - 2}]))), - <<":+">> = iolist_to_binary(join(re:split("aa+","^a+?\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("1234567890","^\\d{8}\\w{2,}",[trim]))), + 2}]))), + <<":+">> = iolist_to_binary(join(re:split("aa+","^a+?\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("1234567890","^\\d{8}\\w{2,}",[trim]))), <<":">> = iolist_to_binary(join(re:split("1234567890","^\\d{8}\\w{2,}",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("1234567890","^\\d{8}\\w{2,}",[]))), - <<"">> = iolist_to_binary(join(re:split("12345678ab","^\\d{8}\\w{2,}",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("1234567890","^\\d{8}\\w{2,}",[]))), + <<"">> = iolist_to_binary(join(re:split("12345678ab","^\\d{8}\\w{2,}",[trim]))), <<":">> = iolist_to_binary(join(re:split("12345678ab","^\\d{8}\\w{2,}",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("12345678ab","^\\d{8}\\w{2,}",[]))), - <<"">> = iolist_to_binary(join(re:split("12345678__","^\\d{8}\\w{2,}",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("12345678ab","^\\d{8}\\w{2,}",[]))), + <<"">> = iolist_to_binary(join(re:split("12345678__","^\\d{8}\\w{2,}",[trim]))), <<":">> = iolist_to_binary(join(re:split("12345678__","^\\d{8}\\w{2,}",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("12345678__","^\\d{8}\\w{2,}",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\d{8}\\w{2,}",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("12345678__","^\\d{8}\\w{2,}",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\d{8}\\w{2,}",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\d{8}\\w{2,}",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\d{8}\\w{2,}",[]))), - <<"1234567">> = iolist_to_binary(join(re:split("1234567","^\\d{8}\\w{2,}",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\d{8}\\w{2,}",[]))), + <<"1234567">> = iolist_to_binary(join(re:split("1234567","^\\d{8}\\w{2,}",[trim]))), <<"1234567">> = iolist_to_binary(join(re:split("1234567","^\\d{8}\\w{2,}",[{parts, - 2}]))), - <<"1234567">> = iolist_to_binary(join(re:split("1234567","^\\d{8}\\w{2,}",[]))), - <<"">> = iolist_to_binary(join(re:split("uoie","^[aeiou\\d]{4,5}$",[trim]))), + 2}]))), + <<"1234567">> = iolist_to_binary(join(re:split("1234567","^\\d{8}\\w{2,}",[]))), + <<"">> = iolist_to_binary(join(re:split("uoie","^[aeiou\\d]{4,5}$",[trim]))), <<":">> = iolist_to_binary(join(re:split("uoie","^[aeiou\\d]{4,5}$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("uoie","^[aeiou\\d]{4,5}$",[]))), - <<"">> = iolist_to_binary(join(re:split("1234","^[aeiou\\d]{4,5}$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("uoie","^[aeiou\\d]{4,5}$",[]))), + <<"">> = iolist_to_binary(join(re:split("1234","^[aeiou\\d]{4,5}$",[trim]))), <<":">> = iolist_to_binary(join(re:split("1234","^[aeiou\\d]{4,5}$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("1234","^[aeiou\\d]{4,5}$",[]))), - <<"">> = iolist_to_binary(join(re:split("12345","^[aeiou\\d]{4,5}$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("1234","^[aeiou\\d]{4,5}$",[]))), + <<"">> = iolist_to_binary(join(re:split("12345","^[aeiou\\d]{4,5}$",[trim]))), <<":">> = iolist_to_binary(join(re:split("12345","^[aeiou\\d]{4,5}$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("12345","^[aeiou\\d]{4,5}$",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaaa","^[aeiou\\d]{4,5}$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("12345","^[aeiou\\d]{4,5}$",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaaa","^[aeiou\\d]{4,5}$",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaaa","^[aeiou\\d]{4,5}$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaaa","^[aeiou\\d]{4,5}$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[aeiou\\d]{4,5}$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaaa","^[aeiou\\d]{4,5}$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[aeiou\\d]{4,5}$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[aeiou\\d]{4,5}$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[aeiou\\d]{4,5}$",[]))), - <<"123456">> = iolist_to_binary(join(re:split("123456","^[aeiou\\d]{4,5}$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[aeiou\\d]{4,5}$",[]))), + <<"123456">> = iolist_to_binary(join(re:split("123456","^[aeiou\\d]{4,5}$",[trim]))), <<"123456">> = iolist_to_binary(join(re:split("123456","^[aeiou\\d]{4,5}$",[{parts, - 2}]))), - <<"123456">> = iolist_to_binary(join(re:split("123456","^[aeiou\\d]{4,5}$",[]))), - <<"">> = iolist_to_binary(join(re:split("uoie","^[aeiou\\d]{4,5}?",[trim]))), + 2}]))), + <<"123456">> = iolist_to_binary(join(re:split("123456","^[aeiou\\d]{4,5}$",[]))), + <<"">> = iolist_to_binary(join(re:split("uoie","^[aeiou\\d]{4,5}?",[trim]))), <<":">> = iolist_to_binary(join(re:split("uoie","^[aeiou\\d]{4,5}?",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("uoie","^[aeiou\\d]{4,5}?",[]))), - <<"">> = iolist_to_binary(join(re:split("1234","^[aeiou\\d]{4,5}?",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("uoie","^[aeiou\\d]{4,5}?",[]))), + <<"">> = iolist_to_binary(join(re:split("1234","^[aeiou\\d]{4,5}?",[trim]))), <<":">> = iolist_to_binary(join(re:split("1234","^[aeiou\\d]{4,5}?",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("1234","^[aeiou\\d]{4,5}?",[]))), - <<":5">> = iolist_to_binary(join(re:split("12345","^[aeiou\\d]{4,5}?",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("1234","^[aeiou\\d]{4,5}?",[]))), + <<":5">> = iolist_to_binary(join(re:split("12345","^[aeiou\\d]{4,5}?",[trim]))), <<":5">> = iolist_to_binary(join(re:split("12345","^[aeiou\\d]{4,5}?",[{parts, - 2}]))), - <<":5">> = iolist_to_binary(join(re:split("12345","^[aeiou\\d]{4,5}?",[]))), - <<":a">> = iolist_to_binary(join(re:split("aaaaa","^[aeiou\\d]{4,5}?",[trim]))), + 2}]))), + <<":5">> = iolist_to_binary(join(re:split("12345","^[aeiou\\d]{4,5}?",[]))), + <<":a">> = iolist_to_binary(join(re:split("aaaaa","^[aeiou\\d]{4,5}?",[trim]))), <<":a">> = iolist_to_binary(join(re:split("aaaaa","^[aeiou\\d]{4,5}?",[{parts, - 2}]))), - <<":a">> = iolist_to_binary(join(re:split("aaaaa","^[aeiou\\d]{4,5}?",[]))), - <<":56">> = iolist_to_binary(join(re:split("123456","^[aeiou\\d]{4,5}?",[trim]))), + 2}]))), + <<":a">> = iolist_to_binary(join(re:split("aaaaa","^[aeiou\\d]{4,5}?",[]))), + <<":56">> = iolist_to_binary(join(re:split("123456","^[aeiou\\d]{4,5}?",[trim]))), <<":56">> = iolist_to_binary(join(re:split("123456","^[aeiou\\d]{4,5}?",[{parts, - 2}]))), - <<":56">> = iolist_to_binary(join(re:split("123456","^[aeiou\\d]{4,5}?",[]))), - <<":abc:abc">> = iolist_to_binary(join(re:split("abc=abcabc","\\A(abc|def)=(\\1){2,3}\\Z",[trim]))), + 2}]))), + <<":56">> = iolist_to_binary(join(re:split("123456","^[aeiou\\d]{4,5}?",[]))), + <<":abc:abc">> = iolist_to_binary(join(re:split("abc=abcabc","\\A(abc|def)=(\\1){2,3}\\Z",[trim]))), <<":abc:abc:">> = iolist_to_binary(join(re:split("abc=abcabc","\\A(abc|def)=(\\1){2,3}\\Z",[{parts, - 2}]))), - <<":abc:abc:">> = iolist_to_binary(join(re:split("abc=abcabc","\\A(abc|def)=(\\1){2,3}\\Z",[]))), - <<":def:def">> = iolist_to_binary(join(re:split("def=defdefdef","\\A(abc|def)=(\\1){2,3}\\Z",[trim]))), + 2}]))), + <<":abc:abc:">> = iolist_to_binary(join(re:split("abc=abcabc","\\A(abc|def)=(\\1){2,3}\\Z",[]))), + <<":def:def">> = iolist_to_binary(join(re:split("def=defdefdef","\\A(abc|def)=(\\1){2,3}\\Z",[trim]))), <<":def:def:">> = iolist_to_binary(join(re:split("def=defdefdef","\\A(abc|def)=(\\1){2,3}\\Z",[{parts, - 2}]))), - <<":def:def:">> = iolist_to_binary(join(re:split("def=defdefdef","\\A(abc|def)=(\\1){2,3}\\Z",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\A(abc|def)=(\\1){2,3}\\Z",[trim]))), + 2}]))), + <<":def:def:">> = iolist_to_binary(join(re:split("def=defdefdef","\\A(abc|def)=(\\1){2,3}\\Z",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\A(abc|def)=(\\1){2,3}\\Z",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\A(abc|def)=(\\1){2,3}\\Z",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\A(abc|def)=(\\1){2,3}\\Z",[]))), - <<"abc=defdef">> = iolist_to_binary(join(re:split("abc=defdef","\\A(abc|def)=(\\1){2,3}\\Z",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\A(abc|def)=(\\1){2,3}\\Z",[]))), + <<"abc=defdef">> = iolist_to_binary(join(re:split("abc=defdef","\\A(abc|def)=(\\1){2,3}\\Z",[trim]))), <<"abc=defdef">> = iolist_to_binary(join(re:split("abc=defdef","\\A(abc|def)=(\\1){2,3}\\Z",[{parts, - 2}]))), - <<"abc=defdef">> = iolist_to_binary(join(re:split("abc=defdef","\\A(abc|def)=(\\1){2,3}\\Z",[]))), - <<":a:b:c:d:e:f:g:h:i:j:k:cd">> = iolist_to_binary(join(re:split("abcdefghijkcda2","^(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)\\11*(\\3\\4)\\1(?#)2$",[trim]))), + 2}]))), + <<"abc=defdef">> = iolist_to_binary(join(re:split("abc=defdef","\\A(abc|def)=(\\1){2,3}\\Z",[]))), + <<":a:b:c:d:e:f:g:h:i:j:k:cd">> = iolist_to_binary(join(re:split("abcdefghijkcda2","^(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)\\11*(\\3\\4)\\1(?#)2$",[trim]))), <<":a:b:c:d:e:f:g:h:i:j:k:cd:">> = iolist_to_binary(join(re:split("abcdefghijkcda2","^(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)\\11*(\\3\\4)\\1(?#)2$",[{parts, - 2}]))), - <<":a:b:c:d:e:f:g:h:i:j:k:cd:">> = iolist_to_binary(join(re:split("abcdefghijkcda2","^(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)\\11*(\\3\\4)\\1(?#)2$",[]))), - <<":a:b:c:d:e:f:g:h:i:j:k:cd">> = iolist_to_binary(join(re:split("abcdefghijkkkkcda2","^(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)\\11*(\\3\\4)\\1(?#)2$",[trim]))), + 2}]))), + <<":a:b:c:d:e:f:g:h:i:j:k:cd:">> = iolist_to_binary(join(re:split("abcdefghijkcda2","^(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)\\11*(\\3\\4)\\1(?#)2$",[]))), + <<":a:b:c:d:e:f:g:h:i:j:k:cd">> = iolist_to_binary(join(re:split("abcdefghijkkkkcda2","^(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)\\11*(\\3\\4)\\1(?#)2$",[trim]))), <<":a:b:c:d:e:f:g:h:i:j:k:cd:">> = iolist_to_binary(join(re:split("abcdefghijkkkkcda2","^(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)\\11*(\\3\\4)\\1(?#)2$",[{parts, - 2}]))), - <<":a:b:c:d:e:f:g:h:i:j:k:cd:">> = iolist_to_binary(join(re:split("abcdefghijkkkkcda2","^(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)\\11*(\\3\\4)\\1(?#)2$",[]))), - <<":cataract:aract:ract::3">> = iolist_to_binary(join(re:split("cataract cataract23","(cat(a(ract|tonic)|erpillar)) \\1()2(3)",[trim]))), + 2}]))), + <<":a:b:c:d:e:f:g:h:i:j:k:cd:">> = iolist_to_binary(join(re:split("abcdefghijkkkkcda2","^(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)\\11*(\\3\\4)\\1(?#)2$",[]))), + <<":cataract:aract:ract::3">> = iolist_to_binary(join(re:split("cataract cataract23","(cat(a(ract|tonic)|erpillar)) \\1()2(3)",[trim]))), <<":cataract:aract:ract::3:">> = iolist_to_binary(join(re:split("cataract cataract23","(cat(a(ract|tonic)|erpillar)) \\1()2(3)",[{parts, - 2}]))), - <<":cataract:aract:ract::3:">> = iolist_to_binary(join(re:split("cataract cataract23","(cat(a(ract|tonic)|erpillar)) \\1()2(3)",[]))), - <<":catatonic:atonic:tonic::3">> = iolist_to_binary(join(re:split("catatonic catatonic23","(cat(a(ract|tonic)|erpillar)) \\1()2(3)",[trim]))), + 2}]))), + <<":cataract:aract:ract::3:">> = iolist_to_binary(join(re:split("cataract cataract23","(cat(a(ract|tonic)|erpillar)) \\1()2(3)",[]))), + <<":catatonic:atonic:tonic::3">> = iolist_to_binary(join(re:split("catatonic catatonic23","(cat(a(ract|tonic)|erpillar)) \\1()2(3)",[trim]))), <<":catatonic:atonic:tonic::3:">> = iolist_to_binary(join(re:split("catatonic catatonic23","(cat(a(ract|tonic)|erpillar)) \\1()2(3)",[{parts, - 2}]))), - <<":catatonic:atonic:tonic::3:">> = iolist_to_binary(join(re:split("catatonic catatonic23","(cat(a(ract|tonic)|erpillar)) \\1()2(3)",[]))), - <<":caterpillar:erpillar:::3">> = iolist_to_binary(join(re:split("caterpillar caterpillar23","(cat(a(ract|tonic)|erpillar)) \\1()2(3)",[trim]))), + 2}]))), + <<":catatonic:atonic:tonic::3:">> = iolist_to_binary(join(re:split("catatonic catatonic23","(cat(a(ract|tonic)|erpillar)) \\1()2(3)",[]))), + <<":caterpillar:erpillar:::3">> = iolist_to_binary(join(re:split("caterpillar caterpillar23","(cat(a(ract|tonic)|erpillar)) \\1()2(3)",[trim]))), <<":caterpillar:erpillar:::3:">> = iolist_to_binary(join(re:split("caterpillar caterpillar23","(cat(a(ract|tonic)|erpillar)) \\1()2(3)",[{parts, - 2}]))), - <<":caterpillar:erpillar:::3:">> = iolist_to_binary(join(re:split("caterpillar caterpillar23","(cat(a(ract|tonic)|erpillar)) \\1()2(3)",[]))), - <<":abcd::02 1997">> = iolist_to_binary(join(re:split("From abcd Mon Sep 01 12:33:02 1997","^From +([^ ]+) +[a-zA-Z][a-zA-Z][a-zA-Z] +[a-zA-Z][a-zA-Z][a-zA-Z] +[0-9]?[0-9] +[0-9][0-9]:[0-9][0-9]",[trim]))), + 2}]))), + <<":caterpillar:erpillar:::3:">> = iolist_to_binary(join(re:split("caterpillar caterpillar23","(cat(a(ract|tonic)|erpillar)) \\1()2(3)",[]))), + <<":abcd::02 1997">> = iolist_to_binary(join(re:split("From abcd Mon Sep 01 12:33:02 1997","^From +([^ ]+) +[a-zA-Z][a-zA-Z][a-zA-Z] +[a-zA-Z][a-zA-Z][a-zA-Z] +[0-9]?[0-9] +[0-9][0-9]:[0-9][0-9]",[trim]))), <<":abcd::02 1997">> = iolist_to_binary(join(re:split("From abcd Mon Sep 01 12:33:02 1997","^From +([^ ]+) +[a-zA-Z][a-zA-Z][a-zA-Z] +[a-zA-Z][a-zA-Z][a-zA-Z] +[0-9]?[0-9] +[0-9][0-9]:[0-9][0-9]",[{parts, - 2}]))), - <<":abcd::02 1997">> = iolist_to_binary(join(re:split("From abcd Mon Sep 01 12:33:02 1997","^From +([^ ]+) +[a-zA-Z][a-zA-Z][a-zA-Z] +[a-zA-Z][a-zA-Z][a-zA-Z] +[0-9]?[0-9] +[0-9][0-9]:[0-9][0-9]",[]))), - <<":Sep ::02 1997">> = iolist_to_binary(join(re:split("From abcd Mon Sep 01 12:33:02 1997","^From\\s+\\S+\\s+([a-zA-Z]{3}\\s+){2}\\d{1,2}\\s+\\d\\d:\\d\\d",[trim]))), + 2}]))), + <<":abcd::02 1997">> = iolist_to_binary(join(re:split("From abcd Mon Sep 01 12:33:02 1997","^From +([^ ]+) +[a-zA-Z][a-zA-Z][a-zA-Z] +[a-zA-Z][a-zA-Z][a-zA-Z] +[0-9]?[0-9] +[0-9][0-9]:[0-9][0-9]",[]))), + <<":Sep ::02 1997">> = iolist_to_binary(join(re:split("From abcd Mon Sep 01 12:33:02 1997","^From\\s+\\S+\\s+([a-zA-Z]{3}\\s+){2}\\d{1,2}\\s+\\d\\d:\\d\\d",[trim]))), <<":Sep ::02 1997">> = iolist_to_binary(join(re:split("From abcd Mon Sep 01 12:33:02 1997","^From\\s+\\S+\\s+([a-zA-Z]{3}\\s+){2}\\d{1,2}\\s+\\d\\d:\\d\\d",[{parts, - 2}]))), - <<":Sep ::02 1997">> = iolist_to_binary(join(re:split("From abcd Mon Sep 01 12:33:02 1997","^From\\s+\\S+\\s+([a-zA-Z]{3}\\s+){2}\\d{1,2}\\s+\\d\\d:\\d\\d",[]))), - <<":Sep ::02 1997">> = iolist_to_binary(join(re:split("From abcd Mon Sep 1 12:33:02 1997","^From\\s+\\S+\\s+([a-zA-Z]{3}\\s+){2}\\d{1,2}\\s+\\d\\d:\\d\\d",[trim]))), + 2}]))), + <<":Sep ::02 1997">> = iolist_to_binary(join(re:split("From abcd Mon Sep 01 12:33:02 1997","^From\\s+\\S+\\s+([a-zA-Z]{3}\\s+){2}\\d{1,2}\\s+\\d\\d:\\d\\d",[]))), + <<":Sep ::02 1997">> = iolist_to_binary(join(re:split("From abcd Mon Sep 1 12:33:02 1997","^From\\s+\\S+\\s+([a-zA-Z]{3}\\s+){2}\\d{1,2}\\s+\\d\\d:\\d\\d",[trim]))), <<":Sep ::02 1997">> = iolist_to_binary(join(re:split("From abcd Mon Sep 1 12:33:02 1997","^From\\s+\\S+\\s+([a-zA-Z]{3}\\s+){2}\\d{1,2}\\s+\\d\\d:\\d\\d",[{parts, - 2}]))), - <<":Sep ::02 1997">> = iolist_to_binary(join(re:split("From abcd Mon Sep 1 12:33:02 1997","^From\\s+\\S+\\s+([a-zA-Z]{3}\\s+){2}\\d{1,2}\\s+\\d\\d:\\d\\d",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^From\\s+\\S+\\s+([a-zA-Z]{3}\\s+){2}\\d{1,2}\\s+\\d\\d:\\d\\d",[trim]))), + 2}]))), + <<":Sep ::02 1997">> = iolist_to_binary(join(re:split("From abcd Mon Sep 1 12:33:02 1997","^From\\s+\\S+\\s+([a-zA-Z]{3}\\s+){2}\\d{1,2}\\s+\\d\\d:\\d\\d",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^From\\s+\\S+\\s+([a-zA-Z]{3}\\s+){2}\\d{1,2}\\s+\\d\\d:\\d\\d",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^From\\s+\\S+\\s+([a-zA-Z]{3}\\s+){2}\\d{1,2}\\s+\\d\\d:\\d\\d",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^From\\s+\\S+\\s+([a-zA-Z]{3}\\s+){2}\\d{1,2}\\s+\\d\\d:\\d\\d",[]))), - <<"From abcd Sep 01 12:33:02 1997">> = iolist_to_binary(join(re:split("From abcd Sep 01 12:33:02 1997","^From\\s+\\S+\\s+([a-zA-Z]{3}\\s+){2}\\d{1,2}\\s+\\d\\d:\\d\\d",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^From\\s+\\S+\\s+([a-zA-Z]{3}\\s+){2}\\d{1,2}\\s+\\d\\d:\\d\\d",[]))), + <<"From abcd Sep 01 12:33:02 1997">> = iolist_to_binary(join(re:split("From abcd Sep 01 12:33:02 1997","^From\\s+\\S+\\s+([a-zA-Z]{3}\\s+){2}\\d{1,2}\\s+\\d\\d:\\d\\d",[trim]))), <<"From abcd Sep 01 12:33:02 1997">> = iolist_to_binary(join(re:split("From abcd Sep 01 12:33:02 1997","^From\\s+\\S+\\s+([a-zA-Z]{3}\\s+){2}\\d{1,2}\\s+\\d\\d:\\d\\d",[{parts, - 2}]))), - <<"From abcd Sep 01 12:33:02 1997">> = iolist_to_binary(join(re:split("From abcd Sep 01 12:33:02 1997","^From\\s+\\S+\\s+([a-zA-Z]{3}\\s+){2}\\d{1,2}\\s+\\d\\d:\\d\\d",[]))), + 2}]))), + <<"From abcd Sep 01 12:33:02 1997">> = iolist_to_binary(join(re:split("From abcd Sep 01 12:33:02 1997","^From\\s+\\S+\\s+([a-zA-Z]{3}\\s+){2}\\d{1,2}\\s+\\d\\d:\\d\\d",[]))), <<"">> = iolist_to_binary(join(re:split("12 -34","^12.34",[dotall,trim]))), +34","^12.34",[dotall,trim]))), <<":">> = iolist_to_binary(join(re:split("12 -34","^12.34",[dotall,{parts,2}]))), +34","^12.34",[dotall,{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("12 -34","^12.34",[dotall]))), +34","^12.34",[dotall]))), <<"">> = iolist_to_binary(join(re:split("12
34","^12.34",[dotall, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("12
34","^12.34",[dotall, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("12
34","^12.34",[dotall]))), - <<"the quick : fox">> = iolist_to_binary(join(re:split("the quick brown fox","\\w+(?=\\t)",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("12
34","^12.34",[dotall]))), + <<"the quick : fox">> = iolist_to_binary(join(re:split("the quick brown fox","\\w+(?=\\t)",[trim]))), <<"the quick : fox">> = iolist_to_binary(join(re:split("the quick brown fox","\\w+(?=\\t)",[{parts, - 2}]))), - <<"the quick : fox">> = iolist_to_binary(join(re:split("the quick brown fox","\\w+(?=\\t)",[]))), - <<"foobar is :lish see?">> = iolist_to_binary(join(re:split("foobar is foolish see?","foo(?!bar)(.*)",[trim]))), + 2}]))), + <<"the quick : fox">> = iolist_to_binary(join(re:split("the quick brown fox","\\w+(?=\\t)",[]))), + <<"foobar is :lish see?">> = iolist_to_binary(join(re:split("foobar is foolish see?","foo(?!bar)(.*)",[trim]))), <<"foobar is :lish see?:">> = iolist_to_binary(join(re:split("foobar is foolish see?","foo(?!bar)(.*)",[{parts, - 2}]))), - <<"foobar is :lish see?:">> = iolist_to_binary(join(re:split("foobar is foolish see?","foo(?!bar)(.*)",[]))), - <<"foobar c: etc">> = iolist_to_binary(join(re:split("foobar crowbar etc","(?:(?!foo)...|^.{0,2})bar(.*)",[trim]))), + 2}]))), + <<"foobar is :lish see?:">> = iolist_to_binary(join(re:split("foobar is foolish see?","foo(?!bar)(.*)",[]))), + <<"foobar c: etc">> = iolist_to_binary(join(re:split("foobar crowbar etc","(?:(?!foo)...|^.{0,2})bar(.*)",[trim]))), <<"foobar c: etc:">> = iolist_to_binary(join(re:split("foobar crowbar etc","(?:(?!foo)...|^.{0,2})bar(.*)",[{parts, - 2}]))), - <<"foobar c: etc:">> = iolist_to_binary(join(re:split("foobar crowbar etc","(?:(?!foo)...|^.{0,2})bar(.*)",[]))), - <<":rel">> = iolist_to_binary(join(re:split("barrel","(?:(?!foo)...|^.{0,2})bar(.*)",[trim]))), + 2}]))), + <<"foobar c: etc:">> = iolist_to_binary(join(re:split("foobar crowbar etc","(?:(?!foo)...|^.{0,2})bar(.*)",[]))), + <<":rel">> = iolist_to_binary(join(re:split("barrel","(?:(?!foo)...|^.{0,2})bar(.*)",[trim]))), <<":rel:">> = iolist_to_binary(join(re:split("barrel","(?:(?!foo)...|^.{0,2})bar(.*)",[{parts, - 2}]))), - <<":rel:">> = iolist_to_binary(join(re:split("barrel","(?:(?!foo)...|^.{0,2})bar(.*)",[]))), - <<":rel">> = iolist_to_binary(join(re:split("2barrel","(?:(?!foo)...|^.{0,2})bar(.*)",[trim]))), + 2}]))), + <<":rel:">> = iolist_to_binary(join(re:split("barrel","(?:(?!foo)...|^.{0,2})bar(.*)",[]))), + <<":rel">> = iolist_to_binary(join(re:split("2barrel","(?:(?!foo)...|^.{0,2})bar(.*)",[trim]))), <<":rel:">> = iolist_to_binary(join(re:split("2barrel","(?:(?!foo)...|^.{0,2})bar(.*)",[{parts, - 2}]))), - <<":rel:">> = iolist_to_binary(join(re:split("2barrel","(?:(?!foo)...|^.{0,2})bar(.*)",[]))), - <<":rel">> = iolist_to_binary(join(re:split("A barrel","(?:(?!foo)...|^.{0,2})bar(.*)",[trim]))), + 2}]))), + <<":rel:">> = iolist_to_binary(join(re:split("2barrel","(?:(?!foo)...|^.{0,2})bar(.*)",[]))), + <<":rel">> = iolist_to_binary(join(re:split("A barrel","(?:(?!foo)...|^.{0,2})bar(.*)",[trim]))), <<":rel:">> = iolist_to_binary(join(re:split("A barrel","(?:(?!foo)...|^.{0,2})bar(.*)",[{parts, - 2}]))), - <<":rel:">> = iolist_to_binary(join(re:split("A barrel","(?:(?!foo)...|^.{0,2})bar(.*)",[]))), - <<":abc:456">> = iolist_to_binary(join(re:split("abc456","^(\\D*)(?=\\d)(?!123)",[trim]))), + 2}]))), + <<":rel:">> = iolist_to_binary(join(re:split("A barrel","(?:(?!foo)...|^.{0,2})bar(.*)",[]))), + <<":abc:456">> = iolist_to_binary(join(re:split("abc456","^(\\D*)(?=\\d)(?!123)",[trim]))), <<":abc:456">> = iolist_to_binary(join(re:split("abc456","^(\\D*)(?=\\d)(?!123)",[{parts, - 2}]))), - <<":abc:456">> = iolist_to_binary(join(re:split("abc456","^(\\D*)(?=\\d)(?!123)",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\D*)(?=\\d)(?!123)",[trim]))), + 2}]))), + <<":abc:456">> = iolist_to_binary(join(re:split("abc456","^(\\D*)(?=\\d)(?!123)",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\D*)(?=\\d)(?!123)",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\D*)(?=\\d)(?!123)",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\D*)(?=\\d)(?!123)",[]))), - <<"abc123">> = iolist_to_binary(join(re:split("abc123","^(\\D*)(?=\\d)(?!123)",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\D*)(?=\\d)(?!123)",[]))), + <<"abc123">> = iolist_to_binary(join(re:split("abc123","^(\\D*)(?=\\d)(?!123)",[trim]))), <<"abc123">> = iolist_to_binary(join(re:split("abc123","^(\\D*)(?=\\d)(?!123)",[{parts, - 2}]))), - <<"abc123">> = iolist_to_binary(join(re:split("abc123","^(\\D*)(?=\\d)(?!123)",[]))), + 2}]))), + <<"abc123">> = iolist_to_binary(join(re:split("abc123","^(\\D*)(?=\\d)(?!123)",[]))), ok. run3() -> <<"">> = iolist_to_binary(join(re:split("1234","^1234(?# test newlines - inside)",[trim]))), + inside)",[trim]))), <<":">> = iolist_to_binary(join(re:split("1234","^1234(?# test newlines - inside)",[{parts,2}]))), + inside)",[{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("1234","^1234(?# test newlines - inside)",[]))), + inside)",[]))), <<"">> = iolist_to_binary(join(re:split("1234","^1234 #comment in extended re - ",[extended,trim]))), + ",[extended,trim]))), <<":">> = iolist_to_binary(join(re:split("1234","^1234 #comment in extended re - ",[extended,{parts,2}]))), + ",[extended,{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("1234","^1234 #comment in extended re - ",[extended]))), + ",[extended]))), <<"">> = iolist_to_binary(join(re:split("abcd","#rhubarb - abcd",[extended,trim]))), + abcd",[extended,trim]))), <<":">> = iolist_to_binary(join(re:split("abcd","#rhubarb - abcd",[extended,{parts,2}]))), + abcd",[extended,{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("abcd","#rhubarb - abcd",[extended]))), + abcd",[extended]))), <<"">> = iolist_to_binary(join(re:split("abcd","^abcd#rhubarb",[extended, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("abcd","^abcd#rhubarb",[extended, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abcd","^abcd#rhubarb",[extended]))), - <<":a:b">> = iolist_to_binary(join(re:split("aaab","^(a)\\1{2,3}(.)",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abcd","^abcd#rhubarb",[extended]))), + <<":a:b">> = iolist_to_binary(join(re:split("aaab","^(a)\\1{2,3}(.)",[trim]))), <<":a:b:">> = iolist_to_binary(join(re:split("aaab","^(a)\\1{2,3}(.)",[{parts, - 2}]))), - <<":a:b:">> = iolist_to_binary(join(re:split("aaab","^(a)\\1{2,3}(.)",[]))), - <<":a:b">> = iolist_to_binary(join(re:split("aaaab","^(a)\\1{2,3}(.)",[trim]))), + 2}]))), + <<":a:b:">> = iolist_to_binary(join(re:split("aaab","^(a)\\1{2,3}(.)",[]))), + <<":a:b">> = iolist_to_binary(join(re:split("aaaab","^(a)\\1{2,3}(.)",[trim]))), <<":a:b:">> = iolist_to_binary(join(re:split("aaaab","^(a)\\1{2,3}(.)",[{parts, - 2}]))), - <<":a:b:">> = iolist_to_binary(join(re:split("aaaab","^(a)\\1{2,3}(.)",[]))), - <<":a:a:b">> = iolist_to_binary(join(re:split("aaaaab","^(a)\\1{2,3}(.)",[trim]))), + 2}]))), + <<":a:b:">> = iolist_to_binary(join(re:split("aaaab","^(a)\\1{2,3}(.)",[]))), + <<":a:a:b">> = iolist_to_binary(join(re:split("aaaaab","^(a)\\1{2,3}(.)",[trim]))), <<":a:a:b">> = iolist_to_binary(join(re:split("aaaaab","^(a)\\1{2,3}(.)",[{parts, - 2}]))), - <<":a:a:b">> = iolist_to_binary(join(re:split("aaaaab","^(a)\\1{2,3}(.)",[]))), - <<":a:a:ab">> = iolist_to_binary(join(re:split("aaaaaab","^(a)\\1{2,3}(.)",[trim]))), + 2}]))), + <<":a:a:b">> = iolist_to_binary(join(re:split("aaaaab","^(a)\\1{2,3}(.)",[]))), + <<":a:a:ab">> = iolist_to_binary(join(re:split("aaaaaab","^(a)\\1{2,3}(.)",[trim]))), <<":a:a:ab">> = iolist_to_binary(join(re:split("aaaaaab","^(a)\\1{2,3}(.)",[{parts, - 2}]))), - <<":a:a:ab">> = iolist_to_binary(join(re:split("aaaaaab","^(a)\\1{2,3}(.)",[]))), - <<"the ">> = iolist_to_binary(join(re:split("the abc","(?!^)abc",[trim]))), + 2}]))), + <<":a:a:ab">> = iolist_to_binary(join(re:split("aaaaaab","^(a)\\1{2,3}(.)",[]))), + <<"the ">> = iolist_to_binary(join(re:split("the abc","(?!^)abc",[trim]))), <<"the :">> = iolist_to_binary(join(re:split("the abc","(?!^)abc",[{parts, - 2}]))), - <<"the :">> = iolist_to_binary(join(re:split("the abc","(?!^)abc",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?!^)abc",[trim]))), + 2}]))), + <<"the :">> = iolist_to_binary(join(re:split("the abc","(?!^)abc",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?!^)abc",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?!^)abc",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?!^)abc",[]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","(?!^)abc",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?!^)abc",[]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","(?!^)abc",[trim]))), <<"abc">> = iolist_to_binary(join(re:split("abc","(?!^)abc",[{parts, - 2}]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","(?!^)abc",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","(?=^)abc",[trim]))), + 2}]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","(?!^)abc",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","(?=^)abc",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","(?=^)abc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","(?=^)abc",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?=^)abc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","(?=^)abc",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?=^)abc",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?=^)abc",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?=^)abc",[]))), - <<"the abc">> = iolist_to_binary(join(re:split("the abc","(?=^)abc",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?=^)abc",[]))), + <<"the abc">> = iolist_to_binary(join(re:split("the abc","(?=^)abc",[trim]))), <<"the abc">> = iolist_to_binary(join(re:split("the abc","(?=^)abc",[{parts, - 2}]))), - <<"the abc">> = iolist_to_binary(join(re:split("the abc","(?=^)abc",[]))), - <<":b:bbb">> = iolist_to_binary(join(re:split("aabbbbb","^[ab]{1,3}(ab*|b)",[trim]))), + 2}]))), + <<"the abc">> = iolist_to_binary(join(re:split("the abc","(?=^)abc",[]))), + <<":b:bbb">> = iolist_to_binary(join(re:split("aabbbbb","^[ab]{1,3}(ab*|b)",[trim]))), <<":b:bbb">> = iolist_to_binary(join(re:split("aabbbbb","^[ab]{1,3}(ab*|b)",[{parts, - 2}]))), - <<":b:bbb">> = iolist_to_binary(join(re:split("aabbbbb","^[ab]{1,3}(ab*|b)",[]))), - <<":abbbbb">> = iolist_to_binary(join(re:split("aabbbbb","^[ab]{1,3}?(ab*|b)",[trim]))), + 2}]))), + <<":b:bbb">> = iolist_to_binary(join(re:split("aabbbbb","^[ab]{1,3}(ab*|b)",[]))), + <<":abbbbb">> = iolist_to_binary(join(re:split("aabbbbb","^[ab]{1,3}?(ab*|b)",[trim]))), <<":abbbbb:">> = iolist_to_binary(join(re:split("aabbbbb","^[ab]{1,3}?(ab*|b)",[{parts, - 2}]))), - <<":abbbbb:">> = iolist_to_binary(join(re:split("aabbbbb","^[ab]{1,3}?(ab*|b)",[]))), - <<":a:bbbbb">> = iolist_to_binary(join(re:split("aabbbbb","^[ab]{1,3}?(ab*?|b)",[trim]))), + 2}]))), + <<":abbbbb:">> = iolist_to_binary(join(re:split("aabbbbb","^[ab]{1,3}?(ab*|b)",[]))), + <<":a:bbbbb">> = iolist_to_binary(join(re:split("aabbbbb","^[ab]{1,3}?(ab*?|b)",[trim]))), <<":a:bbbbb">> = iolist_to_binary(join(re:split("aabbbbb","^[ab]{1,3}?(ab*?|b)",[{parts, - 2}]))), - <<":a:bbbbb">> = iolist_to_binary(join(re:split("aabbbbb","^[ab]{1,3}?(ab*?|b)",[]))), - <<":b:bbb">> = iolist_to_binary(join(re:split("aabbbbb","^[ab]{1,3}(ab*?|b)",[trim]))), + 2}]))), + <<":a:bbbbb">> = iolist_to_binary(join(re:split("aabbbbb","^[ab]{1,3}?(ab*?|b)",[]))), + <<":b:bbb">> = iolist_to_binary(join(re:split("aabbbbb","^[ab]{1,3}(ab*?|b)",[trim]))), <<":b:bbb">> = iolist_to_binary(join(re:split("aabbbbb","^[ab]{1,3}(ab*?|b)",[{parts, - 2}]))), - <<":b:bbb">> = iolist_to_binary(join(re:split("aabbbbb","^[ab]{1,3}(ab*?|b)",[]))), + 2}]))), + <<":b:bbb">> = iolist_to_binary(join(re:split("aabbbbb","^[ab]{1,3}(ab*?|b)",[]))), <<"Alan Other <user.ain>">> = iolist_to_binary(join(re:split("Alan Other <user.ain>"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -1630,7 +1630,7 @@ run3() -> # name and address ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* -\\) )* # optional trailing comment",[extended,trim]))), +\\) )* # optional trailing comment",[extended,trim]))), <<"Alan Other <user.ain>">> = iolist_to_binary(join(re:split("Alan Other <user.ain>"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -1824,7 +1824,7 @@ run3() -> ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional trailing comment",[extended, - {parts,2}]))), + {parts,2}]))), <<"Alan Other <user.ain>">> = iolist_to_binary(join(re:split("Alan Other <user.ain>"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -2017,7 +2017,7 @@ run3() -> # name and address ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* -\\) )* # optional trailing comment",[extended]))), +\\) )* # optional trailing comment",[extended]))), <<"<user.ain>">> = iolist_to_binary(join(re:split("<user.ain>"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -2210,7 +2210,7 @@ run3() -> # name and address ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* -\\) )* # optional trailing comment",[extended,trim]))), +\\) )* # optional trailing comment",[extended,trim]))), <<"<user.ain>">> = iolist_to_binary(join(re:split("<user.ain>"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -2404,7 +2404,7 @@ run3() -> ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional trailing comment",[extended, - {parts,2}]))), + {parts,2}]))), <<"<user.ain>">> = iolist_to_binary(join(re:split("<user.ain>"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -2597,7 +2597,7 @@ run3() -> # name and address ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* -\\) )* # optional trailing comment",[extended]))), +\\) )* # optional trailing comment",[extended]))), <<"user.ain">> = iolist_to_binary(join(re:split("user.ain"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -2790,7 +2790,7 @@ run3() -> # name and address ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* -\\) )* # optional trailing comment",[extended,trim]))), +\\) )* # optional trailing comment",[extended,trim]))), <<"user.ain">> = iolist_to_binary(join(re:split("user.ain"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -2984,7 +2984,7 @@ run3() -> ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional trailing comment",[extended, - {parts,2}]))), + {parts,2}]))), <<"user.ain">> = iolist_to_binary(join(re:split("user.ain"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -3177,7 +3177,7 @@ run3() -> # name and address ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* -\\) )* # optional trailing comment",[extended]))), +\\) )* # optional trailing comment",[extended]))), <<"\"A. Other\" <user.1234.ain> (a comment)">> = iolist_to_binary(join(re:split("\"A. Other\" <user.1234.ain> (a comment)"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -3370,7 +3370,7 @@ run3() -> # name and address ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* -\\) )* # optional trailing comment",[extended,trim]))), +\\) )* # optional trailing comment",[extended,trim]))), <<"\"A. Other\" <user.1234.ain> (a comment)">> = iolist_to_binary(join(re:split("\"A. Other\" <user.1234.ain> (a comment)"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -3564,7 +3564,7 @@ run3() -> ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional trailing comment",[extended, - {parts,2}]))), + {parts,2}]))), <<"\"A. Other\" <user.1234.ain> (a comment)">> = iolist_to_binary(join(re:split("\"A. Other\" <user.1234.ain> (a comment)"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -3757,7 +3757,7 @@ run3() -> # name and address ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* -\\) )* # optional trailing comment",[extended]))), +\\) )* # optional trailing comment",[extended]))), <<"A. Other <user.1234.ain> (a comment)">> = iolist_to_binary(join(re:split("A. Other <user.1234.ain> (a comment)"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -3950,7 +3950,7 @@ run3() -> # name and address ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* -\\) )* # optional trailing comment",[extended,trim]))), +\\) )* # optional trailing comment",[extended,trim]))), <<"A. Other <user.1234.ain> (a comment)">> = iolist_to_binary(join(re:split("A. Other <user.1234.ain> (a comment)"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -4144,7 +4144,7 @@ run3() -> ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional trailing comment",[extended, - {parts,2}]))), + {parts,2}]))), <<"A. Other <user.1234.ain> (a comment)">> = iolist_to_binary(join(re:split("A. Other <user.1234.ain> (a comment)"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -4337,7 +4337,7 @@ run3() -> # name and address ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* -\\) )* # optional trailing comment",[extended]))), +\\) )* # optional trailing comment",[extended]))), <<"\"/s=user/ou=host/o=place/prmd=uu.yy/admd= /c=gb/\"-re.lay">> = iolist_to_binary(join(re:split("\"/s=user/ou=host/o=place/prmd=uu.yy/admd= /c=gb/\"-re.lay"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -4530,7 +4530,7 @@ run3() -> # name and address ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* -\\) )* # optional trailing comment",[extended,trim]))), +\\) )* # optional trailing comment",[extended,trim]))), <<"\"/s=user/ou=host/o=place/prmd=uu.yy/admd= /c=gb/\"-re.lay">> = iolist_to_binary(join(re:split("\"/s=user/ou=host/o=place/prmd=uu.yy/admd= /c=gb/\"-re.lay"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -4724,7 +4724,7 @@ run3() -> ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional trailing comment",[extended, - {parts,2}]))), + {parts,2}]))), <<"\"/s=user/ou=host/o=place/prmd=uu.yy/admd= /c=gb/\"-re.lay">> = iolist_to_binary(join(re:split("\"/s=user/ou=host/o=place/prmd=uu.yy/admd= /c=gb/\"-re.lay"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -4917,7 +4917,7 @@ run3() -> # name and address ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* -\\) )* # optional trailing comment",[extended]))), +\\) )* # optional trailing comment",[extended]))), <<"A missing angle <user.where">> = iolist_to_binary(join(re:split("A missing angle <user.where"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -5110,7 +5110,7 @@ run3() -> # name and address ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* -\\) )* # optional trailing comment",[extended,trim]))), +\\) )* # optional trailing comment",[extended,trim]))), <<"A missing angle <user.where">> = iolist_to_binary(join(re:split("A missing angle <user.where"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -5304,7 +5304,7 @@ run3() -> ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional trailing comment",[extended, - {parts,2}]))), + {parts,2}]))), <<"A missing angle <user.where">> = iolist_to_binary(join(re:split("A missing angle <user.where"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -5497,7 +5497,7 @@ run3() -> # name and address ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* -\\) )* # optional trailing comment",[extended]))), +\\) )* # optional trailing comment",[extended]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -5690,7 +5690,7 @@ run3() -> # name and address ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* -\\) )* # optional trailing comment",[extended,trim]))), +\\) )* # optional trailing comment",[extended,trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -5884,7 +5884,7 @@ run3() -> ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional trailing comment",[extended, - {parts,2}]))), + {parts,2}]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -6077,7 +6077,7 @@ run3() -> # name and address ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* -\\) )* # optional trailing comment",[extended]))), +\\) )* # optional trailing comment",[extended]))), <<"The quick brown fox">> = iolist_to_binary(join(re:split("The quick brown fox"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -6270,7 +6270,7 @@ run3() -> # name and address ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* -\\) )* # optional trailing comment",[extended,trim]))), +\\) )* # optional trailing comment",[extended,trim]))), <<"The quick brown fox">> = iolist_to_binary(join(re:split("The quick brown fox"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -6464,7 +6464,7 @@ run3() -> ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional trailing comment",[extended, - {parts,2}]))), + {parts,2}]))), <<"The quick brown fox">> = iolist_to_binary(join(re:split("The quick brown fox"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -6657,7 +6657,7 @@ run3() -> # name and address ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* -\\) )* # optional trailing comment",[extended]))), +\\) )* # optional trailing comment",[extended]))), <<"Alan Other <user.ain>">> = iolist_to_binary(join(re:split("Alan Other <user.ain>","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -7238,7 +7238,7 @@ run3() -> # address spec > # > # name and address -)",[extended,trim]))), +)",[extended,trim]))), <<"Alan Other <user.ain>">> = iolist_to_binary(join(re:split("Alan Other <user.ain>","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -7819,7 +7819,7 @@ run3() -> # address spec > # > # name and address -)",[extended,{parts,2}]))), +)",[extended,{parts,2}]))), <<"Alan Other <user.ain>">> = iolist_to_binary(join(re:split("Alan Other <user.ain>","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -8400,7 +8400,7 @@ run3() -> # address spec > # > # name and address -)",[extended]))), +)",[extended]))), <<"<user.ain>">> = iolist_to_binary(join(re:split("<user.ain>","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -8981,7 +8981,7 @@ run3() -> # address spec > # > # name and address -)",[extended,trim]))), +)",[extended,trim]))), <<"<user.ain>">> = iolist_to_binary(join(re:split("<user.ain>","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -9562,7 +9562,7 @@ run3() -> # address spec > # > # name and address -)",[extended,{parts,2}]))), +)",[extended,{parts,2}]))), <<"<user.ain>">> = iolist_to_binary(join(re:split("<user.ain>","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -10143,7 +10143,7 @@ run3() -> # address spec > # > # name and address -)",[extended]))), +)",[extended]))), <<"user.ain">> = iolist_to_binary(join(re:split("user.ain","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -10724,7 +10724,7 @@ run3() -> # address spec > # > # name and address -)",[extended,trim]))), +)",[extended,trim]))), <<"user.ain">> = iolist_to_binary(join(re:split("user.ain","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -11305,7 +11305,7 @@ run3() -> # address spec > # > # name and address -)",[extended,{parts,2}]))), +)",[extended,{parts,2}]))), <<"user.ain">> = iolist_to_binary(join(re:split("user.ain","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -11886,7 +11886,7 @@ run3() -> # address spec > # > # name and address -)",[extended]))), +)",[extended]))), <<"\"A. Other\" <user.1234.ain> (a comment)">> = iolist_to_binary(join(re:split("\"A. Other\" <user.1234.ain> (a comment)","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -12467,7 +12467,7 @@ run3() -> # address spec > # > # name and address -)",[extended,trim]))), +)",[extended,trim]))), <<"\"A. Other\" <user.1234.ain> (a comment)">> = iolist_to_binary(join(re:split("\"A. Other\" <user.1234.ain> (a comment)","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -13048,7 +13048,7 @@ run3() -> # address spec > # > # name and address -)",[extended,{parts,2}]))), +)",[extended,{parts,2}]))), <<"\"A. Other\" <user.1234.ain> (a comment)">> = iolist_to_binary(join(re:split("\"A. Other\" <user.1234.ain> (a comment)","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -13629,7 +13629,7 @@ run3() -> # address spec > # > # name and address -)",[extended]))), +)",[extended]))), <<"A. Other <user.1234.ain> (a comment)">> = iolist_to_binary(join(re:split("A. Other <user.1234.ain> (a comment)","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -14210,7 +14210,7 @@ run3() -> # address spec > # > # name and address -)",[extended,trim]))), +)",[extended,trim]))), <<"A. Other <user.1234.ain> (a comment)">> = iolist_to_binary(join(re:split("A. Other <user.1234.ain> (a comment)","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -14791,7 +14791,7 @@ run3() -> # address spec > # > # name and address -)",[extended,{parts,2}]))), +)",[extended,{parts,2}]))), <<"A. Other <user.1234.ain> (a comment)">> = iolist_to_binary(join(re:split("A. Other <user.1234.ain> (a comment)","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -15372,7 +15372,7 @@ run3() -> # address spec > # > # name and address -)",[extended]))), +)",[extended]))), <<"\"/s=user/ou=host/o=place/prmd=uu.yy/admd= /c=gb/\"-re.lay">> = iolist_to_binary(join(re:split("\"/s=user/ou=host/o=place/prmd=uu.yy/admd= /c=gb/\"-re.lay","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -15953,7 +15953,7 @@ run3() -> # address spec > # > # name and address -)",[extended,trim]))), +)",[extended,trim]))), <<"\"/s=user/ou=host/o=place/prmd=uu.yy/admd= /c=gb/\"-re.lay">> = iolist_to_binary(join(re:split("\"/s=user/ou=host/o=place/prmd=uu.yy/admd= /c=gb/\"-re.lay","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -16534,7 +16534,7 @@ run3() -> # address spec > # > # name and address -)",[extended,{parts,2}]))), +)",[extended,{parts,2}]))), <<"\"/s=user/ou=host/o=place/prmd=uu.yy/admd= /c=gb/\"-re.lay">> = iolist_to_binary(join(re:split("\"/s=user/ou=host/o=place/prmd=uu.yy/admd= /c=gb/\"-re.lay","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -17115,7 +17115,7 @@ run3() -> # address spec > # > # name and address -)",[extended]))), +)",[extended]))), <<"A missing angle <user.where">> = iolist_to_binary(join(re:split("A missing angle <user.where","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -17696,7 +17696,7 @@ run3() -> # address spec > # > # name and address -)",[extended,trim]))), +)",[extended,trim]))), <<"A missing angle <user.where">> = iolist_to_binary(join(re:split("A missing angle <user.where","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -18277,7 +18277,7 @@ run3() -> # address spec > # > # name and address -)",[extended,{parts,2}]))), +)",[extended,{parts,2}]))), <<"A missing angle <user.where">> = iolist_to_binary(join(re:split("A missing angle <user.where","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -18858,7 +18858,7 @@ run3() -> # address spec > # > # name and address -)",[extended]))), +)",[extended]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -19439,7 +19439,7 @@ run3() -> # address spec > # > # name and address -)",[extended,trim]))), +)",[extended,trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -20020,7 +20020,7 @@ run3() -> # address spec > # > # name and address -)",[extended,{parts,2}]))), +)",[extended,{parts,2}]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -20601,7 +20601,7 @@ run3() -> # address spec > # > # name and address -)",[extended]))), +)",[extended]))), <<"The quick brown fox">> = iolist_to_binary(join(re:split("The quick brown fox","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -21182,7 +21182,7 @@ run3() -> # address spec > # > # name and address -)",[extended,trim]))), +)",[extended,trim]))), <<"The quick brown fox">> = iolist_to_binary(join(re:split("The quick brown fox","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -21763,7 +21763,7 @@ run3() -> # address spec > # > # name and address -)",[extended,{parts,2}]))), +)",[extended,{parts,2}]))), <<"The quick brown fox">> = iolist_to_binary(join(re:split("The quick brown fox","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -22344,5763 +22344,5763 @@ run3() -> # address spec > # > # name and address -)",[extended]))), - <<"abcdefpqrxyz0AB">> = iolist_to_binary(join(re:split("abcdefpqrxyz0AB","abc\\0def\\00pqr\\000xyz\\0000AB",[trim]))), +)",[extended]))), + <<"abcdefpqrxyz0AB">> = iolist_to_binary(join(re:split("abcdefpqrxyz0AB","abc\\0def\\00pqr\\000xyz\\0000AB",[trim]))), <<"abcdefpqrxyz0AB">> = iolist_to_binary(join(re:split("abcdefpqrxyz0AB","abc\\0def\\00pqr\\000xyz\\0000AB",[{parts, - 2}]))), - <<"abcdefpqrxyz0AB">> = iolist_to_binary(join(re:split("abcdefpqrxyz0AB","abc\\0def\\00pqr\\000xyz\\0000AB",[]))), - <<"abc456 abcdefpqrxyz0ABCDE">> = iolist_to_binary(join(re:split("abc456 abcdefpqrxyz0ABCDE","abc\\0def\\00pqr\\000xyz\\0000AB",[trim]))), + 2}]))), + <<"abcdefpqrxyz0AB">> = iolist_to_binary(join(re:split("abcdefpqrxyz0AB","abc\\0def\\00pqr\\000xyz\\0000AB",[]))), + <<"abc456 abcdefpqrxyz0ABCDE">> = iolist_to_binary(join(re:split("abc456 abcdefpqrxyz0ABCDE","abc\\0def\\00pqr\\000xyz\\0000AB",[trim]))), <<"abc456 abcdefpqrxyz0ABCDE">> = iolist_to_binary(join(re:split("abc456 abcdefpqrxyz0ABCDE","abc\\0def\\00pqr\\000xyz\\0000AB",[{parts, - 2}]))), - <<"abc456 abcdefpqrxyz0ABCDE">> = iolist_to_binary(join(re:split("abc456 abcdefpqrxyz0ABCDE","abc\\0def\\00pqr\\000xyz\\0000AB",[]))), - <<"abc
efpqr0xyz00AB">> = iolist_to_binary(join(re:split("abc
efpqr0xyz00AB","abc\\x0def\\x00pqr\\x000xyz\\x0000AB",[trim]))), + 2}]))), + <<"abc456 abcdefpqrxyz0ABCDE">> = iolist_to_binary(join(re:split("abc456 abcdefpqrxyz0ABCDE","abc\\0def\\00pqr\\000xyz\\0000AB",[]))), + <<"abc
efpqr0xyz00AB">> = iolist_to_binary(join(re:split("abc
efpqr0xyz00AB","abc\\x0def\\x00pqr\\x000xyz\\x0000AB",[trim]))), <<"abc
efpqr0xyz00AB">> = iolist_to_binary(join(re:split("abc
efpqr0xyz00AB","abc\\x0def\\x00pqr\\x000xyz\\x0000AB",[{parts, - 2}]))), - <<"abc
efpqr0xyz00AB">> = iolist_to_binary(join(re:split("abc
efpqr0xyz00AB","abc\\x0def\\x00pqr\\x000xyz\\x0000AB",[]))), - <<"abc456 abc
efpqr0xyz00ABCDE">> = iolist_to_binary(join(re:split("abc456 abc
efpqr0xyz00ABCDE","abc\\x0def\\x00pqr\\x000xyz\\x0000AB",[trim]))), + 2}]))), + <<"abc
efpqr0xyz00AB">> = iolist_to_binary(join(re:split("abc
efpqr0xyz00AB","abc\\x0def\\x00pqr\\x000xyz\\x0000AB",[]))), + <<"abc456 abc
efpqr0xyz00ABCDE">> = iolist_to_binary(join(re:split("abc456 abc
efpqr0xyz00ABCDE","abc\\x0def\\x00pqr\\x000xyz\\x0000AB",[trim]))), <<"abc456 abc
efpqr0xyz00ABCDE">> = iolist_to_binary(join(re:split("abc456 abc
efpqr0xyz00ABCDE","abc\\x0def\\x00pqr\\x000xyz\\x0000AB",[{parts, - 2}]))), - <<"abc456 abc
efpqr0xyz00ABCDE">> = iolist_to_binary(join(re:split("abc456 abc
efpqr0xyz00ABCDE","abc\\x0def\\x00pqr\\x000xyz\\x0000AB",[]))), - <<"A">> = iolist_to_binary(join(re:split("A","^[\\000-\\037]",[trim]))), + 2}]))), + <<"abc456 abc
efpqr0xyz00ABCDE">> = iolist_to_binary(join(re:split("abc456 abc
efpqr0xyz00ABCDE","abc\\x0def\\x00pqr\\x000xyz\\x0000AB",[]))), + <<"A">> = iolist_to_binary(join(re:split("A","^[\\000-\\037]",[trim]))), <<"A">> = iolist_to_binary(join(re:split("A","^[\\000-\\037]",[{parts, - 2}]))), - <<"A">> = iolist_to_binary(join(re:split("A","^[\\000-\\037]",[]))), - <<":B">> = iolist_to_binary(join(re:split("B","^[\\000-\\037]",[trim]))), + 2}]))), + <<"A">> = iolist_to_binary(join(re:split("A","^[\\000-\\037]",[]))), + <<":B">> = iolist_to_binary(join(re:split("B","^[\\000-\\037]",[trim]))), <<":B">> = iolist_to_binary(join(re:split("B","^[\\000-\\037]",[{parts, - 2}]))), - <<":B">> = iolist_to_binary(join(re:split("B","^[\\000-\\037]",[]))), - <<":C">> = iolist_to_binary(join(re:split("C","^[\\000-\\037]",[trim]))), + 2}]))), + <<":B">> = iolist_to_binary(join(re:split("B","^[\\000-\\037]",[]))), + <<":C">> = iolist_to_binary(join(re:split("C","^[\\000-\\037]",[trim]))), <<":C">> = iolist_to_binary(join(re:split("C","^[\\000-\\037]",[{parts, - 2}]))), - <<":C">> = iolist_to_binary(join(re:split("C","^[\\000-\\037]",[]))), - <<"">> = iolist_to_binary(join(re:split("","\\0*",[trim]))), + 2}]))), + <<":C">> = iolist_to_binary(join(re:split("C","^[\\000-\\037]",[]))), + <<"">> = iolist_to_binary(join(re:split("","\\0*",[trim]))), <<"">> = iolist_to_binary(join(re:split("","\\0*",[{parts, - 2}]))), - <<"">> = iolist_to_binary(join(re:split("","\\0*",[]))), - <<"The AZ">> = iolist_to_binary(join(re:split("The AZ","A\\x0{2,3}Z",[trim]))), + 2}]))), + <<"">> = iolist_to_binary(join(re:split("","\\0*",[]))), + <<"The AZ">> = iolist_to_binary(join(re:split("The AZ","A\\x0{2,3}Z",[trim]))), <<"The AZ">> = iolist_to_binary(join(re:split("The AZ","A\\x0{2,3}Z",[{parts, - 2}]))), - <<"The AZ">> = iolist_to_binary(join(re:split("The AZ","A\\x0{2,3}Z",[]))), - <<"An AZ">> = iolist_to_binary(join(re:split("An AZ","A\\x0{2,3}Z",[trim]))), + 2}]))), + <<"The AZ">> = iolist_to_binary(join(re:split("The AZ","A\\x0{2,3}Z",[]))), + <<"An AZ">> = iolist_to_binary(join(re:split("An AZ","A\\x0{2,3}Z",[trim]))), <<"An AZ">> = iolist_to_binary(join(re:split("An AZ","A\\x0{2,3}Z",[{parts, - 2}]))), - <<"An AZ">> = iolist_to_binary(join(re:split("An AZ","A\\x0{2,3}Z",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","A\\x0{2,3}Z",[trim]))), + 2}]))), + <<"An AZ">> = iolist_to_binary(join(re:split("An AZ","A\\x0{2,3}Z",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","A\\x0{2,3}Z",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","A\\x0{2,3}Z",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","A\\x0{2,3}Z",[]))), - <<"AZ">> = iolist_to_binary(join(re:split("AZ","A\\x0{2,3}Z",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","A\\x0{2,3}Z",[]))), + <<"AZ">> = iolist_to_binary(join(re:split("AZ","A\\x0{2,3}Z",[trim]))), <<"AZ">> = iolist_to_binary(join(re:split("AZ","A\\x0{2,3}Z",[{parts, - 2}]))), - <<"AZ">> = iolist_to_binary(join(re:split("AZ","A\\x0{2,3}Z",[]))), - <<"AZ">> = iolist_to_binary(join(re:split("AZ","A\\x0{2,3}Z",[trim]))), + 2}]))), + <<"AZ">> = iolist_to_binary(join(re:split("AZ","A\\x0{2,3}Z",[]))), + <<"AZ">> = iolist_to_binary(join(re:split("AZ","A\\x0{2,3}Z",[trim]))), <<"AZ">> = iolist_to_binary(join(re:split("AZ","A\\x0{2,3}Z",[{parts, - 2}]))), - <<"AZ">> = iolist_to_binary(join(re:split("AZ","A\\x0{2,3}Z",[]))), - <<":cow:bell">> = iolist_to_binary(join(re:split("cowcowbell","^(cow|)\\1(bell)",[trim]))), + 2}]))), + <<"AZ">> = iolist_to_binary(join(re:split("AZ","A\\x0{2,3}Z",[]))), + <<":cow:bell">> = iolist_to_binary(join(re:split("cowcowbell","^(cow|)\\1(bell)",[trim]))), <<":cow:bell:">> = iolist_to_binary(join(re:split("cowcowbell","^(cow|)\\1(bell)",[{parts, - 2}]))), - <<":cow:bell:">> = iolist_to_binary(join(re:split("cowcowbell","^(cow|)\\1(bell)",[]))), - <<"::bell">> = iolist_to_binary(join(re:split("bell","^(cow|)\\1(bell)",[trim]))), + 2}]))), + <<":cow:bell:">> = iolist_to_binary(join(re:split("cowcowbell","^(cow|)\\1(bell)",[]))), + <<"::bell">> = iolist_to_binary(join(re:split("bell","^(cow|)\\1(bell)",[trim]))), <<"::bell:">> = iolist_to_binary(join(re:split("bell","^(cow|)\\1(bell)",[{parts, - 2}]))), - <<"::bell:">> = iolist_to_binary(join(re:split("bell","^(cow|)\\1(bell)",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(cow|)\\1(bell)",[trim]))), + 2}]))), + <<"::bell:">> = iolist_to_binary(join(re:split("bell","^(cow|)\\1(bell)",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(cow|)\\1(bell)",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(cow|)\\1(bell)",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(cow|)\\1(bell)",[]))), - <<"cowbell">> = iolist_to_binary(join(re:split("cowbell","^(cow|)\\1(bell)",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(cow|)\\1(bell)",[]))), + <<"cowbell">> = iolist_to_binary(join(re:split("cowbell","^(cow|)\\1(bell)",[trim]))), <<"cowbell">> = iolist_to_binary(join(re:split("cowbell","^(cow|)\\1(bell)",[{parts, - 2}]))), - <<"cowbell">> = iolist_to_binary(join(re:split("cowbell","^(cow|)\\1(bell)",[]))), - <<":abc">> = iolist_to_binary(join(re:split(" abc","^\\s",[trim]))), + 2}]))), + <<"cowbell">> = iolist_to_binary(join(re:split("cowbell","^(cow|)\\1(bell)",[]))), + <<":abc">> = iolist_to_binary(join(re:split(" abc","^\\s",[trim]))), <<":abc">> = iolist_to_binary(join(re:split(" abc","^\\s",[{parts, - 2}]))), - <<":abc">> = iolist_to_binary(join(re:split(" abc","^\\s",[]))), - <<":abc">> = iolist_to_binary(join(re:split("abc","^\\s",[trim]))), + 2}]))), + <<":abc">> = iolist_to_binary(join(re:split(" abc","^\\s",[]))), + <<":abc">> = iolist_to_binary(join(re:split("abc","^\\s",[trim]))), <<":abc">> = iolist_to_binary(join(re:split("abc","^\\s",[{parts, - 2}]))), - <<":abc">> = iolist_to_binary(join(re:split("abc","^\\s",[]))), + 2}]))), + <<":abc">> = iolist_to_binary(join(re:split("abc","^\\s",[]))), <<":abc">> = iolist_to_binary(join(re:split(" -abc","^\\s",[trim]))), +abc","^\\s",[trim]))), <<":abc">> = iolist_to_binary(join(re:split(" -abc","^\\s",[{parts,2}]))), +abc","^\\s",[{parts,2}]))), <<":abc">> = iolist_to_binary(join(re:split(" -abc","^\\s",[]))), - <<":abc">> = iolist_to_binary(join(re:split("
abc","^\\s",[trim]))), +abc","^\\s",[]))), + <<":abc">> = iolist_to_binary(join(re:split("
abc","^\\s",[trim]))), <<":abc">> = iolist_to_binary(join(re:split("
abc","^\\s",[{parts, - 2}]))), - <<":abc">> = iolist_to_binary(join(re:split("
abc","^\\s",[]))), - <<":abc">> = iolist_to_binary(join(re:split(" abc","^\\s",[trim]))), + 2}]))), + <<":abc">> = iolist_to_binary(join(re:split("
abc","^\\s",[]))), + <<":abc">> = iolist_to_binary(join(re:split(" abc","^\\s",[trim]))), <<":abc">> = iolist_to_binary(join(re:split(" abc","^\\s",[{parts, - 2}]))), - <<":abc">> = iolist_to_binary(join(re:split(" abc","^\\s",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\s",[trim]))), + 2}]))), + <<":abc">> = iolist_to_binary(join(re:split(" abc","^\\s",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\s",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\s",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\s",[]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","^\\s",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\s",[]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","^\\s",[trim]))), <<"abc">> = iolist_to_binary(join(re:split("abc","^\\s",[{parts, - 2}]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","^\\s",[]))), + 2}]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","^\\s",[]))), ok. run4() -> <<"">> = iolist_to_binary(join(re:split("abc","^a b - c",[extended,trim]))), + c",[extended,trim]))), <<":">> = iolist_to_binary(join(re:split("abc","^a b - c",[extended,{parts,2}]))), + c",[extended,{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("abc","^a b - c",[extended]))), - <<":a">> = iolist_to_binary(join(re:split("ab","^(a|)\\1*b",[trim]))), + c",[extended]))), + <<":a">> = iolist_to_binary(join(re:split("ab","^(a|)\\1*b",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("ab","^(a|)\\1*b",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("ab","^(a|)\\1*b",[]))), - <<":a">> = iolist_to_binary(join(re:split("aaaab","^(a|)\\1*b",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("ab","^(a|)\\1*b",[]))), + <<":a">> = iolist_to_binary(join(re:split("aaaab","^(a|)\\1*b",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aaaab","^(a|)\\1*b",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aaaab","^(a|)\\1*b",[]))), - <<"">> = iolist_to_binary(join(re:split("b","^(a|)\\1*b",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aaaab","^(a|)\\1*b",[]))), + <<"">> = iolist_to_binary(join(re:split("b","^(a|)\\1*b",[trim]))), <<"::">> = iolist_to_binary(join(re:split("b","^(a|)\\1*b",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("b","^(a|)\\1*b",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1*b",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("b","^(a|)\\1*b",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1*b",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1*b",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1*b",[]))), - <<"acb">> = iolist_to_binary(join(re:split("acb","^(a|)\\1*b",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1*b",[]))), + <<"acb">> = iolist_to_binary(join(re:split("acb","^(a|)\\1*b",[trim]))), <<"acb">> = iolist_to_binary(join(re:split("acb","^(a|)\\1*b",[{parts, - 2}]))), - <<"acb">> = iolist_to_binary(join(re:split("acb","^(a|)\\1*b",[]))), - <<":a">> = iolist_to_binary(join(re:split("aab","^(a|)\\1+b",[trim]))), + 2}]))), + <<"acb">> = iolist_to_binary(join(re:split("acb","^(a|)\\1*b",[]))), + <<":a">> = iolist_to_binary(join(re:split("aab","^(a|)\\1+b",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aab","^(a|)\\1+b",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aab","^(a|)\\1+b",[]))), - <<":a">> = iolist_to_binary(join(re:split("aaaab","^(a|)\\1+b",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aab","^(a|)\\1+b",[]))), + <<":a">> = iolist_to_binary(join(re:split("aaaab","^(a|)\\1+b",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aaaab","^(a|)\\1+b",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aaaab","^(a|)\\1+b",[]))), - <<"">> = iolist_to_binary(join(re:split("b","^(a|)\\1+b",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aaaab","^(a|)\\1+b",[]))), + <<"">> = iolist_to_binary(join(re:split("b","^(a|)\\1+b",[trim]))), <<"::">> = iolist_to_binary(join(re:split("b","^(a|)\\1+b",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("b","^(a|)\\1+b",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1+b",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("b","^(a|)\\1+b",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1+b",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1+b",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1+b",[]))), - <<"ab">> = iolist_to_binary(join(re:split("ab","^(a|)\\1+b",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1+b",[]))), + <<"ab">> = iolist_to_binary(join(re:split("ab","^(a|)\\1+b",[trim]))), <<"ab">> = iolist_to_binary(join(re:split("ab","^(a|)\\1+b",[{parts, - 2}]))), - <<"ab">> = iolist_to_binary(join(re:split("ab","^(a|)\\1+b",[]))), - <<":a">> = iolist_to_binary(join(re:split("ab","^(a|)\\1?b",[trim]))), + 2}]))), + <<"ab">> = iolist_to_binary(join(re:split("ab","^(a|)\\1+b",[]))), + <<":a">> = iolist_to_binary(join(re:split("ab","^(a|)\\1?b",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("ab","^(a|)\\1?b",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("ab","^(a|)\\1?b",[]))), - <<":a">> = iolist_to_binary(join(re:split("aab","^(a|)\\1?b",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("ab","^(a|)\\1?b",[]))), + <<":a">> = iolist_to_binary(join(re:split("aab","^(a|)\\1?b",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aab","^(a|)\\1?b",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aab","^(a|)\\1?b",[]))), - <<"">> = iolist_to_binary(join(re:split("b","^(a|)\\1?b",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aab","^(a|)\\1?b",[]))), + <<"">> = iolist_to_binary(join(re:split("b","^(a|)\\1?b",[trim]))), <<"::">> = iolist_to_binary(join(re:split("b","^(a|)\\1?b",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("b","^(a|)\\1?b",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1?b",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("b","^(a|)\\1?b",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1?b",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1?b",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1?b",[]))), - <<"acb">> = iolist_to_binary(join(re:split("acb","^(a|)\\1?b",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1?b",[]))), + <<"acb">> = iolist_to_binary(join(re:split("acb","^(a|)\\1?b",[trim]))), <<"acb">> = iolist_to_binary(join(re:split("acb","^(a|)\\1?b",[{parts, - 2}]))), - <<"acb">> = iolist_to_binary(join(re:split("acb","^(a|)\\1?b",[]))), - <<":a">> = iolist_to_binary(join(re:split("aaab","^(a|)\\1{2}b",[trim]))), + 2}]))), + <<"acb">> = iolist_to_binary(join(re:split("acb","^(a|)\\1?b",[]))), + <<":a">> = iolist_to_binary(join(re:split("aaab","^(a|)\\1{2}b",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aaab","^(a|)\\1{2}b",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aaab","^(a|)\\1{2}b",[]))), - <<"">> = iolist_to_binary(join(re:split("b","^(a|)\\1{2}b",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aaab","^(a|)\\1{2}b",[]))), + <<"">> = iolist_to_binary(join(re:split("b","^(a|)\\1{2}b",[trim]))), <<"::">> = iolist_to_binary(join(re:split("b","^(a|)\\1{2}b",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("b","^(a|)\\1{2}b",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1{2}b",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("b","^(a|)\\1{2}b",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1{2}b",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1{2}b",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1{2}b",[]))), - <<"ab">> = iolist_to_binary(join(re:split("ab","^(a|)\\1{2}b",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1{2}b",[]))), + <<"ab">> = iolist_to_binary(join(re:split("ab","^(a|)\\1{2}b",[trim]))), <<"ab">> = iolist_to_binary(join(re:split("ab","^(a|)\\1{2}b",[{parts, - 2}]))), - <<"ab">> = iolist_to_binary(join(re:split("ab","^(a|)\\1{2}b",[]))), - <<"aab">> = iolist_to_binary(join(re:split("aab","^(a|)\\1{2}b",[trim]))), + 2}]))), + <<"ab">> = iolist_to_binary(join(re:split("ab","^(a|)\\1{2}b",[]))), + <<"aab">> = iolist_to_binary(join(re:split("aab","^(a|)\\1{2}b",[trim]))), <<"aab">> = iolist_to_binary(join(re:split("aab","^(a|)\\1{2}b",[{parts, - 2}]))), - <<"aab">> = iolist_to_binary(join(re:split("aab","^(a|)\\1{2}b",[]))), - <<"aaaab">> = iolist_to_binary(join(re:split("aaaab","^(a|)\\1{2}b",[trim]))), + 2}]))), + <<"aab">> = iolist_to_binary(join(re:split("aab","^(a|)\\1{2}b",[]))), + <<"aaaab">> = iolist_to_binary(join(re:split("aaaab","^(a|)\\1{2}b",[trim]))), <<"aaaab">> = iolist_to_binary(join(re:split("aaaab","^(a|)\\1{2}b",[{parts, - 2}]))), - <<"aaaab">> = iolist_to_binary(join(re:split("aaaab","^(a|)\\1{2}b",[]))), - <<":a">> = iolist_to_binary(join(re:split("aaab","^(a|)\\1{2,3}b",[trim]))), + 2}]))), + <<"aaaab">> = iolist_to_binary(join(re:split("aaaab","^(a|)\\1{2}b",[]))), + <<":a">> = iolist_to_binary(join(re:split("aaab","^(a|)\\1{2,3}b",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aaab","^(a|)\\1{2,3}b",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aaab","^(a|)\\1{2,3}b",[]))), - <<":a">> = iolist_to_binary(join(re:split("aaaab","^(a|)\\1{2,3}b",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aaab","^(a|)\\1{2,3}b",[]))), + <<":a">> = iolist_to_binary(join(re:split("aaaab","^(a|)\\1{2,3}b",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aaaab","^(a|)\\1{2,3}b",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aaaab","^(a|)\\1{2,3}b",[]))), - <<"">> = iolist_to_binary(join(re:split("b","^(a|)\\1{2,3}b",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aaaab","^(a|)\\1{2,3}b",[]))), + <<"">> = iolist_to_binary(join(re:split("b","^(a|)\\1{2,3}b",[trim]))), <<"::">> = iolist_to_binary(join(re:split("b","^(a|)\\1{2,3}b",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("b","^(a|)\\1{2,3}b",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1{2,3}b",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("b","^(a|)\\1{2,3}b",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1{2,3}b",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1{2,3}b",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1{2,3}b",[]))), - <<"ab">> = iolist_to_binary(join(re:split("ab","^(a|)\\1{2,3}b",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1{2,3}b",[]))), + <<"ab">> = iolist_to_binary(join(re:split("ab","^(a|)\\1{2,3}b",[trim]))), <<"ab">> = iolist_to_binary(join(re:split("ab","^(a|)\\1{2,3}b",[{parts, - 2}]))), - <<"ab">> = iolist_to_binary(join(re:split("ab","^(a|)\\1{2,3}b",[]))), - <<"aab">> = iolist_to_binary(join(re:split("aab","^(a|)\\1{2,3}b",[trim]))), + 2}]))), + <<"ab">> = iolist_to_binary(join(re:split("ab","^(a|)\\1{2,3}b",[]))), + <<"aab">> = iolist_to_binary(join(re:split("aab","^(a|)\\1{2,3}b",[trim]))), <<"aab">> = iolist_to_binary(join(re:split("aab","^(a|)\\1{2,3}b",[{parts, - 2}]))), - <<"aab">> = iolist_to_binary(join(re:split("aab","^(a|)\\1{2,3}b",[]))), - <<"aaaaab">> = iolist_to_binary(join(re:split("aaaaab","^(a|)\\1{2,3}b",[trim]))), + 2}]))), + <<"aab">> = iolist_to_binary(join(re:split("aab","^(a|)\\1{2,3}b",[]))), + <<"aaaaab">> = iolist_to_binary(join(re:split("aaaaab","^(a|)\\1{2,3}b",[trim]))), <<"aaaaab">> = iolist_to_binary(join(re:split("aaaaab","^(a|)\\1{2,3}b",[{parts, - 2}]))), - <<"aaaaab">> = iolist_to_binary(join(re:split("aaaaab","^(a|)\\1{2,3}b",[]))), - <<"">> = iolist_to_binary(join(re:split("abbbbc","ab{1,3}bc",[trim]))), + 2}]))), + <<"aaaaab">> = iolist_to_binary(join(re:split("aaaaab","^(a|)\\1{2,3}b",[]))), + <<"">> = iolist_to_binary(join(re:split("abbbbc","ab{1,3}bc",[trim]))), <<":">> = iolist_to_binary(join(re:split("abbbbc","ab{1,3}bc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abbbbc","ab{1,3}bc",[]))), - <<"">> = iolist_to_binary(join(re:split("abbbc","ab{1,3}bc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abbbbc","ab{1,3}bc",[]))), + <<"">> = iolist_to_binary(join(re:split("abbbc","ab{1,3}bc",[trim]))), <<":">> = iolist_to_binary(join(re:split("abbbc","ab{1,3}bc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abbbc","ab{1,3}bc",[]))), - <<"">> = iolist_to_binary(join(re:split("abbc","ab{1,3}bc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abbbc","ab{1,3}bc",[]))), + <<"">> = iolist_to_binary(join(re:split("abbc","ab{1,3}bc",[trim]))), <<":">> = iolist_to_binary(join(re:split("abbc","ab{1,3}bc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abbc","ab{1,3}bc",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab{1,3}bc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abbc","ab{1,3}bc",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab{1,3}bc",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab{1,3}bc",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab{1,3}bc",[]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","ab{1,3}bc",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab{1,3}bc",[]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","ab{1,3}bc",[trim]))), <<"abc">> = iolist_to_binary(join(re:split("abc","ab{1,3}bc",[{parts, - 2}]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","ab{1,3}bc",[]))), - <<"abbbbbc">> = iolist_to_binary(join(re:split("abbbbbc","ab{1,3}bc",[trim]))), + 2}]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","ab{1,3}bc",[]))), + <<"abbbbbc">> = iolist_to_binary(join(re:split("abbbbbc","ab{1,3}bc",[trim]))), <<"abbbbbc">> = iolist_to_binary(join(re:split("abbbbbc","ab{1,3}bc",[{parts, - 2}]))), - <<"abbbbbc">> = iolist_to_binary(join(re:split("abbbbbc","ab{1,3}bc",[]))), - <<":track1:title:Blah blah blah">> = iolist_to_binary(join(re:split("track1.title:TBlah blah blah","([^.]*)\\.([^:]*):[T ]+(.*)",[trim]))), + 2}]))), + <<"abbbbbc">> = iolist_to_binary(join(re:split("abbbbbc","ab{1,3}bc",[]))), + <<":track1:title:Blah blah blah">> = iolist_to_binary(join(re:split("track1.title:TBlah blah blah","([^.]*)\\.([^:]*):[T ]+(.*)",[trim]))), <<":track1:title:Blah blah blah:">> = iolist_to_binary(join(re:split("track1.title:TBlah blah blah","([^.]*)\\.([^:]*):[T ]+(.*)",[{parts, - 2}]))), - <<":track1:title:Blah blah blah:">> = iolist_to_binary(join(re:split("track1.title:TBlah blah blah","([^.]*)\\.([^:]*):[T ]+(.*)",[]))), + 2}]))), + <<":track1:title:Blah blah blah:">> = iolist_to_binary(join(re:split("track1.title:TBlah blah blah","([^.]*)\\.([^:]*):[T ]+(.*)",[]))), <<":track1:title:Blah blah blah">> = iolist_to_binary(join(re:split("track1.title:TBlah blah blah","([^.]*)\\.([^:]*):[T ]+(.*)",[caseless, - trim]))), + trim]))), <<":track1:title:Blah blah blah:">> = iolist_to_binary(join(re:split("track1.title:TBlah blah blah","([^.]*)\\.([^:]*):[T ]+(.*)",[caseless, {parts, - 2}]))), - <<":track1:title:Blah blah blah:">> = iolist_to_binary(join(re:split("track1.title:TBlah blah blah","([^.]*)\\.([^:]*):[T ]+(.*)",[caseless]))), + 2}]))), + <<":track1:title:Blah blah blah:">> = iolist_to_binary(join(re:split("track1.title:TBlah blah blah","([^.]*)\\.([^:]*):[T ]+(.*)",[caseless]))), <<":track1:title:Blah blah blah">> = iolist_to_binary(join(re:split("track1.title:TBlah blah blah","([^.]*)\\.([^:]*):[t ]+(.*)",[caseless, - trim]))), + trim]))), <<":track1:title:Blah blah blah:">> = iolist_to_binary(join(re:split("track1.title:TBlah blah blah","([^.]*)\\.([^:]*):[t ]+(.*)",[caseless, {parts, - 2}]))), - <<":track1:title:Blah blah blah:">> = iolist_to_binary(join(re:split("track1.title:TBlah blah blah","([^.]*)\\.([^:]*):[t ]+(.*)",[caseless]))), - <<"">> = iolist_to_binary(join(re:split("WXY_^abc","^[W-c]+$",[trim]))), + 2}]))), + <<":track1:title:Blah blah blah:">> = iolist_to_binary(join(re:split("track1.title:TBlah blah blah","([^.]*)\\.([^:]*):[t ]+(.*)",[caseless]))), + <<"">> = iolist_to_binary(join(re:split("WXY_^abc","^[W-c]+$",[trim]))), <<":">> = iolist_to_binary(join(re:split("WXY_^abc","^[W-c]+$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("WXY_^abc","^[W-c]+$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[W-c]+$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("WXY_^abc","^[W-c]+$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[W-c]+$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[W-c]+$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[W-c]+$",[]))), - <<"wxy">> = iolist_to_binary(join(re:split("wxy","^[W-c]+$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[W-c]+$",[]))), + <<"wxy">> = iolist_to_binary(join(re:split("wxy","^[W-c]+$",[trim]))), <<"wxy">> = iolist_to_binary(join(re:split("wxy","^[W-c]+$",[{parts, - 2}]))), - <<"wxy">> = iolist_to_binary(join(re:split("wxy","^[W-c]+$",[]))), + 2}]))), + <<"wxy">> = iolist_to_binary(join(re:split("wxy","^[W-c]+$",[]))), <<"">> = iolist_to_binary(join(re:split("WXY_^abc","^[W-c]+$",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("WXY_^abc","^[W-c]+$",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("WXY_^abc","^[W-c]+$",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("WXY_^abc","^[W-c]+$",[caseless]))), <<"">> = iolist_to_binary(join(re:split("wxy_^ABC","^[W-c]+$",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("wxy_^ABC","^[W-c]+$",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("wxy_^ABC","^[W-c]+$",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("wxy_^ABC","^[W-c]+$",[caseless]))), <<"">> = iolist_to_binary(join(re:split("WXY_^abc","^[\\x3f-\\x5F]+$",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("WXY_^abc","^[\\x3f-\\x5F]+$",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("WXY_^abc","^[\\x3f-\\x5F]+$",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("WXY_^abc","^[\\x3f-\\x5F]+$",[caseless]))), <<"">> = iolist_to_binary(join(re:split("wxy_^ABC","^[\\x3f-\\x5F]+$",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("wxy_^ABC","^[\\x3f-\\x5F]+$",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("wxy_^ABC","^[\\x3f-\\x5F]+$",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("wxy_^ABC","^[\\x3f-\\x5F]+$",[caseless]))), <<"">> = iolist_to_binary(join(re:split("abc","^abc$",[multiline, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("abc","^abc$",[multiline, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","^abc$",[multiline]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","^abc$",[multiline]))), <<"qqq ">> = iolist_to_binary(join(re:split("qqq -abc","^abc$",[multiline,trim]))), +abc","^abc$",[multiline,trim]))), <<"qqq :">> = iolist_to_binary(join(re:split("qqq -abc","^abc$",[multiline,{parts,2}]))), +abc","^abc$",[multiline,{parts,2}]))), <<"qqq :">> = iolist_to_binary(join(re:split("qqq -abc","^abc$",[multiline]))), +abc","^abc$",[multiline]))), <<": zzz">> = iolist_to_binary(join(re:split("abc -zzz","^abc$",[multiline,trim]))), +zzz","^abc$",[multiline,trim]))), <<": zzz">> = iolist_to_binary(join(re:split("abc -zzz","^abc$",[multiline,{parts,2}]))), +zzz","^abc$",[multiline,{parts,2}]))), <<": zzz">> = iolist_to_binary(join(re:split("abc -zzz","^abc$",[multiline]))), +zzz","^abc$",[multiline]))), <<"qqq : zzz">> = iolist_to_binary(join(re:split("qqq abc -zzz","^abc$",[multiline,trim]))), +zzz","^abc$",[multiline,trim]))), <<"qqq : zzz">> = iolist_to_binary(join(re:split("qqq abc -zzz","^abc$",[multiline,{parts,2}]))), +zzz","^abc$",[multiline,{parts,2}]))), <<"qqq : zzz">> = iolist_to_binary(join(re:split("qqq abc -zzz","^abc$",[multiline]))), - <<"">> = iolist_to_binary(join(re:split("abc","^abc$",[trim]))), +zzz","^abc$",[multiline]))), + <<"">> = iolist_to_binary(join(re:split("abc","^abc$",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","^abc$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","^abc$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^abc$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","^abc$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^abc$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^abc$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^abc$",[]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^abc$",[]))), <<"qqq abc">> = iolist_to_binary(join(re:split("qqq -abc","^abc$",[trim]))), +abc","^abc$",[trim]))), <<"qqq abc">> = iolist_to_binary(join(re:split("qqq -abc","^abc$",[{parts,2}]))), +abc","^abc$",[{parts,2}]))), <<"qqq abc">> = iolist_to_binary(join(re:split("qqq -abc","^abc$",[]))), +abc","^abc$",[]))), <<"abc zzz">> = iolist_to_binary(join(re:split("abc -zzz","^abc$",[trim]))), +zzz","^abc$",[trim]))), <<"abc zzz">> = iolist_to_binary(join(re:split("abc -zzz","^abc$",[{parts,2}]))), +zzz","^abc$",[{parts,2}]))), <<"abc zzz">> = iolist_to_binary(join(re:split("abc -zzz","^abc$",[]))), +zzz","^abc$",[]))), <<"qqq abc zzz">> = iolist_to_binary(join(re:split("qqq abc -zzz","^abc$",[trim]))), +zzz","^abc$",[trim]))), <<"qqq abc zzz">> = iolist_to_binary(join(re:split("qqq abc -zzz","^abc$",[{parts,2}]))), +zzz","^abc$",[{parts,2}]))), <<"qqq abc zzz">> = iolist_to_binary(join(re:split("qqq abc -zzz","^abc$",[]))), +zzz","^abc$",[]))), <<"">> = iolist_to_binary(join(re:split("abc","\\Aabc\\Z",[multiline, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("abc","\\Aabc\\Z",[multiline, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","\\Aabc\\Z",[multiline]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","\\Aabc\\Z",[multiline]))), <<"">> = iolist_to_binary(join(re:split("abc","\\Aabc\\Z",[multiline, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("abc","\\Aabc\\Z",[multiline, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","\\Aabc\\Z",[multiline]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","\\Aabc\\Z",[multiline]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\Aabc\\Z",[multiline, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\Aabc\\Z",[multiline, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\Aabc\\Z",[multiline]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\Aabc\\Z",[multiline]))), <<"qqq abc">> = iolist_to_binary(join(re:split("qqq -abc","\\Aabc\\Z",[multiline,trim]))), +abc","\\Aabc\\Z",[multiline,trim]))), <<"qqq abc">> = iolist_to_binary(join(re:split("qqq -abc","\\Aabc\\Z",[multiline,{parts,2}]))), +abc","\\Aabc\\Z",[multiline,{parts,2}]))), <<"qqq abc">> = iolist_to_binary(join(re:split("qqq -abc","\\Aabc\\Z",[multiline]))), +abc","\\Aabc\\Z",[multiline]))), <<"abc zzz">> = iolist_to_binary(join(re:split("abc -zzz","\\Aabc\\Z",[multiline,trim]))), +zzz","\\Aabc\\Z",[multiline,trim]))), <<"abc zzz">> = iolist_to_binary(join(re:split("abc -zzz","\\Aabc\\Z",[multiline,{parts,2}]))), +zzz","\\Aabc\\Z",[multiline,{parts,2}]))), <<"abc zzz">> = iolist_to_binary(join(re:split("abc -zzz","\\Aabc\\Z",[multiline]))), +zzz","\\Aabc\\Z",[multiline]))), <<"qqq abc zzz">> = iolist_to_binary(join(re:split("qqq abc -zzz","\\Aabc\\Z",[multiline,trim]))), +zzz","\\Aabc\\Z",[multiline,trim]))), <<"qqq abc zzz">> = iolist_to_binary(join(re:split("qqq abc -zzz","\\Aabc\\Z",[multiline,{parts,2}]))), +zzz","\\Aabc\\Z",[multiline,{parts,2}]))), <<"qqq abc zzz">> = iolist_to_binary(join(re:split("qqq abc -zzz","\\Aabc\\Z",[multiline]))), +zzz","\\Aabc\\Z",[multiline]))), <<":f">> = iolist_to_binary(join(re:split("abc -def","\\A(.)*\\Z",[dotall,trim]))), +def","\\A(.)*\\Z",[dotall,trim]))), <<":f:">> = iolist_to_binary(join(re:split("abc -def","\\A(.)*\\Z",[dotall,{parts,2}]))), +def","\\A(.)*\\Z",[dotall,{parts,2}]))), <<":f:">> = iolist_to_binary(join(re:split("abc -def","\\A(.)*\\Z",[dotall]))), +def","\\A(.)*\\Z",[dotall]))), <<":s">> = iolist_to_binary(join(re:split("*** Failers","\\A(.)*\\Z",[multiline, - trim]))), + trim]))), <<":s:">> = iolist_to_binary(join(re:split("*** Failers","\\A(.)*\\Z",[multiline, {parts, - 2}]))), - <<":s:">> = iolist_to_binary(join(re:split("*** Failers","\\A(.)*\\Z",[multiline]))), + 2}]))), + <<":s:">> = iolist_to_binary(join(re:split("*** Failers","\\A(.)*\\Z",[multiline]))), <<"abc def">> = iolist_to_binary(join(re:split("abc -def","\\A(.)*\\Z",[multiline,trim]))), +def","\\A(.)*\\Z",[multiline,trim]))), <<"abc def">> = iolist_to_binary(join(re:split("abc -def","\\A(.)*\\Z",[multiline,{parts,2}]))), +def","\\A(.)*\\Z",[multiline,{parts,2}]))), <<"abc def">> = iolist_to_binary(join(re:split("abc -def","\\A(.)*\\Z",[multiline]))), - <<"::c">> = iolist_to_binary(join(re:split("b::c","(?:b)|(?::+)",[trim]))), +def","\\A(.)*\\Z",[multiline]))), + <<"::c">> = iolist_to_binary(join(re:split("b::c","(?:b)|(?::+)",[trim]))), <<":::c">> = iolist_to_binary(join(re:split("b::c","(?:b)|(?::+)",[{parts, - 2}]))), - <<"::c">> = iolist_to_binary(join(re:split("b::c","(?:b)|(?::+)",[]))), - <<"c">> = iolist_to_binary(join(re:split("c::b","(?:b)|(?::+)",[trim]))), + 2}]))), + <<"::c">> = iolist_to_binary(join(re:split("b::c","(?:b)|(?::+)",[]))), + <<"c">> = iolist_to_binary(join(re:split("c::b","(?:b)|(?::+)",[trim]))), <<"c:b">> = iolist_to_binary(join(re:split("c::b","(?:b)|(?::+)",[{parts, - 2}]))), - <<"c::">> = iolist_to_binary(join(re:split("c::b","(?:b)|(?::+)",[]))), - <<"">> = iolist_to_binary(join(re:split("az-","[-az]+",[trim]))), + 2}]))), + <<"c::">> = iolist_to_binary(join(re:split("c::b","(?:b)|(?::+)",[]))), + <<"">> = iolist_to_binary(join(re:split("az-","[-az]+",[trim]))), <<":">> = iolist_to_binary(join(re:split("az-","[-az]+",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("az-","[-az]+",[]))), - <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","[-az]+",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("az-","[-az]+",[]))), + <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","[-az]+",[trim]))), <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","[-az]+",[{parts, - 2}]))), - <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","[-az]+",[]))), - <<"b">> = iolist_to_binary(join(re:split("b","[-az]+",[trim]))), + 2}]))), + <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","[-az]+",[]))), + <<"b">> = iolist_to_binary(join(re:split("b","[-az]+",[trim]))), <<"b">> = iolist_to_binary(join(re:split("b","[-az]+",[{parts, - 2}]))), - <<"b">> = iolist_to_binary(join(re:split("b","[-az]+",[]))), + 2}]))), + <<"b">> = iolist_to_binary(join(re:split("b","[-az]+",[]))), ok. run5() -> - <<"">> = iolist_to_binary(join(re:split("za-","[az-]+",[trim]))), + <<"">> = iolist_to_binary(join(re:split("za-","[az-]+",[trim]))), <<":">> = iolist_to_binary(join(re:split("za-","[az-]+",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("za-","[az-]+",[]))), - <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","[az-]+",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("za-","[az-]+",[]))), + <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","[az-]+",[trim]))), <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","[az-]+",[{parts, - 2}]))), - <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","[az-]+",[]))), - <<"b">> = iolist_to_binary(join(re:split("b","[az-]+",[trim]))), + 2}]))), + <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","[az-]+",[]))), + <<"b">> = iolist_to_binary(join(re:split("b","[az-]+",[trim]))), <<"b">> = iolist_to_binary(join(re:split("b","[az-]+",[{parts, - 2}]))), - <<"b">> = iolist_to_binary(join(re:split("b","[az-]+",[]))), - <<"">> = iolist_to_binary(join(re:split("a-z","[a\\-z]+",[trim]))), + 2}]))), + <<"b">> = iolist_to_binary(join(re:split("b","[az-]+",[]))), + <<"">> = iolist_to_binary(join(re:split("a-z","[a\\-z]+",[trim]))), <<":">> = iolist_to_binary(join(re:split("a-z","[a\\-z]+",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a-z","[a\\-z]+",[]))), - <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","[a\\-z]+",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a-z","[a\\-z]+",[]))), + <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","[a\\-z]+",[trim]))), <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","[a\\-z]+",[{parts, - 2}]))), - <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","[a\\-z]+",[]))), - <<"b">> = iolist_to_binary(join(re:split("b","[a\\-z]+",[trim]))), + 2}]))), + <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","[a\\-z]+",[]))), + <<"b">> = iolist_to_binary(join(re:split("b","[a\\-z]+",[trim]))), <<"b">> = iolist_to_binary(join(re:split("b","[a\\-z]+",[{parts, - 2}]))), - <<"b">> = iolist_to_binary(join(re:split("b","[a\\-z]+",[]))), - <<"">> = iolist_to_binary(join(re:split("abcdxyz","[a-z]+",[trim]))), + 2}]))), + <<"b">> = iolist_to_binary(join(re:split("b","[a\\-z]+",[]))), + <<"">> = iolist_to_binary(join(re:split("abcdxyz","[a-z]+",[trim]))), <<":">> = iolist_to_binary(join(re:split("abcdxyz","[a-z]+",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abcdxyz","[a-z]+",[]))), - <<"">> = iolist_to_binary(join(re:split("12-34","[\\d-]+",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abcdxyz","[a-z]+",[]))), + <<"">> = iolist_to_binary(join(re:split("12-34","[\\d-]+",[trim]))), <<":">> = iolist_to_binary(join(re:split("12-34","[\\d-]+",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("12-34","[\\d-]+",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","[\\d-]+",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("12-34","[\\d-]+",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","[\\d-]+",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","[\\d-]+",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","[\\d-]+",[]))), - <<"aaa">> = iolist_to_binary(join(re:split("aaa","[\\d-]+",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","[\\d-]+",[]))), + <<"aaa">> = iolist_to_binary(join(re:split("aaa","[\\d-]+",[trim]))), <<"aaa">> = iolist_to_binary(join(re:split("aaa","[\\d-]+",[{parts, - 2}]))), - <<"aaa">> = iolist_to_binary(join(re:split("aaa","[\\d-]+",[]))), - <<"">> = iolist_to_binary(join(re:split("12-34z","[\\d-z]+",[trim]))), + 2}]))), + <<"aaa">> = iolist_to_binary(join(re:split("aaa","[\\d-]+",[]))), + <<"">> = iolist_to_binary(join(re:split("12-34z","[\\d-z]+",[trim]))), <<":">> = iolist_to_binary(join(re:split("12-34z","[\\d-z]+",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("12-34z","[\\d-z]+",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","[\\d-z]+",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("12-34z","[\\d-z]+",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","[\\d-z]+",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","[\\d-z]+",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","[\\d-z]+",[]))), - <<"aaa">> = iolist_to_binary(join(re:split("aaa","[\\d-z]+",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","[\\d-z]+",[]))), + <<"aaa">> = iolist_to_binary(join(re:split("aaa","[\\d-z]+",[trim]))), <<"aaa">> = iolist_to_binary(join(re:split("aaa","[\\d-z]+",[{parts, - 2}]))), - <<"aaa">> = iolist_to_binary(join(re:split("aaa","[\\d-z]+",[]))), - <<": ">> = iolist_to_binary(join(re:split("\\ ","\\x5c",[trim]))), + 2}]))), + <<"aaa">> = iolist_to_binary(join(re:split("aaa","[\\d-z]+",[]))), + <<": ">> = iolist_to_binary(join(re:split("\\ ","\\x5c",[trim]))), <<": ">> = iolist_to_binary(join(re:split("\\ ","\\x5c",[{parts, - 2}]))), - <<": ">> = iolist_to_binary(join(re:split("\\ ","\\x5c",[]))), - <<"the:oo">> = iolist_to_binary(join(re:split("the Zoo","\\x20Z",[trim]))), + 2}]))), + <<": ">> = iolist_to_binary(join(re:split("\\ ","\\x5c",[]))), + <<"the:oo">> = iolist_to_binary(join(re:split("the Zoo","\\x20Z",[trim]))), <<"the:oo">> = iolist_to_binary(join(re:split("the Zoo","\\x20Z",[{parts, - 2}]))), - <<"the:oo">> = iolist_to_binary(join(re:split("the Zoo","\\x20Z",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\x20Z",[trim]))), + 2}]))), + <<"the:oo">> = iolist_to_binary(join(re:split("the Zoo","\\x20Z",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\x20Z",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\x20Z",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\x20Z",[]))), - <<"Zulu">> = iolist_to_binary(join(re:split("Zulu","\\x20Z",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\x20Z",[]))), + <<"Zulu">> = iolist_to_binary(join(re:split("Zulu","\\x20Z",[trim]))), <<"Zulu">> = iolist_to_binary(join(re:split("Zulu","\\x20Z",[{parts, - 2}]))), - <<"Zulu">> = iolist_to_binary(join(re:split("Zulu","\\x20Z",[]))), + 2}]))), + <<"Zulu">> = iolist_to_binary(join(re:split("Zulu","\\x20Z",[]))), <<":abc">> = iolist_to_binary(join(re:split("abcabc","(abc)\\1",[caseless, - trim]))), + trim]))), <<":abc:">> = iolist_to_binary(join(re:split("abcabc","(abc)\\1",[caseless, {parts, - 2}]))), - <<":abc:">> = iolist_to_binary(join(re:split("abcabc","(abc)\\1",[caseless]))), + 2}]))), + <<":abc:">> = iolist_to_binary(join(re:split("abcabc","(abc)\\1",[caseless]))), <<":ABC">> = iolist_to_binary(join(re:split("ABCabc","(abc)\\1",[caseless, - trim]))), + trim]))), <<":ABC:">> = iolist_to_binary(join(re:split("ABCabc","(abc)\\1",[caseless, {parts, - 2}]))), - <<":ABC:">> = iolist_to_binary(join(re:split("ABCabc","(abc)\\1",[caseless]))), + 2}]))), + <<":ABC:">> = iolist_to_binary(join(re:split("ABCabc","(abc)\\1",[caseless]))), <<":abc">> = iolist_to_binary(join(re:split("abcABC","(abc)\\1",[caseless, - trim]))), + trim]))), <<":abc:">> = iolist_to_binary(join(re:split("abcABC","(abc)\\1",[caseless, {parts, - 2}]))), - <<":abc:">> = iolist_to_binary(join(re:split("abcABC","(abc)\\1",[caseless]))), - <<"">> = iolist_to_binary(join(re:split("ab{3cd","ab{3cd",[trim]))), + 2}]))), + <<":abc:">> = iolist_to_binary(join(re:split("abcABC","(abc)\\1",[caseless]))), + <<"">> = iolist_to_binary(join(re:split("ab{3cd","ab{3cd",[trim]))), <<":">> = iolist_to_binary(join(re:split("ab{3cd","ab{3cd",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ab{3cd","ab{3cd",[]))), - <<"">> = iolist_to_binary(join(re:split("ab{3,cd","ab{3,cd",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ab{3cd","ab{3cd",[]))), + <<"">> = iolist_to_binary(join(re:split("ab{3,cd","ab{3,cd",[trim]))), <<":">> = iolist_to_binary(join(re:split("ab{3,cd","ab{3,cd",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ab{3,cd","ab{3,cd",[]))), - <<"">> = iolist_to_binary(join(re:split("ab{3,4a}cd","ab{3,4a}cd",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ab{3,cd","ab{3,cd",[]))), + <<"">> = iolist_to_binary(join(re:split("ab{3,4a}cd","ab{3,4a}cd",[trim]))), <<":">> = iolist_to_binary(join(re:split("ab{3,4a}cd","ab{3,4a}cd",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ab{3,4a}cd","ab{3,4a}cd",[]))), - <<"">> = iolist_to_binary(join(re:split("{4,5a}bc","{4,5a}bc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ab{3,4a}cd","ab{3,4a}cd",[]))), + <<"">> = iolist_to_binary(join(re:split("{4,5a}bc","{4,5a}bc",[trim]))), <<":">> = iolist_to_binary(join(re:split("{4,5a}bc","{4,5a}bc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("{4,5a}bc","{4,5a}bc",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","abc$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("{4,5a}bc","{4,5a}bc",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","abc$",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","abc$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","abc$",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","abc$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","abc$",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","abc$",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","abc$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","abc$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","abc$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc$",[]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc$",[]))), <<"abc def">> = iolist_to_binary(join(re:split("abc -def","abc$",[trim]))), +def","abc$",[trim]))), <<"abc def">> = iolist_to_binary(join(re:split("abc -def","abc$",[{parts,2}]))), +def","abc$",[{parts,2}]))), <<"abc def">> = iolist_to_binary(join(re:split("abc -def","abc$",[]))), - <<":abc">> = iolist_to_binary(join(re:split("abcS","(abc)\\123",[trim]))), +def","abc$",[]))), + <<":abc">> = iolist_to_binary(join(re:split("abcS","(abc)\\123",[trim]))), <<":abc:">> = iolist_to_binary(join(re:split("abcS","(abc)\\123",[{parts, - 2}]))), - <<":abc:">> = iolist_to_binary(join(re:split("abcS","(abc)\\123",[]))), - <<":abc">> = iolist_to_binary(join(re:split("abc","(abc)\\223",[trim]))), + 2}]))), + <<":abc:">> = iolist_to_binary(join(re:split("abcS","(abc)\\123",[]))), + <<":abc">> = iolist_to_binary(join(re:split("abc","(abc)\\223",[trim]))), <<":abc:">> = iolist_to_binary(join(re:split("abc","(abc)\\223",[{parts, - 2}]))), - <<":abc:">> = iolist_to_binary(join(re:split("abc","(abc)\\223",[]))), - <<":abc">> = iolist_to_binary(join(re:split("abcÓ","(abc)\\323",[trim]))), + 2}]))), + <<":abc:">> = iolist_to_binary(join(re:split("abc","(abc)\\223",[]))), + <<":abc">> = iolist_to_binary(join(re:split("abcÓ","(abc)\\323",[trim]))), <<":abc:">> = iolist_to_binary(join(re:split("abcÓ","(abc)\\323",[{parts, - 2}]))), - <<":abc:">> = iolist_to_binary(join(re:split("abcÓ","(abc)\\323",[]))), - <<":abc">> = iolist_to_binary(join(re:split("abc@","(abc)\\100",[trim]))), + 2}]))), + <<":abc:">> = iolist_to_binary(join(re:split("abcÓ","(abc)\\323",[]))), + <<":abc">> = iolist_to_binary(join(re:split("abc@","(abc)\\100",[trim]))), <<":abc:">> = iolist_to_binary(join(re:split("abc@","(abc)\\100",[{parts, - 2}]))), - <<":abc:">> = iolist_to_binary(join(re:split("abc@","(abc)\\100",[]))), - <<":abc">> = iolist_to_binary(join(re:split("abc@","(abc)\\100",[trim]))), + 2}]))), + <<":abc:">> = iolist_to_binary(join(re:split("abc@","(abc)\\100",[]))), + <<":abc">> = iolist_to_binary(join(re:split("abc@","(abc)\\100",[trim]))), <<":abc:">> = iolist_to_binary(join(re:split("abc@","(abc)\\100",[{parts, - 2}]))), - <<":abc:">> = iolist_to_binary(join(re:split("abc@","(abc)\\100",[]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[trim]))), + 2}]))), + <<":abc:">> = iolist_to_binary(join(re:split("abc@","(abc)\\100",[]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[trim]))), <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[{parts, - 2}]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[trim]))), + 2}]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[trim]))), <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[{parts, - 2}]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[trim]))), + 2}]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[trim]))), <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[{parts, - 2}]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[trim]))), + 2}]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[trim]))), <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[{parts, - 2}]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[trim]))), + 2}]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[trim]))), <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[{parts, - 2}]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[trim]))), + 2}]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[trim]))), <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[{parts, - 2}]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[]))), - <<":A:B:C:D:E:F:G:H:I">> = iolist_to_binary(join(re:split("ABCDEFGHIHI","^(A)(B)(C)(D)(E)(F)(G)(H)(I)\\8\\9$",[trim]))), + 2}]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[]))), + <<":A:B:C:D:E:F:G:H:I">> = iolist_to_binary(join(re:split("ABCDEFGHIHI","^(A)(B)(C)(D)(E)(F)(G)(H)(I)\\8\\9$",[trim]))), <<":A:B:C:D:E:F:G:H:I:">> = iolist_to_binary(join(re:split("ABCDEFGHIHI","^(A)(B)(C)(D)(E)(F)(G)(H)(I)\\8\\9$",[{parts, - 2}]))), - <<":A:B:C:D:E:F:G:H:I:">> = iolist_to_binary(join(re:split("ABCDEFGHIHI","^(A)(B)(C)(D)(E)(F)(G)(H)(I)\\8\\9$",[]))), + 2}]))), + <<":A:B:C:D:E:F:G:H:I:">> = iolist_to_binary(join(re:split("ABCDEFGHIHI","^(A)(B)(C)(D)(E)(F)(G)(H)(I)\\8\\9$",[]))), ok. run6() -> - <<"">> = iolist_to_binary(join(re:split("A8B9C","^[A\\8B\\9C]+$",[trim]))), + <<"">> = iolist_to_binary(join(re:split("A8B9C","^[A\\8B\\9C]+$",[trim]))), <<":">> = iolist_to_binary(join(re:split("A8B9C","^[A\\8B\\9C]+$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("A8B9C","^[A\\8B\\9C]+$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[A\\8B\\9C]+$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("A8B9C","^[A\\8B\\9C]+$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[A\\8B\\9C]+$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[A\\8B\\9C]+$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[A\\8B\\9C]+$",[]))), - <<"">> = iolist_to_binary(join(re:split("A8B9C","^[A\\8B\\9C]+$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[A\\8B\\9C]+$",[]))), + <<"">> = iolist_to_binary(join(re:split("A8B9C","^[A\\8B\\9C]+$",[trim]))), <<":">> = iolist_to_binary(join(re:split("A8B9C","^[A\\8B\\9C]+$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("A8B9C","^[A\\8B\\9C]+$",[]))), - <<":a:b:c:d:e:f:g:h:i:j:k:l">> = iolist_to_binary(join(re:split("abcdefghijkllS","(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)(l)\\12\\123",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("A8B9C","^[A\\8B\\9C]+$",[]))), + <<":a:b:c:d:e:f:g:h:i:j:k:l">> = iolist_to_binary(join(re:split("abcdefghijkllS","(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)(l)\\12\\123",[trim]))), <<":a:b:c:d:e:f:g:h:i:j:k:l:">> = iolist_to_binary(join(re:split("abcdefghijkllS","(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)(l)\\12\\123",[{parts, - 2}]))), - <<":a:b:c:d:e:f:g:h:i:j:k:l:">> = iolist_to_binary(join(re:split("abcdefghijkllS","(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)(l)\\12\\123",[]))), + 2}]))), + <<":a:b:c:d:e:f:g:h:i:j:k:l:">> = iolist_to_binary(join(re:split("abcdefghijkllS","(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)(l)\\12\\123",[]))), <<":a:b:c:d:e:f:g:h:i:j:k">> = iolist_to_binary(join(re:split("abcdefghijk -S","(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)\\12\\123",[trim]))), +S","(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)\\12\\123",[trim]))), <<":a:b:c:d:e:f:g:h:i:j:k:">> = iolist_to_binary(join(re:split("abcdefghijk -S","(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)\\12\\123",[{parts,2}]))), +S","(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)\\12\\123",[{parts,2}]))), <<":a:b:c:d:e:f:g:h:i:j:k:">> = iolist_to_binary(join(re:split("abcdefghijk -S","(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)\\12\\123",[]))), - <<"">> = iolist_to_binary(join(re:split("abidef","ab\\idef",[trim]))), +S","(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)\\12\\123",[]))), + <<"">> = iolist_to_binary(join(re:split("abidef","ab\\idef",[trim]))), <<":">> = iolist_to_binary(join(re:split("abidef","ab\\idef",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abidef","ab\\idef",[]))), - <<"">> = iolist_to_binary(join(re:split("bc","a{0}bc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abidef","ab\\idef",[]))), + <<"">> = iolist_to_binary(join(re:split("bc","a{0}bc",[trim]))), <<":">> = iolist_to_binary(join(re:split("bc","a{0}bc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("bc","a{0}bc",[]))), - <<"">> = iolist_to_binary(join(re:split("xyz","(a|(bc)){0,0}?xyz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("bc","a{0}bc",[]))), + <<"">> = iolist_to_binary(join(re:split("xyz","(a|(bc)){0,0}?xyz",[trim]))), <<":::">> = iolist_to_binary(join(re:split("xyz","(a|(bc)){0,0}?xyz",[{parts, - 2}]))), - <<":::">> = iolist_to_binary(join(re:split("xyz","(a|(bc)){0,0}?xyz",[]))), - <<"">> = iolist_to_binary(join(re:split("abcde","abc[\\10]de",[trim]))), + 2}]))), + <<":::">> = iolist_to_binary(join(re:split("xyz","(a|(bc)){0,0}?xyz",[]))), + <<"">> = iolist_to_binary(join(re:split("abcde","abc[\\10]de",[trim]))), <<":">> = iolist_to_binary(join(re:split("abcde","abc[\\10]de",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abcde","abc[\\10]de",[]))), - <<"">> = iolist_to_binary(join(re:split("abcde","abc[\\1]de",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abcde","abc[\\10]de",[]))), + <<"">> = iolist_to_binary(join(re:split("abcde","abc[\\1]de",[trim]))), <<":">> = iolist_to_binary(join(re:split("abcde","abc[\\1]de",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abcde","abc[\\1]de",[]))), - <<":abc">> = iolist_to_binary(join(re:split("abcde","(abc)[\\1]de",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abcde","abc[\\1]de",[]))), + <<":abc">> = iolist_to_binary(join(re:split("abcde","(abc)[\\1]de",[trim]))), <<":abc:">> = iolist_to_binary(join(re:split("abcde","(abc)[\\1]de",[{parts, - 2}]))), - <<":abc:">> = iolist_to_binary(join(re:split("abcde","(abc)[\\1]de",[]))), + 2}]))), + <<":abc:">> = iolist_to_binary(join(re:split("abcde","(abc)[\\1]de",[]))), <<"">> = iolist_to_binary(join(re:split("a -b","(?s)a.b",[trim]))), +b","(?s)a.b",[trim]))), <<":">> = iolist_to_binary(join(re:split("a -b","(?s)a.b",[{parts,2}]))), +b","(?s)a.b",[{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("a -b","(?s)a.b",[]))), - <<":b:a:NOT:cccc:d">> = iolist_to_binary(join(re:split("baNOTccccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[trim]))), +b","(?s)a.b",[]))), + <<":b:a:NOT:cccc:d">> = iolist_to_binary(join(re:split("baNOTccccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[trim]))), <<":b:a:NOT:cccc:d">> = iolist_to_binary(join(re:split("baNOTccccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[{parts, - 2}]))), - <<":b:a:NOT:cccc:d">> = iolist_to_binary(join(re:split("baNOTccccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[]))), - <<":b:a:NOT:ccc:d">> = iolist_to_binary(join(re:split("baNOTcccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[trim]))), + 2}]))), + <<":b:a:NOT:cccc:d">> = iolist_to_binary(join(re:split("baNOTccccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[]))), + <<":b:a:NOT:ccc:d">> = iolist_to_binary(join(re:split("baNOTcccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[trim]))), <<":b:a:NOT:ccc:d">> = iolist_to_binary(join(re:split("baNOTcccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[{parts, - 2}]))), - <<":b:a:NOT:ccc:d">> = iolist_to_binary(join(re:split("baNOTcccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[]))), - <<":b:a:NO:Tcc:d">> = iolist_to_binary(join(re:split("baNOTccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[trim]))), + 2}]))), + <<":b:a:NOT:ccc:d">> = iolist_to_binary(join(re:split("baNOTcccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[]))), + <<":b:a:NO:Tcc:d">> = iolist_to_binary(join(re:split("baNOTccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[trim]))), <<":b:a:NO:Tcc:d">> = iolist_to_binary(join(re:split("baNOTccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[{parts, - 2}]))), - <<":b:a:NO:Tcc:d">> = iolist_to_binary(join(re:split("baNOTccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[]))), - <<":b:a::ccc:d">> = iolist_to_binary(join(re:split("bacccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[trim]))), + 2}]))), + <<":b:a:NO:Tcc:d">> = iolist_to_binary(join(re:split("baNOTccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[]))), + <<":b:a::ccc:d">> = iolist_to_binary(join(re:split("bacccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[trim]))), <<":b:a::ccc:d">> = iolist_to_binary(join(re:split("bacccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[{parts, - 2}]))), - <<":b:a::ccc:d">> = iolist_to_binary(join(re:split("bacccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[]))), - <<":*:*:* Fail:ers">> = iolist_to_binary(join(re:split("*** Failers","^([^a])([^\\b])([^c]*)([^d]{3,4})",[trim]))), + 2}]))), + <<":b:a::ccc:d">> = iolist_to_binary(join(re:split("bacccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[]))), + <<":*:*:* Fail:ers">> = iolist_to_binary(join(re:split("*** Failers","^([^a])([^\\b])([^c]*)([^d]{3,4})",[trim]))), <<":*:*:* Fail:ers:">> = iolist_to_binary(join(re:split("*** Failers","^([^a])([^\\b])([^c]*)([^d]{3,4})",[{parts, - 2}]))), - <<":*:*:* Fail:ers:">> = iolist_to_binary(join(re:split("*** Failers","^([^a])([^\\b])([^c]*)([^d]{3,4})",[]))), - <<"anything">> = iolist_to_binary(join(re:split("anything","^([^a])([^\\b])([^c]*)([^d]{3,4})",[trim]))), + 2}]))), + <<":*:*:* Fail:ers:">> = iolist_to_binary(join(re:split("*** Failers","^([^a])([^\\b])([^c]*)([^d]{3,4})",[]))), + <<"anything">> = iolist_to_binary(join(re:split("anything","^([^a])([^\\b])([^c]*)([^d]{3,4})",[trim]))), <<"anything">> = iolist_to_binary(join(re:split("anything","^([^a])([^\\b])([^c]*)([^d]{3,4})",[{parts, - 2}]))), - <<"anything">> = iolist_to_binary(join(re:split("anything","^([^a])([^\\b])([^c]*)([^d]{3,4})",[]))), - <<"bc">> = iolist_to_binary(join(re:split("bc","^([^a])([^\\b])([^c]*)([^d]{3,4})",[trim]))), + 2}]))), + <<"anything">> = iolist_to_binary(join(re:split("anything","^([^a])([^\\b])([^c]*)([^d]{3,4})",[]))), + <<"bc">> = iolist_to_binary(join(re:split("bc","^([^a])([^\\b])([^c]*)([^d]{3,4})",[trim]))), <<"bc">> = iolist_to_binary(join(re:split("bc","^([^a])([^\\b])([^c]*)([^d]{3,4})",[{parts, - 2}]))), - <<"bc">> = iolist_to_binary(join(re:split("bc","^([^a])([^\\b])([^c]*)([^d]{3,4})",[]))), - <<"baccd">> = iolist_to_binary(join(re:split("baccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[trim]))), + 2}]))), + <<"bc">> = iolist_to_binary(join(re:split("bc","^([^a])([^\\b])([^c]*)([^d]{3,4})",[]))), + <<"baccd">> = iolist_to_binary(join(re:split("baccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[trim]))), <<"baccd">> = iolist_to_binary(join(re:split("baccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[{parts, - 2}]))), - <<"baccd">> = iolist_to_binary(join(re:split("baccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[]))), - <<"">> = iolist_to_binary(join(re:split("Abc","[^a]",[trim]))), + 2}]))), + <<"baccd">> = iolist_to_binary(join(re:split("baccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[]))), + <<"">> = iolist_to_binary(join(re:split("Abc","[^a]",[trim]))), <<":bc">> = iolist_to_binary(join(re:split("Abc","[^a]",[{parts, - 2}]))), - <<":::">> = iolist_to_binary(join(re:split("Abc","[^a]",[]))), + 2}]))), + <<":::">> = iolist_to_binary(join(re:split("Abc","[^a]",[]))), <<"A">> = iolist_to_binary(join(re:split("Abc","[^a]",[caseless, - trim]))), + trim]))), <<"A:c">> = iolist_to_binary(join(re:split("Abc","[^a]",[caseless, {parts, - 2}]))), - <<"A::">> = iolist_to_binary(join(re:split("Abc","[^a]",[caseless]))), - <<":a">> = iolist_to_binary(join(re:split("AAAaAbc","[^a]+",[trim]))), + 2}]))), + <<"A::">> = iolist_to_binary(join(re:split("Abc","[^a]",[caseless]))), + <<":a">> = iolist_to_binary(join(re:split("AAAaAbc","[^a]+",[trim]))), <<":aAbc">> = iolist_to_binary(join(re:split("AAAaAbc","[^a]+",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("AAAaAbc","[^a]+",[]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("AAAaAbc","[^a]+",[]))), <<"AAAaA">> = iolist_to_binary(join(re:split("AAAaAbc","[^a]+",[caseless, - trim]))), + trim]))), <<"AAAaA:">> = iolist_to_binary(join(re:split("AAAaAbc","[^a]+",[caseless, {parts, - 2}]))), - <<"AAAaA:">> = iolist_to_binary(join(re:split("AAAaAbc","[^a]+",[caseless]))), + 2}]))), + <<"AAAaA:">> = iolist_to_binary(join(re:split("AAAaAbc","[^a]+",[caseless]))), <<"">> = iolist_to_binary(join(re:split("bbb -ccc","[^a]+",[trim]))), +ccc","[^a]+",[trim]))), <<":">> = iolist_to_binary(join(re:split("bbb -ccc","[^a]+",[{parts,2}]))), +ccc","[^a]+",[{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("bbb -ccc","[^a]+",[]))), - <<"ab">> = iolist_to_binary(join(re:split("abc","[^k]$",[trim]))), +ccc","[^a]+",[]))), + <<"ab">> = iolist_to_binary(join(re:split("abc","[^k]$",[trim]))), <<"ab:">> = iolist_to_binary(join(re:split("abc","[^k]$",[{parts, - 2}]))), - <<"ab:">> = iolist_to_binary(join(re:split("abc","[^k]$",[]))), - <<"*** Failer">> = iolist_to_binary(join(re:split("*** Failers","[^k]$",[trim]))), + 2}]))), + <<"ab:">> = iolist_to_binary(join(re:split("abc","[^k]$",[]))), + <<"*** Failer">> = iolist_to_binary(join(re:split("*** Failers","[^k]$",[trim]))), <<"*** Failer:">> = iolist_to_binary(join(re:split("*** Failers","[^k]$",[{parts, - 2}]))), - <<"*** Failer:">> = iolist_to_binary(join(re:split("*** Failers","[^k]$",[]))), - <<"abk">> = iolist_to_binary(join(re:split("abk","[^k]$",[trim]))), + 2}]))), + <<"*** Failer:">> = iolist_to_binary(join(re:split("*** Failers","[^k]$",[]))), + <<"abk">> = iolist_to_binary(join(re:split("abk","[^k]$",[trim]))), <<"abk">> = iolist_to_binary(join(re:split("abk","[^k]$",[{parts, - 2}]))), - <<"abk">> = iolist_to_binary(join(re:split("abk","[^k]$",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","[^k]{2,3}$",[trim]))), + 2}]))), + <<"abk">> = iolist_to_binary(join(re:split("abk","[^k]$",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","[^k]{2,3}$",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","[^k]{2,3}$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","[^k]{2,3}$",[]))), - <<"k">> = iolist_to_binary(join(re:split("kbc","[^k]{2,3}$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","[^k]{2,3}$",[]))), + <<"k">> = iolist_to_binary(join(re:split("kbc","[^k]{2,3}$",[trim]))), <<"k:">> = iolist_to_binary(join(re:split("kbc","[^k]{2,3}$",[{parts, - 2}]))), - <<"k:">> = iolist_to_binary(join(re:split("kbc","[^k]{2,3}$",[]))), - <<"k">> = iolist_to_binary(join(re:split("kabc","[^k]{2,3}$",[trim]))), + 2}]))), + <<"k:">> = iolist_to_binary(join(re:split("kbc","[^k]{2,3}$",[]))), + <<"k">> = iolist_to_binary(join(re:split("kabc","[^k]{2,3}$",[trim]))), <<"k:">> = iolist_to_binary(join(re:split("kabc","[^k]{2,3}$",[{parts, - 2}]))), - <<"k:">> = iolist_to_binary(join(re:split("kabc","[^k]{2,3}$",[]))), - <<"*** Fail">> = iolist_to_binary(join(re:split("*** Failers","[^k]{2,3}$",[trim]))), + 2}]))), + <<"k:">> = iolist_to_binary(join(re:split("kabc","[^k]{2,3}$",[]))), + <<"*** Fail">> = iolist_to_binary(join(re:split("*** Failers","[^k]{2,3}$",[trim]))), <<"*** Fail:">> = iolist_to_binary(join(re:split("*** Failers","[^k]{2,3}$",[{parts, - 2}]))), - <<"*** Fail:">> = iolist_to_binary(join(re:split("*** Failers","[^k]{2,3}$",[]))), - <<"abk">> = iolist_to_binary(join(re:split("abk","[^k]{2,3}$",[trim]))), + 2}]))), + <<"*** Fail:">> = iolist_to_binary(join(re:split("*** Failers","[^k]{2,3}$",[]))), + <<"abk">> = iolist_to_binary(join(re:split("abk","[^k]{2,3}$",[trim]))), <<"abk">> = iolist_to_binary(join(re:split("abk","[^k]{2,3}$",[{parts, - 2}]))), - <<"abk">> = iolist_to_binary(join(re:split("abk","[^k]{2,3}$",[]))), - <<"akb">> = iolist_to_binary(join(re:split("akb","[^k]{2,3}$",[trim]))), + 2}]))), + <<"abk">> = iolist_to_binary(join(re:split("abk","[^k]{2,3}$",[]))), + <<"akb">> = iolist_to_binary(join(re:split("akb","[^k]{2,3}$",[trim]))), <<"akb">> = iolist_to_binary(join(re:split("akb","[^k]{2,3}$",[{parts, - 2}]))), - <<"akb">> = iolist_to_binary(join(re:split("akb","[^k]{2,3}$",[]))), - <<"akk">> = iolist_to_binary(join(re:split("akk","[^k]{2,3}$",[trim]))), + 2}]))), + <<"akb">> = iolist_to_binary(join(re:split("akb","[^k]{2,3}$",[]))), + <<"akk">> = iolist_to_binary(join(re:split("akk","[^k]{2,3}$",[trim]))), <<"akk">> = iolist_to_binary(join(re:split("akk","[^k]{2,3}$",[{parts, - 2}]))), - <<"akk">> = iolist_to_binary(join(re:split("akk","[^k]{2,3}$",[]))), - <<"12345678.b.c.d">> = iolist_to_binary(join(re:split("12345678.b.c.d","^\\d{8,}\\@.+[^k]$",[trim]))), + 2}]))), + <<"akk">> = iolist_to_binary(join(re:split("akk","[^k]{2,3}$",[]))), + <<"12345678.b.c.d">> = iolist_to_binary(join(re:split("12345678.b.c.d","^\\d{8,}\\@.+[^k]$",[trim]))), <<"12345678.b.c.d">> = iolist_to_binary(join(re:split("12345678.b.c.d","^\\d{8,}\\@.+[^k]$",[{parts, - 2}]))), - <<"12345678.b.c.d">> = iolist_to_binary(join(re:split("12345678.b.c.d","^\\d{8,}\\@.+[^k]$",[]))), - <<"123456789.y.z">> = iolist_to_binary(join(re:split("123456789.y.z","^\\d{8,}\\@.+[^k]$",[trim]))), + 2}]))), + <<"12345678.b.c.d">> = iolist_to_binary(join(re:split("12345678.b.c.d","^\\d{8,}\\@.+[^k]$",[]))), + <<"123456789.y.z">> = iolist_to_binary(join(re:split("123456789.y.z","^\\d{8,}\\@.+[^k]$",[trim]))), <<"123456789.y.z">> = iolist_to_binary(join(re:split("123456789.y.z","^\\d{8,}\\@.+[^k]$",[{parts, - 2}]))), - <<"123456789.y.z">> = iolist_to_binary(join(re:split("123456789.y.z","^\\d{8,}\\@.+[^k]$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\d{8,}\\@.+[^k]$",[trim]))), + 2}]))), + <<"123456789.y.z">> = iolist_to_binary(join(re:split("123456789.y.z","^\\d{8,}\\@.+[^k]$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\d{8,}\\@.+[^k]$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\d{8,}\\@.+[^k]$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\d{8,}\\@.+[^k]$",[]))), - <<"12345678.y.uk">> = iolist_to_binary(join(re:split("12345678.y.uk","^\\d{8,}\\@.+[^k]$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\d{8,}\\@.+[^k]$",[]))), + <<"12345678.y.uk">> = iolist_to_binary(join(re:split("12345678.y.uk","^\\d{8,}\\@.+[^k]$",[trim]))), <<"12345678.y.uk">> = iolist_to_binary(join(re:split("12345678.y.uk","^\\d{8,}\\@.+[^k]$",[{parts, - 2}]))), - <<"12345678.y.uk">> = iolist_to_binary(join(re:split("12345678.y.uk","^\\d{8,}\\@.+[^k]$",[]))), - <<"1234567.b.c.d">> = iolist_to_binary(join(re:split("1234567.b.c.d","^\\d{8,}\\@.+[^k]$",[trim]))), + 2}]))), + <<"12345678.y.uk">> = iolist_to_binary(join(re:split("12345678.y.uk","^\\d{8,}\\@.+[^k]$",[]))), + <<"1234567.b.c.d">> = iolist_to_binary(join(re:split("1234567.b.c.d","^\\d{8,}\\@.+[^k]$",[trim]))), <<"1234567.b.c.d">> = iolist_to_binary(join(re:split("1234567.b.c.d","^\\d{8,}\\@.+[^k]$",[{parts, - 2}]))), - <<"1234567.b.c.d">> = iolist_to_binary(join(re:split("1234567.b.c.d","^\\d{8,}\\@.+[^k]$",[]))), - <<":a">> = iolist_to_binary(join(re:split("aaaaaaaaa","(a)\\1{8,}",[trim]))), + 2}]))), + <<"1234567.b.c.d">> = iolist_to_binary(join(re:split("1234567.b.c.d","^\\d{8,}\\@.+[^k]$",[]))), + <<":a">> = iolist_to_binary(join(re:split("aaaaaaaaa","(a)\\1{8,}",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aaaaaaaaa","(a)\\1{8,}",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aaaaaaaaa","(a)\\1{8,}",[]))), - <<":a">> = iolist_to_binary(join(re:split("aaaaaaaaaa","(a)\\1{8,}",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aaaaaaaaa","(a)\\1{8,}",[]))), + <<":a">> = iolist_to_binary(join(re:split("aaaaaaaaaa","(a)\\1{8,}",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aaaaaaaaaa","(a)\\1{8,}",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aaaaaaaaaa","(a)\\1{8,}",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a)\\1{8,}",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aaaaaaaaaa","(a)\\1{8,}",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a)\\1{8,}",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a)\\1{8,}",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a)\\1{8,}",[]))), - <<"aaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaa","(a)\\1{8,}",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a)\\1{8,}",[]))), + <<"aaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaa","(a)\\1{8,}",[trim]))), <<"aaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaa","(a)\\1{8,}",[{parts, - 2}]))), - <<"aaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaa","(a)\\1{8,}",[]))), + 2}]))), + <<"aaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaa","(a)\\1{8,}",[]))), ok. run7() -> - <<"aaaa">> = iolist_to_binary(join(re:split("aaaabcd","[^a]",[trim]))), + <<"aaaa">> = iolist_to_binary(join(re:split("aaaabcd","[^a]",[trim]))), <<"aaaa:cd">> = iolist_to_binary(join(re:split("aaaabcd","[^a]",[{parts, - 2}]))), - <<"aaaa:::">> = iolist_to_binary(join(re:split("aaaabcd","[^a]",[]))), - <<"aa:a">> = iolist_to_binary(join(re:split("aaAabcd","[^a]",[trim]))), + 2}]))), + <<"aaaa:::">> = iolist_to_binary(join(re:split("aaaabcd","[^a]",[]))), + <<"aa:a">> = iolist_to_binary(join(re:split("aaAabcd","[^a]",[trim]))), <<"aa:abcd">> = iolist_to_binary(join(re:split("aaAabcd","[^a]",[{parts, - 2}]))), - <<"aa:a:::">> = iolist_to_binary(join(re:split("aaAabcd","[^a]",[]))), + 2}]))), + <<"aa:a:::">> = iolist_to_binary(join(re:split("aaAabcd","[^a]",[]))), <<"aaaa">> = iolist_to_binary(join(re:split("aaaabcd","[^a]",[caseless, - trim]))), + trim]))), <<"aaaa:cd">> = iolist_to_binary(join(re:split("aaaabcd","[^a]",[caseless, {parts, - 2}]))), - <<"aaaa:::">> = iolist_to_binary(join(re:split("aaaabcd","[^a]",[caseless]))), + 2}]))), + <<"aaaa:::">> = iolist_to_binary(join(re:split("aaaabcd","[^a]",[caseless]))), <<"aaAa">> = iolist_to_binary(join(re:split("aaAabcd","[^a]",[caseless, - trim]))), + trim]))), <<"aaAa:cd">> = iolist_to_binary(join(re:split("aaAabcd","[^a]",[caseless, {parts, - 2}]))), - <<"aaAa:::">> = iolist_to_binary(join(re:split("aaAabcd","[^a]",[caseless]))), - <<"aaaa">> = iolist_to_binary(join(re:split("aaaabcd","[^az]",[trim]))), + 2}]))), + <<"aaAa:::">> = iolist_to_binary(join(re:split("aaAabcd","[^a]",[caseless]))), + <<"aaaa">> = iolist_to_binary(join(re:split("aaaabcd","[^az]",[trim]))), <<"aaaa:cd">> = iolist_to_binary(join(re:split("aaaabcd","[^az]",[{parts, - 2}]))), - <<"aaaa:::">> = iolist_to_binary(join(re:split("aaaabcd","[^az]",[]))), - <<"aa:a">> = iolist_to_binary(join(re:split("aaAabcd","[^az]",[trim]))), + 2}]))), + <<"aaaa:::">> = iolist_to_binary(join(re:split("aaaabcd","[^az]",[]))), + <<"aa:a">> = iolist_to_binary(join(re:split("aaAabcd","[^az]",[trim]))), <<"aa:abcd">> = iolist_to_binary(join(re:split("aaAabcd","[^az]",[{parts, - 2}]))), - <<"aa:a:::">> = iolist_to_binary(join(re:split("aaAabcd","[^az]",[]))), + 2}]))), + <<"aa:a:::">> = iolist_to_binary(join(re:split("aaAabcd","[^az]",[]))), <<"aaaa">> = iolist_to_binary(join(re:split("aaaabcd","[^az]",[caseless, - trim]))), + trim]))), <<"aaaa:cd">> = iolist_to_binary(join(re:split("aaaabcd","[^az]",[caseless, {parts, - 2}]))), - <<"aaaa:::">> = iolist_to_binary(join(re:split("aaaabcd","[^az]",[caseless]))), + 2}]))), + <<"aaaa:::">> = iolist_to_binary(join(re:split("aaaabcd","[^az]",[caseless]))), <<"aaAa">> = iolist_to_binary(join(re:split("aaAabcd","[^az]",[caseless, - trim]))), + trim]))), <<"aaAa:cd">> = iolist_to_binary(join(re:split("aaAabcd","[^az]",[caseless, {parts, - 2}]))), - <<"aaAa:::">> = iolist_to_binary(join(re:split("aaAabcd","[^az]",[caseless]))), - <<"xxxxxxxxxxx:xxxxxxxxx">> = iolist_to_binary(join(re:split("xxxxxxxxxxxPSTAIREISLLxxxxxxxxx","P[^*]TAIRE[^*]{1,6}?LL",[trim]))), + 2}]))), + <<"aaAa:::">> = iolist_to_binary(join(re:split("aaAabcd","[^az]",[caseless]))), + <<"xxxxxxxxxxx:xxxxxxxxx">> = iolist_to_binary(join(re:split("xxxxxxxxxxxPSTAIREISLLxxxxxxxxx","P[^*]TAIRE[^*]{1,6}?LL",[trim]))), <<"xxxxxxxxxxx:xxxxxxxxx">> = iolist_to_binary(join(re:split("xxxxxxxxxxxPSTAIREISLLxxxxxxxxx","P[^*]TAIRE[^*]{1,6}?LL",[{parts, - 2}]))), - <<"xxxxxxxxxxx:xxxxxxxxx">> = iolist_to_binary(join(re:split("xxxxxxxxxxxPSTAIREISLLxxxxxxxxx","P[^*]TAIRE[^*]{1,6}?LL",[]))), - <<"xxxxxxxxxxx:xxxxxxxxx">> = iolist_to_binary(join(re:split("xxxxxxxxxxxPSTAIREISLLxxxxxxxxx","P[^*]TAIRE[^*]{1,}?LL",[trim]))), + 2}]))), + <<"xxxxxxxxxxx:xxxxxxxxx">> = iolist_to_binary(join(re:split("xxxxxxxxxxxPSTAIREISLLxxxxxxxxx","P[^*]TAIRE[^*]{1,6}?LL",[]))), + <<"xxxxxxxxxxx:xxxxxxxxx">> = iolist_to_binary(join(re:split("xxxxxxxxxxxPSTAIREISLLxxxxxxxxx","P[^*]TAIRE[^*]{1,}?LL",[trim]))), <<"xxxxxxxxxxx:xxxxxxxxx">> = iolist_to_binary(join(re:split("xxxxxxxxxxxPSTAIREISLLxxxxxxxxx","P[^*]TAIRE[^*]{1,}?LL",[{parts, - 2}]))), - <<"xxxxxxxxxxx:xxxxxxxxx">> = iolist_to_binary(join(re:split("xxxxxxxxxxxPSTAIREISLLxxxxxxxxx","P[^*]TAIRE[^*]{1,}?LL",[]))), - <<"1:.23">> = iolist_to_binary(join(re:split("1.230003938","(\\.\\d\\d[1-9]?)\\d+",[trim]))), + 2}]))), + <<"xxxxxxxxxxx:xxxxxxxxx">> = iolist_to_binary(join(re:split("xxxxxxxxxxxPSTAIREISLLxxxxxxxxx","P[^*]TAIRE[^*]{1,}?LL",[]))), + <<"1:.23">> = iolist_to_binary(join(re:split("1.230003938","(\\.\\d\\d[1-9]?)\\d+",[trim]))), <<"1:.23:">> = iolist_to_binary(join(re:split("1.230003938","(\\.\\d\\d[1-9]?)\\d+",[{parts, - 2}]))), - <<"1:.23:">> = iolist_to_binary(join(re:split("1.230003938","(\\.\\d\\d[1-9]?)\\d+",[]))), - <<"1:.875">> = iolist_to_binary(join(re:split("1.875000282","(\\.\\d\\d[1-9]?)\\d+",[trim]))), + 2}]))), + <<"1:.23:">> = iolist_to_binary(join(re:split("1.230003938","(\\.\\d\\d[1-9]?)\\d+",[]))), + <<"1:.875">> = iolist_to_binary(join(re:split("1.875000282","(\\.\\d\\d[1-9]?)\\d+",[trim]))), <<"1:.875:">> = iolist_to_binary(join(re:split("1.875000282","(\\.\\d\\d[1-9]?)\\d+",[{parts, - 2}]))), - <<"1:.875:">> = iolist_to_binary(join(re:split("1.875000282","(\\.\\d\\d[1-9]?)\\d+",[]))), - <<"1:.23">> = iolist_to_binary(join(re:split("1.235","(\\.\\d\\d[1-9]?)\\d+",[trim]))), + 2}]))), + <<"1:.875:">> = iolist_to_binary(join(re:split("1.875000282","(\\.\\d\\d[1-9]?)\\d+",[]))), + <<"1:.23">> = iolist_to_binary(join(re:split("1.235","(\\.\\d\\d[1-9]?)\\d+",[trim]))), <<"1:.23:">> = iolist_to_binary(join(re:split("1.235","(\\.\\d\\d[1-9]?)\\d+",[{parts, - 2}]))), - <<"1:.23:">> = iolist_to_binary(join(re:split("1.235","(\\.\\d\\d[1-9]?)\\d+",[]))), - <<"1:.23::0003938">> = iolist_to_binary(join(re:split("1.230003938","(\\.\\d\\d((?=0)|\\d(?=\\d)))",[trim]))), + 2}]))), + <<"1:.23:">> = iolist_to_binary(join(re:split("1.235","(\\.\\d\\d[1-9]?)\\d+",[]))), + <<"1:.23::0003938">> = iolist_to_binary(join(re:split("1.230003938","(\\.\\d\\d((?=0)|\\d(?=\\d)))",[trim]))), <<"1:.23::0003938">> = iolist_to_binary(join(re:split("1.230003938","(\\.\\d\\d((?=0)|\\d(?=\\d)))",[{parts, - 2}]))), - <<"1:.23::0003938">> = iolist_to_binary(join(re:split("1.230003938","(\\.\\d\\d((?=0)|\\d(?=\\d)))",[]))), - <<"1:.875:5:000282">> = iolist_to_binary(join(re:split("1.875000282","(\\.\\d\\d((?=0)|\\d(?=\\d)))",[trim]))), + 2}]))), + <<"1:.23::0003938">> = iolist_to_binary(join(re:split("1.230003938","(\\.\\d\\d((?=0)|\\d(?=\\d)))",[]))), + <<"1:.875:5:000282">> = iolist_to_binary(join(re:split("1.875000282","(\\.\\d\\d((?=0)|\\d(?=\\d)))",[trim]))), <<"1:.875:5:000282">> = iolist_to_binary(join(re:split("1.875000282","(\\.\\d\\d((?=0)|\\d(?=\\d)))",[{parts, - 2}]))), - <<"1:.875:5:000282">> = iolist_to_binary(join(re:split("1.875000282","(\\.\\d\\d((?=0)|\\d(?=\\d)))",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(\\.\\d\\d((?=0)|\\d(?=\\d)))",[trim]))), + 2}]))), + <<"1:.875:5:000282">> = iolist_to_binary(join(re:split("1.875000282","(\\.\\d\\d((?=0)|\\d(?=\\d)))",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(\\.\\d\\d((?=0)|\\d(?=\\d)))",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(\\.\\d\\d((?=0)|\\d(?=\\d)))",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(\\.\\d\\d((?=0)|\\d(?=\\d)))",[]))), - <<"1.235">> = iolist_to_binary(join(re:split("1.235","(\\.\\d\\d((?=0)|\\d(?=\\d)))",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(\\.\\d\\d((?=0)|\\d(?=\\d)))",[]))), + <<"1.235">> = iolist_to_binary(join(re:split("1.235","(\\.\\d\\d((?=0)|\\d(?=\\d)))",[trim]))), <<"1.235">> = iolist_to_binary(join(re:split("1.235","(\\.\\d\\d((?=0)|\\d(?=\\d)))",[{parts, - 2}]))), - <<"1.235">> = iolist_to_binary(join(re:split("1.235","(\\.\\d\\d((?=0)|\\d(?=\\d)))",[]))), - <<"">> = iolist_to_binary(join(re:split("ab","a(?)b",[trim]))), + 2}]))), + <<"1.235">> = iolist_to_binary(join(re:split("1.235","(\\.\\d\\d((?=0)|\\d(?=\\d)))",[]))), + <<"">> = iolist_to_binary(join(re:split("ab","a(?)b",[trim]))), <<":">> = iolist_to_binary(join(re:split("ab","a(?)b",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ab","a(?)b",[]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ab","a(?)b",[]))), <<"Food is on the :foo:table">> = iolist_to_binary(join(re:split("Food is on the foo table","\\b(foo)\\s+(\\w+)",[caseless, - trim]))), + trim]))), <<"Food is on the :foo:table:">> = iolist_to_binary(join(re:split("Food is on the foo table","\\b(foo)\\s+(\\w+)",[caseless, {parts, - 2}]))), - <<"Food is on the :foo:table:">> = iolist_to_binary(join(re:split("Food is on the foo table","\\b(foo)\\s+(\\w+)",[caseless]))), - <<"The :d is under the bar in the :n.">> = iolist_to_binary(join(re:split("The food is under the bar in the barn.","foo(.*)bar",[trim]))), + 2}]))), + <<"Food is on the :foo:table:">> = iolist_to_binary(join(re:split("Food is on the foo table","\\b(foo)\\s+(\\w+)",[caseless]))), + <<"The :d is under the bar in the :n.">> = iolist_to_binary(join(re:split("The food is under the bar in the barn.","foo(.*)bar",[trim]))), <<"The :d is under the bar in the :n.">> = iolist_to_binary(join(re:split("The food is under the bar in the barn.","foo(.*)bar",[{parts, - 2}]))), - <<"The :d is under the bar in the :n.">> = iolist_to_binary(join(re:split("The food is under the bar in the barn.","foo(.*)bar",[]))), - <<"The :d is under the : in the barn.">> = iolist_to_binary(join(re:split("The food is under the bar in the barn.","foo(.*?)bar",[trim]))), + 2}]))), + <<"The :d is under the bar in the :n.">> = iolist_to_binary(join(re:split("The food is under the bar in the barn.","foo(.*)bar",[]))), + <<"The :d is under the : in the barn.">> = iolist_to_binary(join(re:split("The food is under the bar in the barn.","foo(.*?)bar",[trim]))), <<"The :d is under the : in the barn.">> = iolist_to_binary(join(re:split("The food is under the bar in the barn.","foo(.*?)bar",[{parts, - 2}]))), - <<"The :d is under the : in the barn.">> = iolist_to_binary(join(re:split("The food is under the bar in the barn.","foo(.*?)bar",[]))), - <<":I have 2 numbers: 53147">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*)(\\d*)",[trim]))), + 2}]))), + <<"The :d is under the : in the barn.">> = iolist_to_binary(join(re:split("The food is under the bar in the barn.","foo(.*?)bar",[]))), + <<":I have 2 numbers: 53147">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*)(\\d*)",[trim]))), <<":I have 2 numbers: 53147::">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*)(\\d*)",[{parts, - 2}]))), - <<":I have 2 numbers: 53147::">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*)(\\d*)",[]))), - <<":I have 2 numbers: 5314:7">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*)(\\d+)",[trim]))), + 2}]))), + <<":I have 2 numbers: 53147::">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*)(\\d*)",[]))), + <<":I have 2 numbers: 5314:7">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*)(\\d+)",[trim]))), <<":I have 2 numbers: 5314:7:">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*)(\\d+)",[{parts, - 2}]))), - <<":I have 2 numbers: 5314:7:">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*)(\\d+)",[]))), - <<":I::: :::h:::a:::v:::e::: :2:: :::n:::u:::m:::b:::e:::r:::s::::::: :53147">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*?)(\\d*)",[trim]))), + 2}]))), + <<":I have 2 numbers: 5314:7:">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*)(\\d+)",[]))), + <<":I::: :::h:::a:::v:::e::: :2:: :::n:::u:::m:::b:::e:::r:::s::::::: :53147">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*?)(\\d*)",[trim]))), <<":I:: have 2 numbers: 53147">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*?)(\\d*)",[{parts, - 2}]))), - <<":I::: :::h:::a:::v:::e::: :2:: :::n:::u:::m:::b:::e:::r:::s::::::: :53147:">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*?)(\\d*)",[]))), - <<":I have :2:: numbers: :53147">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*?)(\\d+)",[trim]))), + 2}]))), + <<":I::: :::h:::a:::v:::e::: :2:: :::n:::u:::m:::b:::e:::r:::s::::::: :53147:">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*?)(\\d*)",[]))), + <<":I have :2:: numbers: :53147">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*?)(\\d+)",[trim]))), <<":I have :2: numbers: 53147">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*?)(\\d+)",[{parts, - 2}]))), - <<":I have :2:: numbers: :53147:">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*?)(\\d+)",[]))), - <<":I have 2 numbers: 5314:7">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*)(\\d+)$",[trim]))), + 2}]))), + <<":I have :2:: numbers: :53147:">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*?)(\\d+)",[]))), + <<":I have 2 numbers: 5314:7">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*)(\\d+)$",[trim]))), <<":I have 2 numbers: 5314:7:">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*)(\\d+)$",[{parts, - 2}]))), - <<":I have 2 numbers: 5314:7:">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*)(\\d+)$",[]))), - <<":I have 2 numbers: :53147">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*?)(\\d+)$",[trim]))), + 2}]))), + <<":I have 2 numbers: 5314:7:">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*)(\\d+)$",[]))), + <<":I have 2 numbers: :53147">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*?)(\\d+)$",[trim]))), <<":I have 2 numbers: :53147:">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*?)(\\d+)$",[{parts, - 2}]))), - <<":I have 2 numbers: :53147:">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*?)(\\d+)$",[]))), - <<":I have 2 numbers: :53147">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*)\\b(\\d+)$",[trim]))), + 2}]))), + <<":I have 2 numbers: :53147:">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*?)(\\d+)$",[]))), + <<":I have 2 numbers: :53147">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*)\\b(\\d+)$",[trim]))), <<":I have 2 numbers: :53147:">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*)\\b(\\d+)$",[{parts, - 2}]))), - <<":I have 2 numbers: :53147:">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*)\\b(\\d+)$",[]))), + 2}]))), + <<":I have 2 numbers: :53147:">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*)\\b(\\d+)$",[]))), ok. run8() -> - <<":I have 2 numbers: :53147">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*\\D)(\\d+)$",[trim]))), + <<":I have 2 numbers: :53147">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*\\D)(\\d+)$",[trim]))), <<":I have 2 numbers: :53147:">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*\\D)(\\d+)$",[{parts, - 2}]))), - <<":I have 2 numbers: :53147:">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*\\D)(\\d+)$",[]))), - <<":C123">> = iolist_to_binary(join(re:split("ABC123","^\\D*(?!123)",[trim]))), + 2}]))), + <<":I have 2 numbers: :53147:">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*\\D)(\\d+)$",[]))), + <<":C123">> = iolist_to_binary(join(re:split("ABC123","^\\D*(?!123)",[trim]))), <<":C123">> = iolist_to_binary(join(re:split("ABC123","^\\D*(?!123)",[{parts, - 2}]))), - <<":C123">> = iolist_to_binary(join(re:split("ABC123","^\\D*(?!123)",[]))), - <<":ABC:445">> = iolist_to_binary(join(re:split("ABC445","^(\\D*)(?=\\d)(?!123)",[trim]))), + 2}]))), + <<":C123">> = iolist_to_binary(join(re:split("ABC123","^\\D*(?!123)",[]))), + <<":ABC:445">> = iolist_to_binary(join(re:split("ABC445","^(\\D*)(?=\\d)(?!123)",[trim]))), <<":ABC:445">> = iolist_to_binary(join(re:split("ABC445","^(\\D*)(?=\\d)(?!123)",[{parts, - 2}]))), - <<":ABC:445">> = iolist_to_binary(join(re:split("ABC445","^(\\D*)(?=\\d)(?!123)",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\D*)(?=\\d)(?!123)",[trim]))), + 2}]))), + <<":ABC:445">> = iolist_to_binary(join(re:split("ABC445","^(\\D*)(?=\\d)(?!123)",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\D*)(?=\\d)(?!123)",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\D*)(?=\\d)(?!123)",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\D*)(?=\\d)(?!123)",[]))), - <<"ABC123">> = iolist_to_binary(join(re:split("ABC123","^(\\D*)(?=\\d)(?!123)",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\D*)(?=\\d)(?!123)",[]))), + <<"ABC123">> = iolist_to_binary(join(re:split("ABC123","^(\\D*)(?=\\d)(?!123)",[trim]))), <<"ABC123">> = iolist_to_binary(join(re:split("ABC123","^(\\D*)(?=\\d)(?!123)",[{parts, - 2}]))), - <<"ABC123">> = iolist_to_binary(join(re:split("ABC123","^(\\D*)(?=\\d)(?!123)",[]))), - <<":789">> = iolist_to_binary(join(re:split("W46]789","^[W-]46]",[trim]))), + 2}]))), + <<"ABC123">> = iolist_to_binary(join(re:split("ABC123","^(\\D*)(?=\\d)(?!123)",[]))), + <<":789">> = iolist_to_binary(join(re:split("W46]789","^[W-]46]",[trim]))), <<":789">> = iolist_to_binary(join(re:split("W46]789","^[W-]46]",[{parts, - 2}]))), - <<":789">> = iolist_to_binary(join(re:split("W46]789","^[W-]46]",[]))), - <<":789">> = iolist_to_binary(join(re:split("-46]789","^[W-]46]",[trim]))), + 2}]))), + <<":789">> = iolist_to_binary(join(re:split("W46]789","^[W-]46]",[]))), + <<":789">> = iolist_to_binary(join(re:split("-46]789","^[W-]46]",[trim]))), <<":789">> = iolist_to_binary(join(re:split("-46]789","^[W-]46]",[{parts, - 2}]))), - <<":789">> = iolist_to_binary(join(re:split("-46]789","^[W-]46]",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[W-]46]",[trim]))), + 2}]))), + <<":789">> = iolist_to_binary(join(re:split("-46]789","^[W-]46]",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[W-]46]",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[W-]46]",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[W-]46]",[]))), - <<"Wall">> = iolist_to_binary(join(re:split("Wall","^[W-]46]",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[W-]46]",[]))), + <<"Wall">> = iolist_to_binary(join(re:split("Wall","^[W-]46]",[trim]))), <<"Wall">> = iolist_to_binary(join(re:split("Wall","^[W-]46]",[{parts, - 2}]))), - <<"Wall">> = iolist_to_binary(join(re:split("Wall","^[W-]46]",[]))), - <<"Zebra">> = iolist_to_binary(join(re:split("Zebra","^[W-]46]",[trim]))), + 2}]))), + <<"Wall">> = iolist_to_binary(join(re:split("Wall","^[W-]46]",[]))), + <<"Zebra">> = iolist_to_binary(join(re:split("Zebra","^[W-]46]",[trim]))), <<"Zebra">> = iolist_to_binary(join(re:split("Zebra","^[W-]46]",[{parts, - 2}]))), - <<"Zebra">> = iolist_to_binary(join(re:split("Zebra","^[W-]46]",[]))), - <<"42">> = iolist_to_binary(join(re:split("42","^[W-]46]",[trim]))), + 2}]))), + <<"Zebra">> = iolist_to_binary(join(re:split("Zebra","^[W-]46]",[]))), + <<"42">> = iolist_to_binary(join(re:split("42","^[W-]46]",[trim]))), <<"42">> = iolist_to_binary(join(re:split("42","^[W-]46]",[{parts, - 2}]))), - <<"42">> = iolist_to_binary(join(re:split("42","^[W-]46]",[]))), - <<"[abcd]">> = iolist_to_binary(join(re:split("[abcd]","^[W-]46]",[trim]))), + 2}]))), + <<"42">> = iolist_to_binary(join(re:split("42","^[W-]46]",[]))), + <<"[abcd]">> = iolist_to_binary(join(re:split("[abcd]","^[W-]46]",[trim]))), <<"[abcd]">> = iolist_to_binary(join(re:split("[abcd]","^[W-]46]",[{parts, - 2}]))), - <<"[abcd]">> = iolist_to_binary(join(re:split("[abcd]","^[W-]46]",[]))), - <<"]abcd[">> = iolist_to_binary(join(re:split("]abcd[","^[W-]46]",[trim]))), + 2}]))), + <<"[abcd]">> = iolist_to_binary(join(re:split("[abcd]","^[W-]46]",[]))), + <<"]abcd[">> = iolist_to_binary(join(re:split("]abcd[","^[W-]46]",[trim]))), <<"]abcd[">> = iolist_to_binary(join(re:split("]abcd[","^[W-]46]",[{parts, - 2}]))), - <<"]abcd[">> = iolist_to_binary(join(re:split("]abcd[","^[W-]46]",[]))), - <<":46]789">> = iolist_to_binary(join(re:split("W46]789","^[W-\\]46]",[trim]))), + 2}]))), + <<"]abcd[">> = iolist_to_binary(join(re:split("]abcd[","^[W-]46]",[]))), + <<":46]789">> = iolist_to_binary(join(re:split("W46]789","^[W-\\]46]",[trim]))), <<":46]789">> = iolist_to_binary(join(re:split("W46]789","^[W-\\]46]",[{parts, - 2}]))), - <<":46]789">> = iolist_to_binary(join(re:split("W46]789","^[W-\\]46]",[]))), - <<":all">> = iolist_to_binary(join(re:split("Wall","^[W-\\]46]",[trim]))), + 2}]))), + <<":46]789">> = iolist_to_binary(join(re:split("W46]789","^[W-\\]46]",[]))), + <<":all">> = iolist_to_binary(join(re:split("Wall","^[W-\\]46]",[trim]))), <<":all">> = iolist_to_binary(join(re:split("Wall","^[W-\\]46]",[{parts, - 2}]))), - <<":all">> = iolist_to_binary(join(re:split("Wall","^[W-\\]46]",[]))), - <<":ebra">> = iolist_to_binary(join(re:split("Zebra","^[W-\\]46]",[trim]))), + 2}]))), + <<":all">> = iolist_to_binary(join(re:split("Wall","^[W-\\]46]",[]))), + <<":ebra">> = iolist_to_binary(join(re:split("Zebra","^[W-\\]46]",[trim]))), <<":ebra">> = iolist_to_binary(join(re:split("Zebra","^[W-\\]46]",[{parts, - 2}]))), - <<":ebra">> = iolist_to_binary(join(re:split("Zebra","^[W-\\]46]",[]))), - <<":ylophone">> = iolist_to_binary(join(re:split("Xylophone","^[W-\\]46]",[trim]))), + 2}]))), + <<":ebra">> = iolist_to_binary(join(re:split("Zebra","^[W-\\]46]",[]))), + <<":ylophone">> = iolist_to_binary(join(re:split("Xylophone","^[W-\\]46]",[trim]))), <<":ylophone">> = iolist_to_binary(join(re:split("Xylophone","^[W-\\]46]",[{parts, - 2}]))), - <<":ylophone">> = iolist_to_binary(join(re:split("Xylophone","^[W-\\]46]",[]))), - <<":2">> = iolist_to_binary(join(re:split("42","^[W-\\]46]",[trim]))), + 2}]))), + <<":ylophone">> = iolist_to_binary(join(re:split("Xylophone","^[W-\\]46]",[]))), + <<":2">> = iolist_to_binary(join(re:split("42","^[W-\\]46]",[trim]))), <<":2">> = iolist_to_binary(join(re:split("42","^[W-\\]46]",[{parts, - 2}]))), - <<":2">> = iolist_to_binary(join(re:split("42","^[W-\\]46]",[]))), - <<":abcd]">> = iolist_to_binary(join(re:split("[abcd]","^[W-\\]46]",[trim]))), + 2}]))), + <<":2">> = iolist_to_binary(join(re:split("42","^[W-\\]46]",[]))), + <<":abcd]">> = iolist_to_binary(join(re:split("[abcd]","^[W-\\]46]",[trim]))), <<":abcd]">> = iolist_to_binary(join(re:split("[abcd]","^[W-\\]46]",[{parts, - 2}]))), - <<":abcd]">> = iolist_to_binary(join(re:split("[abcd]","^[W-\\]46]",[]))), - <<":abcd[">> = iolist_to_binary(join(re:split("]abcd[","^[W-\\]46]",[trim]))), + 2}]))), + <<":abcd]">> = iolist_to_binary(join(re:split("[abcd]","^[W-\\]46]",[]))), + <<":abcd[">> = iolist_to_binary(join(re:split("]abcd[","^[W-\\]46]",[trim]))), <<":abcd[">> = iolist_to_binary(join(re:split("]abcd[","^[W-\\]46]",[{parts, - 2}]))), - <<":abcd[">> = iolist_to_binary(join(re:split("]abcd[","^[W-\\]46]",[]))), - <<":backslash">> = iolist_to_binary(join(re:split("\\backslash","^[W-\\]46]",[trim]))), + 2}]))), + <<":abcd[">> = iolist_to_binary(join(re:split("]abcd[","^[W-\\]46]",[]))), + <<":backslash">> = iolist_to_binary(join(re:split("\\backslash","^[W-\\]46]",[trim]))), <<":backslash">> = iolist_to_binary(join(re:split("\\backslash","^[W-\\]46]",[{parts, - 2}]))), - <<":backslash">> = iolist_to_binary(join(re:split("\\backslash","^[W-\\]46]",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[W-\\]46]",[trim]))), + 2}]))), + <<":backslash">> = iolist_to_binary(join(re:split("\\backslash","^[W-\\]46]",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[W-\\]46]",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[W-\\]46]",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[W-\\]46]",[]))), - <<"-46]789">> = iolist_to_binary(join(re:split("-46]789","^[W-\\]46]",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[W-\\]46]",[]))), + <<"-46]789">> = iolist_to_binary(join(re:split("-46]789","^[W-\\]46]",[trim]))), <<"-46]789">> = iolist_to_binary(join(re:split("-46]789","^[W-\\]46]",[{parts, - 2}]))), - <<"-46]789">> = iolist_to_binary(join(re:split("-46]789","^[W-\\]46]",[]))), - <<"well">> = iolist_to_binary(join(re:split("well","^[W-\\]46]",[trim]))), + 2}]))), + <<"-46]789">> = iolist_to_binary(join(re:split("-46]789","^[W-\\]46]",[]))), + <<"well">> = iolist_to_binary(join(re:split("well","^[W-\\]46]",[trim]))), <<"well">> = iolist_to_binary(join(re:split("well","^[W-\\]46]",[{parts, - 2}]))), - <<"well">> = iolist_to_binary(join(re:split("well","^[W-\\]46]",[]))), - <<"">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark otherword","word (?:[a-zA-Z0-9]+ ){0,10}otherword",[trim]))), + 2}]))), + <<"well">> = iolist_to_binary(join(re:split("well","^[W-\\]46]",[]))), + <<"">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark otherword","word (?:[a-zA-Z0-9]+ ){0,10}otherword",[trim]))), <<":">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark otherword","word (?:[a-zA-Z0-9]+ ){0,10}otherword",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark otherword","word (?:[a-zA-Z0-9]+ ){0,10}otherword",[]))), - <<"word cat dog elephant mussel cow horse canary baboon snake shark">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark","word (?:[a-zA-Z0-9]+ ){0,10}otherword",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark otherword","word (?:[a-zA-Z0-9]+ ){0,10}otherword",[]))), + <<"word cat dog elephant mussel cow horse canary baboon snake shark">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark","word (?:[a-zA-Z0-9]+ ){0,10}otherword",[trim]))), <<"word cat dog elephant mussel cow horse canary baboon snake shark">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark","word (?:[a-zA-Z0-9]+ ){0,10}otherword",[{parts, - 2}]))), - <<"word cat dog elephant mussel cow horse canary baboon snake shark">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark","word (?:[a-zA-Z0-9]+ ){0,10}otherword",[]))), - <<"word cat dog elephant mussel cow horse canary baboon snake shark the quick brown fox and the lazy dog and several other words getting close to thirty by now I hope">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark the quick brown fox and the lazy dog and several other words getting close to thirty by now I hope","word (?:[a-zA-Z0-9]+ ){0,300}otherword",[trim]))), + 2}]))), + <<"word cat dog elephant mussel cow horse canary baboon snake shark">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark","word (?:[a-zA-Z0-9]+ ){0,10}otherword",[]))), + <<"word cat dog elephant mussel cow horse canary baboon snake shark the quick brown fox and the lazy dog and several other words getting close to thirty by now I hope">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark the quick brown fox and the lazy dog and several other words getting close to thirty by now I hope","word (?:[a-zA-Z0-9]+ ){0,300}otherword",[trim]))), <<"word cat dog elephant mussel cow horse canary baboon snake shark the quick brown fox and the lazy dog and several other words getting close to thirty by now I hope">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark the quick brown fox and the lazy dog and several other words getting close to thirty by now I hope","word (?:[a-zA-Z0-9]+ ){0,300}otherword",[{parts, - 2}]))), - <<"word cat dog elephant mussel cow horse canary baboon snake shark the quick brown fox and the lazy dog and several other words getting close to thirty by now I hope">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark the quick brown fox and the lazy dog and several other words getting close to thirty by now I hope","word (?:[a-zA-Z0-9]+ ){0,300}otherword",[]))), - <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,0}",[trim]))), + 2}]))), + <<"word cat dog elephant mussel cow horse canary baboon snake shark the quick brown fox and the lazy dog and several other words getting close to thirty by now I hope">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark the quick brown fox and the lazy dog and several other words getting close to thirty by now I hope","word (?:[a-zA-Z0-9]+ ){0,300}otherword",[]))), + <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,0}",[trim]))), <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,0}",[{parts, - 2}]))), - <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,0}",[]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","^(a){0,0}",[trim]))), + 2}]))), + <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,0}",[]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","^(a){0,0}",[trim]))), <<"abc">> = iolist_to_binary(join(re:split("abc","^(a){0,0}",[{parts, - 2}]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","^(a){0,0}",[]))), - <<"aab">> = iolist_to_binary(join(re:split("aab","^(a){0,0}",[trim]))), + 2}]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","^(a){0,0}",[]))), + <<"aab">> = iolist_to_binary(join(re:split("aab","^(a){0,0}",[trim]))), <<"aab">> = iolist_to_binary(join(re:split("aab","^(a){0,0}",[{parts, - 2}]))), - <<"aab">> = iolist_to_binary(join(re:split("aab","^(a){0,0}",[]))), - <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,1}",[trim]))), + 2}]))), + <<"aab">> = iolist_to_binary(join(re:split("aab","^(a){0,0}",[]))), + <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,1}",[trim]))), <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,1}",[{parts, - 2}]))), - <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,1}",[]))), - <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){0,1}",[trim]))), + 2}]))), + <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,1}",[]))), + <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){0,1}",[trim]))), <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){0,1}",[{parts, - 2}]))), - <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){0,1}",[]))), - <<":a:ab">> = iolist_to_binary(join(re:split("aab","^(a){0,1}",[trim]))), + 2}]))), + <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){0,1}",[]))), + <<":a:ab">> = iolist_to_binary(join(re:split("aab","^(a){0,1}",[trim]))), <<":a:ab">> = iolist_to_binary(join(re:split("aab","^(a){0,1}",[{parts, - 2}]))), - <<":a:ab">> = iolist_to_binary(join(re:split("aab","^(a){0,1}",[]))), - <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,2}",[trim]))), + 2}]))), + <<":a:ab">> = iolist_to_binary(join(re:split("aab","^(a){0,1}",[]))), + <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,2}",[trim]))), <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,2}",[{parts, - 2}]))), - <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,2}",[]))), - <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){0,2}",[trim]))), + 2}]))), + <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,2}",[]))), + <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){0,2}",[trim]))), <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){0,2}",[{parts, - 2}]))), - <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){0,2}",[]))), - <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){0,2}",[trim]))), + 2}]))), + <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){0,2}",[]))), + <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){0,2}",[trim]))), <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){0,2}",[{parts, - 2}]))), - <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){0,2}",[]))), - <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,3}",[trim]))), + 2}]))), + <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){0,2}",[]))), + <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,3}",[trim]))), <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,3}",[{parts, - 2}]))), - <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,3}",[]))), - <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){0,3}",[trim]))), + 2}]))), + <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,3}",[]))), + <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){0,3}",[trim]))), <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){0,3}",[{parts, - 2}]))), - <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){0,3}",[]))), - <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){0,3}",[trim]))), + 2}]))), + <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){0,3}",[]))), + <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){0,3}",[trim]))), <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){0,3}",[{parts, - 2}]))), - <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){0,3}",[]))), - <<":a">> = iolist_to_binary(join(re:split("aaa","^(a){0,3}",[trim]))), + 2}]))), + <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){0,3}",[]))), + <<":a">> = iolist_to_binary(join(re:split("aaa","^(a){0,3}",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aaa","^(a){0,3}",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aaa","^(a){0,3}",[]))), - <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,}",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aaa","^(a){0,3}",[]))), + <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,}",[trim]))), <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,}",[{parts, - 2}]))), - <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,}",[]))), - <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){0,}",[trim]))), + 2}]))), + <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,}",[]))), + <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){0,}",[trim]))), <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){0,}",[{parts, - 2}]))), - <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){0,}",[]))), - <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){0,}",[trim]))), + 2}]))), + <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){0,}",[]))), + <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){0,}",[trim]))), <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){0,}",[{parts, - 2}]))), - <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){0,}",[]))), - <<":a">> = iolist_to_binary(join(re:split("aaa","^(a){0,}",[trim]))), + 2}]))), + <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){0,}",[]))), + <<":a">> = iolist_to_binary(join(re:split("aaa","^(a){0,}",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aaa","^(a){0,}",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aaa","^(a){0,}",[]))), - <<":a">> = iolist_to_binary(join(re:split("aaaaaaaa","^(a){0,}",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aaa","^(a){0,}",[]))), + <<":a">> = iolist_to_binary(join(re:split("aaaaaaaa","^(a){0,}",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aaaaaaaa","^(a){0,}",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aaaaaaaa","^(a){0,}",[]))), - <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){1,1}",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aaaaaaaa","^(a){0,}",[]))), + <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){1,1}",[trim]))), <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){1,1}",[{parts, - 2}]))), - <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){1,1}",[]))), - <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){1,1}",[trim]))), + 2}]))), + <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){1,1}",[]))), + <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){1,1}",[trim]))), <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){1,1}",[{parts, - 2}]))), - <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){1,1}",[]))), - <<":a:ab">> = iolist_to_binary(join(re:split("aab","^(a){1,1}",[trim]))), + 2}]))), + <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){1,1}",[]))), + <<":a:ab">> = iolist_to_binary(join(re:split("aab","^(a){1,1}",[trim]))), <<":a:ab">> = iolist_to_binary(join(re:split("aab","^(a){1,1}",[{parts, - 2}]))), - <<":a:ab">> = iolist_to_binary(join(re:split("aab","^(a){1,1}",[]))), - <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){1,2}",[trim]))), + 2}]))), + <<":a:ab">> = iolist_to_binary(join(re:split("aab","^(a){1,1}",[]))), + <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){1,2}",[trim]))), <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){1,2}",[{parts, - 2}]))), - <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){1,2}",[]))), - <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){1,2}",[trim]))), + 2}]))), + <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){1,2}",[]))), + <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){1,2}",[trim]))), <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){1,2}",[{parts, - 2}]))), - <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){1,2}",[]))), - <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){1,2}",[trim]))), + 2}]))), + <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){1,2}",[]))), + <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){1,2}",[trim]))), <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){1,2}",[{parts, - 2}]))), - <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){1,2}",[]))), - <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){1,3}",[trim]))), + 2}]))), + <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){1,2}",[]))), + <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){1,3}",[trim]))), <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){1,3}",[{parts, - 2}]))), - <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){1,3}",[]))), - <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){1,3}",[trim]))), + 2}]))), + <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){1,3}",[]))), + <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){1,3}",[trim]))), <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){1,3}",[{parts, - 2}]))), - <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){1,3}",[]))), - <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){1,3}",[trim]))), + 2}]))), + <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){1,3}",[]))), + <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){1,3}",[trim]))), <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){1,3}",[{parts, - 2}]))), - <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){1,3}",[]))), - <<":a">> = iolist_to_binary(join(re:split("aaa","^(a){1,3}",[trim]))), + 2}]))), + <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){1,3}",[]))), + <<":a">> = iolist_to_binary(join(re:split("aaa","^(a){1,3}",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aaa","^(a){1,3}",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aaa","^(a){1,3}",[]))), - <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){1,}",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aaa","^(a){1,3}",[]))), + <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){1,}",[trim]))), <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){1,}",[{parts, - 2}]))), - <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){1,}",[]))), - <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){1,}",[trim]))), + 2}]))), + <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){1,}",[]))), + <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){1,}",[trim]))), <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){1,}",[{parts, - 2}]))), - <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){1,}",[]))), - <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){1,}",[trim]))), + 2}]))), + <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){1,}",[]))), + <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){1,}",[trim]))), <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){1,}",[{parts, - 2}]))), - <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){1,}",[]))), - <<":a">> = iolist_to_binary(join(re:split("aaa","^(a){1,}",[trim]))), + 2}]))), + <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){1,}",[]))), + <<":a">> = iolist_to_binary(join(re:split("aaa","^(a){1,}",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aaa","^(a){1,}",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aaa","^(a){1,}",[]))), - <<":a">> = iolist_to_binary(join(re:split("aaaaaaaa","^(a){1,}",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aaa","^(a){1,}",[]))), + <<":a">> = iolist_to_binary(join(re:split("aaaaaaaa","^(a){1,}",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aaaaaaaa","^(a){1,}",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aaaaaaaa","^(a){1,}",[]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aaaaaaaa","^(a){1,}",[]))), <<"borfle : no">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*\\.gif",[trim]))), +no",".*\\.gif",[trim]))), <<"borfle : no">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*\\.gif",[{parts,2}]))), +no",".*\\.gif",[{parts,2}]))), <<"borfle : no">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*\\.gif",[]))), +no",".*\\.gif",[]))), <<"borfle : no">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".{0,}\\.gif",[trim]))), +no",".{0,}\\.gif",[trim]))), <<"borfle : no">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".{0,}\\.gif",[{parts,2}]))), +no",".{0,}\\.gif",[{parts,2}]))), <<"borfle : no">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".{0,}\\.gif",[]))), +no",".{0,}\\.gif",[]))), <<"borfle : no">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*\\.gif",[multiline,trim]))), +no",".*\\.gif",[multiline,trim]))), <<"borfle : no">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*\\.gif",[multiline,{parts,2}]))), +no",".*\\.gif",[multiline,{parts,2}]))), <<"borfle : no">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*\\.gif",[multiline]))), +no",".*\\.gif",[multiline]))), ok. run9() -> <<": no">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*\\.gif",[dotall,trim]))), +no",".*\\.gif",[dotall,trim]))), <<": no">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*\\.gif",[dotall,{parts,2}]))), +no",".*\\.gif",[dotall,{parts,2}]))), <<": no">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*\\.gif",[dotall]))), +no",".*\\.gif",[dotall]))), <<": no">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*\\.gif",[multiline,dotall,trim]))), +no",".*\\.gif",[multiline,dotall,trim]))), <<": no">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*\\.gif",[multiline,dotall,{parts,2}]))), +no",".*\\.gif",[multiline,dotall,{parts,2}]))), <<": no">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*\\.gif",[multiline,dotall]))), +no",".*\\.gif",[multiline,dotall]))), <<"borfle bib.gif ">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[trim]))), +no",".*$",[trim]))), <<"borfle bib.gif :">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[{parts,2}]))), +no",".*$",[{parts,2}]))), <<"borfle bib.gif :">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[]))), +no",".*$",[]))), <<": : ">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[multiline,trim]))), +no",".*$",[multiline,trim]))), <<": bib.gif no">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[multiline,{parts,2}]))), +no",".*$",[multiline,{parts,2}]))), <<": : :">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[multiline]))), +no",".*$",[multiline]))), <<"">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[dotall,trim]))), +no",".*$",[dotall,trim]))), <<":">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[dotall,{parts,2}]))), +no",".*$",[dotall,{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[dotall]))), +no",".*$",[dotall]))), <<"">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[multiline,dotall,trim]))), +no",".*$",[multiline,dotall,trim]))), <<":">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[multiline,dotall,{parts,2}]))), +no",".*$",[multiline,dotall,{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[multiline,dotall]))), +no",".*$",[multiline,dotall]))), <<"borfle bib.gif ">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[trim]))), +no",".*$",[trim]))), <<"borfle bib.gif :">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[{parts,2}]))), +no",".*$",[{parts,2}]))), <<"borfle bib.gif :">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[]))), +no",".*$",[]))), <<": : ">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[multiline,trim]))), +no",".*$",[multiline,trim]))), <<": bib.gif no">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[multiline,{parts,2}]))), +no",".*$",[multiline,{parts,2}]))), <<": : :">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[multiline]))), +no",".*$",[multiline]))), <<"">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[dotall,trim]))), +no",".*$",[dotall,trim]))), <<":">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[dotall,{parts,2}]))), +no",".*$",[dotall,{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[dotall]))), +no",".*$",[dotall]))), <<"">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[multiline,dotall,trim]))), +no",".*$",[multiline,dotall,trim]))), <<":">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[multiline,dotall,{parts,2}]))), +no",".*$",[multiline,dotall,{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[multiline,dotall]))), +no",".*$",[multiline,dotall]))), <<"abcde :1234X:yz">> = iolist_to_binary(join(re:split("abcde -1234Xyz","(.*X|^B)",[trim]))), +1234Xyz","(.*X|^B)",[trim]))), <<"abcde :1234X:yz">> = iolist_to_binary(join(re:split("abcde -1234Xyz","(.*X|^B)",[{parts,2}]))), +1234Xyz","(.*X|^B)",[{parts,2}]))), <<"abcde :1234X:yz">> = iolist_to_binary(join(re:split("abcde -1234Xyz","(.*X|^B)",[]))), - <<":B:arFoo">> = iolist_to_binary(join(re:split("BarFoo","(.*X|^B)",[trim]))), +1234Xyz","(.*X|^B)",[]))), + <<":B:arFoo">> = iolist_to_binary(join(re:split("BarFoo","(.*X|^B)",[trim]))), <<":B:arFoo">> = iolist_to_binary(join(re:split("BarFoo","(.*X|^B)",[{parts, - 2}]))), - <<":B:arFoo">> = iolist_to_binary(join(re:split("BarFoo","(.*X|^B)",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(.*X|^B)",[trim]))), + 2}]))), + <<":B:arFoo">> = iolist_to_binary(join(re:split("BarFoo","(.*X|^B)",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(.*X|^B)",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(.*X|^B)",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(.*X|^B)",[]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(.*X|^B)",[]))), <<"abcde Bar">> = iolist_to_binary(join(re:split("abcde -Bar","(.*X|^B)",[trim]))), +Bar","(.*X|^B)",[trim]))), <<"abcde Bar">> = iolist_to_binary(join(re:split("abcde -Bar","(.*X|^B)",[{parts,2}]))), +Bar","(.*X|^B)",[{parts,2}]))), <<"abcde Bar">> = iolist_to_binary(join(re:split("abcde -Bar","(.*X|^B)",[]))), +Bar","(.*X|^B)",[]))), <<"abcde :1234X:yz">> = iolist_to_binary(join(re:split("abcde -1234Xyz","(.*X|^B)",[multiline,trim]))), +1234Xyz","(.*X|^B)",[multiline,trim]))), <<"abcde :1234X:yz">> = iolist_to_binary(join(re:split("abcde -1234Xyz","(.*X|^B)",[multiline,{parts,2}]))), +1234Xyz","(.*X|^B)",[multiline,{parts,2}]))), <<"abcde :1234X:yz">> = iolist_to_binary(join(re:split("abcde -1234Xyz","(.*X|^B)",[multiline]))), +1234Xyz","(.*X|^B)",[multiline]))), <<":B:arFoo">> = iolist_to_binary(join(re:split("BarFoo","(.*X|^B)",[multiline, - trim]))), + trim]))), <<":B:arFoo">> = iolist_to_binary(join(re:split("BarFoo","(.*X|^B)",[multiline, {parts, - 2}]))), - <<":B:arFoo">> = iolist_to_binary(join(re:split("BarFoo","(.*X|^B)",[multiline]))), + 2}]))), + <<":B:arFoo">> = iolist_to_binary(join(re:split("BarFoo","(.*X|^B)",[multiline]))), <<"abcde :B:ar">> = iolist_to_binary(join(re:split("abcde -Bar","(.*X|^B)",[multiline,trim]))), +Bar","(.*X|^B)",[multiline,trim]))), <<"abcde :B:ar">> = iolist_to_binary(join(re:split("abcde -Bar","(.*X|^B)",[multiline,{parts,2}]))), +Bar","(.*X|^B)",[multiline,{parts,2}]))), <<"abcde :B:ar">> = iolist_to_binary(join(re:split("abcde -Bar","(.*X|^B)",[multiline]))), +Bar","(.*X|^B)",[multiline]))), <<":abcde 1234X:yz">> = iolist_to_binary(join(re:split("abcde -1234Xyz","(.*X|^B)",[dotall,trim]))), +1234Xyz","(.*X|^B)",[dotall,trim]))), <<":abcde 1234X:yz">> = iolist_to_binary(join(re:split("abcde -1234Xyz","(.*X|^B)",[dotall,{parts,2}]))), +1234Xyz","(.*X|^B)",[dotall,{parts,2}]))), <<":abcde 1234X:yz">> = iolist_to_binary(join(re:split("abcde -1234Xyz","(.*X|^B)",[dotall]))), +1234Xyz","(.*X|^B)",[dotall]))), <<":B:arFoo">> = iolist_to_binary(join(re:split("BarFoo","(.*X|^B)",[dotall, - trim]))), + trim]))), <<":B:arFoo">> = iolist_to_binary(join(re:split("BarFoo","(.*X|^B)",[dotall, {parts, - 2}]))), - <<":B:arFoo">> = iolist_to_binary(join(re:split("BarFoo","(.*X|^B)",[dotall]))), + 2}]))), + <<":B:arFoo">> = iolist_to_binary(join(re:split("BarFoo","(.*X|^B)",[dotall]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(.*X|^B)",[dotall, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(.*X|^B)",[dotall, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(.*X|^B)",[dotall]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(.*X|^B)",[dotall]))), <<"abcde Bar">> = iolist_to_binary(join(re:split("abcde -Bar","(.*X|^B)",[dotall,trim]))), +Bar","(.*X|^B)",[dotall,trim]))), <<"abcde Bar">> = iolist_to_binary(join(re:split("abcde -Bar","(.*X|^B)",[dotall,{parts,2}]))), +Bar","(.*X|^B)",[dotall,{parts,2}]))), <<"abcde Bar">> = iolist_to_binary(join(re:split("abcde -Bar","(.*X|^B)",[dotall]))), +Bar","(.*X|^B)",[dotall]))), <<":abcde 1234X:yz">> = iolist_to_binary(join(re:split("abcde -1234Xyz","(.*X|^B)",[multiline,dotall,trim]))), +1234Xyz","(.*X|^B)",[multiline,dotall,trim]))), <<":abcde 1234X:yz">> = iolist_to_binary(join(re:split("abcde -1234Xyz","(.*X|^B)",[multiline,dotall,{parts,2}]))), +1234Xyz","(.*X|^B)",[multiline,dotall,{parts,2}]))), <<":abcde 1234X:yz">> = iolist_to_binary(join(re:split("abcde -1234Xyz","(.*X|^B)",[multiline,dotall]))), +1234Xyz","(.*X|^B)",[multiline,dotall]))), <<":B:arFoo">> = iolist_to_binary(join(re:split("BarFoo","(.*X|^B)",[multiline, dotall, - trim]))), + trim]))), <<":B:arFoo">> = iolist_to_binary(join(re:split("BarFoo","(.*X|^B)",[multiline, dotall, {parts, - 2}]))), + 2}]))), <<":B:arFoo">> = iolist_to_binary(join(re:split("BarFoo","(.*X|^B)",[multiline, - dotall]))), + dotall]))), <<"abcde :B:ar">> = iolist_to_binary(join(re:split("abcde -Bar","(.*X|^B)",[multiline,dotall,trim]))), +Bar","(.*X|^B)",[multiline,dotall,trim]))), <<"abcde :B:ar">> = iolist_to_binary(join(re:split("abcde -Bar","(.*X|^B)",[multiline,dotall,{parts,2}]))), +Bar","(.*X|^B)",[multiline,dotall,{parts,2}]))), <<"abcde :B:ar">> = iolist_to_binary(join(re:split("abcde -Bar","(.*X|^B)",[multiline,dotall]))), +Bar","(.*X|^B)",[multiline,dotall]))), <<":abcde 1234X:yz">> = iolist_to_binary(join(re:split("abcde -1234Xyz","(?s)(.*X|^B)",[trim]))), +1234Xyz","(?s)(.*X|^B)",[trim]))), <<":abcde 1234X:yz">> = iolist_to_binary(join(re:split("abcde -1234Xyz","(?s)(.*X|^B)",[{parts,2}]))), +1234Xyz","(?s)(.*X|^B)",[{parts,2}]))), <<":abcde 1234X:yz">> = iolist_to_binary(join(re:split("abcde -1234Xyz","(?s)(.*X|^B)",[]))), - <<":B:arFoo">> = iolist_to_binary(join(re:split("BarFoo","(?s)(.*X|^B)",[trim]))), +1234Xyz","(?s)(.*X|^B)",[]))), + <<":B:arFoo">> = iolist_to_binary(join(re:split("BarFoo","(?s)(.*X|^B)",[trim]))), <<":B:arFoo">> = iolist_to_binary(join(re:split("BarFoo","(?s)(.*X|^B)",[{parts, - 2}]))), - <<":B:arFoo">> = iolist_to_binary(join(re:split("BarFoo","(?s)(.*X|^B)",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?s)(.*X|^B)",[trim]))), + 2}]))), + <<":B:arFoo">> = iolist_to_binary(join(re:split("BarFoo","(?s)(.*X|^B)",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?s)(.*X|^B)",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?s)(.*X|^B)",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?s)(.*X|^B)",[]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?s)(.*X|^B)",[]))), <<"abcde Bar">> = iolist_to_binary(join(re:split("abcde -Bar","(?s)(.*X|^B)",[trim]))), +Bar","(?s)(.*X|^B)",[trim]))), <<"abcde Bar">> = iolist_to_binary(join(re:split("abcde -Bar","(?s)(.*X|^B)",[{parts,2}]))), +Bar","(?s)(.*X|^B)",[{parts,2}]))), <<"abcde Bar">> = iolist_to_binary(join(re:split("abcde -Bar","(?s)(.*X|^B)",[]))), +Bar","(?s)(.*X|^B)",[]))), <<":yz">> = iolist_to_binary(join(re:split("abcde -1234Xyz","(?s:.*X|^B)",[trim]))), +1234Xyz","(?s:.*X|^B)",[trim]))), <<":yz">> = iolist_to_binary(join(re:split("abcde -1234Xyz","(?s:.*X|^B)",[{parts,2}]))), +1234Xyz","(?s:.*X|^B)",[{parts,2}]))), <<":yz">> = iolist_to_binary(join(re:split("abcde -1234Xyz","(?s:.*X|^B)",[]))), - <<":arFoo">> = iolist_to_binary(join(re:split("BarFoo","(?s:.*X|^B)",[trim]))), +1234Xyz","(?s:.*X|^B)",[]))), + <<":arFoo">> = iolist_to_binary(join(re:split("BarFoo","(?s:.*X|^B)",[trim]))), <<":arFoo">> = iolist_to_binary(join(re:split("BarFoo","(?s:.*X|^B)",[{parts, - 2}]))), - <<":arFoo">> = iolist_to_binary(join(re:split("BarFoo","(?s:.*X|^B)",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?s:.*X|^B)",[trim]))), + 2}]))), + <<":arFoo">> = iolist_to_binary(join(re:split("BarFoo","(?s:.*X|^B)",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?s:.*X|^B)",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?s:.*X|^B)",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?s:.*X|^B)",[]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?s:.*X|^B)",[]))), <<"abcde Bar">> = iolist_to_binary(join(re:split("abcde -Bar","(?s:.*X|^B)",[trim]))), +Bar","(?s:.*X|^B)",[trim]))), <<"abcde Bar">> = iolist_to_binary(join(re:split("abcde -Bar","(?s:.*X|^B)",[{parts,2}]))), +Bar","(?s:.*X|^B)",[{parts,2}]))), <<"abcde Bar">> = iolist_to_binary(join(re:split("abcde -Bar","(?s:.*X|^B)",[]))), - <<"**** Failers">> = iolist_to_binary(join(re:split("**** Failers","^.*B",[trim]))), +Bar","(?s:.*X|^B)",[]))), + <<"**** Failers">> = iolist_to_binary(join(re:split("**** Failers","^.*B",[trim]))), <<"**** Failers">> = iolist_to_binary(join(re:split("**** Failers","^.*B",[{parts, - 2}]))), - <<"**** Failers">> = iolist_to_binary(join(re:split("**** Failers","^.*B",[]))), + 2}]))), + <<"**** Failers">> = iolist_to_binary(join(re:split("**** Failers","^.*B",[]))), <<"abc B">> = iolist_to_binary(join(re:split("abc -B","^.*B",[trim]))), +B","^.*B",[trim]))), <<"abc B">> = iolist_to_binary(join(re:split("abc -B","^.*B",[{parts,2}]))), +B","^.*B",[{parts,2}]))), <<"abc B">> = iolist_to_binary(join(re:split("abc -B","^.*B",[]))), +B","^.*B",[]))), <<"">> = iolist_to_binary(join(re:split("abc -B","(?s)^.*B",[trim]))), +B","(?s)^.*B",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc -B","(?s)^.*B",[{parts,2}]))), +B","(?s)^.*B",[{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("abc -B","(?s)^.*B",[]))), +B","(?s)^.*B",[]))), <<"abc ">> = iolist_to_binary(join(re:split("abc -B","(?m)^.*B",[trim]))), +B","(?m)^.*B",[trim]))), <<"abc :">> = iolist_to_binary(join(re:split("abc -B","(?m)^.*B",[{parts,2}]))), +B","(?m)^.*B",[{parts,2}]))), <<"abc :">> = iolist_to_binary(join(re:split("abc -B","(?m)^.*B",[]))), +B","(?m)^.*B",[]))), <<"">> = iolist_to_binary(join(re:split("abc -B","(?ms)^.*B",[trim]))), +B","(?ms)^.*B",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc -B","(?ms)^.*B",[{parts,2}]))), +B","(?ms)^.*B",[{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("abc -B","(?ms)^.*B",[]))), +B","(?ms)^.*B",[]))), ok. run10() -> <<"abc ">> = iolist_to_binary(join(re:split("abc -B","(?ms)^B",[trim]))), +B","(?ms)^B",[trim]))), <<"abc :">> = iolist_to_binary(join(re:split("abc -B","(?ms)^B",[{parts,2}]))), +B","(?ms)^B",[{parts,2}]))), <<"abc :">> = iolist_to_binary(join(re:split("abc -B","(?ms)^B",[]))), - <<"">> = iolist_to_binary(join(re:split("B","(?s)B$",[trim]))), +B","(?ms)^B",[]))), + <<"">> = iolist_to_binary(join(re:split("B","(?s)B$",[trim]))), <<":">> = iolist_to_binary(join(re:split("B","(?s)B$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("B","(?s)B$",[]))), - <<"">> = iolist_to_binary(join(re:split("123456654321","^[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("B","(?s)B$",[]))), + <<"">> = iolist_to_binary(join(re:split("123456654321","^[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]",[trim]))), <<":">> = iolist_to_binary(join(re:split("123456654321","^[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("123456654321","^[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]",[]))), - <<"">> = iolist_to_binary(join(re:split("123456654321","^\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("123456654321","^[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]",[]))), + <<"">> = iolist_to_binary(join(re:split("123456654321","^\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d",[trim]))), <<":">> = iolist_to_binary(join(re:split("123456654321","^\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("123456654321","^\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d",[]))), - <<"">> = iolist_to_binary(join(re:split("123456654321","^[\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("123456654321","^\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d",[]))), + <<"">> = iolist_to_binary(join(re:split("123456654321","^[\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d]",[trim]))), <<":">> = iolist_to_binary(join(re:split("123456654321","^[\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("123456654321","^[\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d]",[]))), - <<"">> = iolist_to_binary(join(re:split("abcabcabcabc","^[abc]{12}",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("123456654321","^[\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d]",[]))), + <<"">> = iolist_to_binary(join(re:split("abcabcabcabc","^[abc]{12}",[trim]))), <<":">> = iolist_to_binary(join(re:split("abcabcabcabc","^[abc]{12}",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abcabcabcabc","^[abc]{12}",[]))), - <<"">> = iolist_to_binary(join(re:split("abcabcabcabc","^[a-c]{12}",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abcabcabcabc","^[abc]{12}",[]))), + <<"">> = iolist_to_binary(join(re:split("abcabcabcabc","^[a-c]{12}",[trim]))), <<":">> = iolist_to_binary(join(re:split("abcabcabcabc","^[a-c]{12}",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abcabcabcabc","^[a-c]{12}",[]))), - <<":c">> = iolist_to_binary(join(re:split("abcabcabcabc","^(a|b|c){12}",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abcabcabcabc","^[a-c]{12}",[]))), + <<":c">> = iolist_to_binary(join(re:split("abcabcabcabc","^(a|b|c){12}",[trim]))), <<":c:">> = iolist_to_binary(join(re:split("abcabcabcabc","^(a|b|c){12}",[{parts, - 2}]))), - <<":c:">> = iolist_to_binary(join(re:split("abcabcabcabc","^(a|b|c){12}",[]))), - <<"">> = iolist_to_binary(join(re:split("n","^[abcdefghijklmnopqrstuvwxy0123456789]",[trim]))), + 2}]))), + <<":c:">> = iolist_to_binary(join(re:split("abcabcabcabc","^(a|b|c){12}",[]))), + <<"">> = iolist_to_binary(join(re:split("n","^[abcdefghijklmnopqrstuvwxy0123456789]",[trim]))), <<":">> = iolist_to_binary(join(re:split("n","^[abcdefghijklmnopqrstuvwxy0123456789]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("n","^[abcdefghijklmnopqrstuvwxy0123456789]",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[abcdefghijklmnopqrstuvwxy0123456789]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("n","^[abcdefghijklmnopqrstuvwxy0123456789]",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[abcdefghijklmnopqrstuvwxy0123456789]",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[abcdefghijklmnopqrstuvwxy0123456789]",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[abcdefghijklmnopqrstuvwxy0123456789]",[]))), - <<"z">> = iolist_to_binary(join(re:split("z","^[abcdefghijklmnopqrstuvwxy0123456789]",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[abcdefghijklmnopqrstuvwxy0123456789]",[]))), + <<"z">> = iolist_to_binary(join(re:split("z","^[abcdefghijklmnopqrstuvwxy0123456789]",[trim]))), <<"z">> = iolist_to_binary(join(re:split("z","^[abcdefghijklmnopqrstuvwxy0123456789]",[{parts, - 2}]))), - <<"z">> = iolist_to_binary(join(re:split("z","^[abcdefghijklmnopqrstuvwxy0123456789]",[]))), - <<"">> = iolist_to_binary(join(re:split("abcd","abcde{0,0}",[trim]))), + 2}]))), + <<"z">> = iolist_to_binary(join(re:split("z","^[abcdefghijklmnopqrstuvwxy0123456789]",[]))), + <<"">> = iolist_to_binary(join(re:split("abcd","abcde{0,0}",[trim]))), <<":">> = iolist_to_binary(join(re:split("abcd","abcde{0,0}",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abcd","abcde{0,0}",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abcde{0,0}",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abcd","abcde{0,0}",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abcde{0,0}",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abcde{0,0}",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abcde{0,0}",[]))), - <<"abce">> = iolist_to_binary(join(re:split("abce","abcde{0,0}",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abcde{0,0}",[]))), + <<"abce">> = iolist_to_binary(join(re:split("abce","abcde{0,0}",[trim]))), <<"abce">> = iolist_to_binary(join(re:split("abce","abcde{0,0}",[{parts, - 2}]))), - <<"abce">> = iolist_to_binary(join(re:split("abce","abcde{0,0}",[]))), - <<"">> = iolist_to_binary(join(re:split("abe","ab[cd]{0,0}e",[trim]))), + 2}]))), + <<"abce">> = iolist_to_binary(join(re:split("abce","abcde{0,0}",[]))), + <<"">> = iolist_to_binary(join(re:split("abe","ab[cd]{0,0}e",[trim]))), <<":">> = iolist_to_binary(join(re:split("abe","ab[cd]{0,0}e",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abe","ab[cd]{0,0}e",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab[cd]{0,0}e",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abe","ab[cd]{0,0}e",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab[cd]{0,0}e",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab[cd]{0,0}e",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab[cd]{0,0}e",[]))), - <<"abcde">> = iolist_to_binary(join(re:split("abcde","ab[cd]{0,0}e",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab[cd]{0,0}e",[]))), + <<"abcde">> = iolist_to_binary(join(re:split("abcde","ab[cd]{0,0}e",[trim]))), <<"abcde">> = iolist_to_binary(join(re:split("abcde","ab[cd]{0,0}e",[{parts, - 2}]))), - <<"abcde">> = iolist_to_binary(join(re:split("abcde","ab[cd]{0,0}e",[]))), - <<"">> = iolist_to_binary(join(re:split("abd","ab(c){0,0}d",[trim]))), + 2}]))), + <<"abcde">> = iolist_to_binary(join(re:split("abcde","ab[cd]{0,0}e",[]))), + <<"">> = iolist_to_binary(join(re:split("abd","ab(c){0,0}d",[trim]))), <<"::">> = iolist_to_binary(join(re:split("abd","ab(c){0,0}d",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("abd","ab(c){0,0}d",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab(c){0,0}d",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("abd","ab(c){0,0}d",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab(c){0,0}d",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab(c){0,0}d",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab(c){0,0}d",[]))), - <<"abcd">> = iolist_to_binary(join(re:split("abcd","ab(c){0,0}d",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab(c){0,0}d",[]))), + <<"abcd">> = iolist_to_binary(join(re:split("abcd","ab(c){0,0}d",[trim]))), <<"abcd">> = iolist_to_binary(join(re:split("abcd","ab(c){0,0}d",[{parts, - 2}]))), - <<"abcd">> = iolist_to_binary(join(re:split("abcd","ab(c){0,0}d",[]))), - <<"">> = iolist_to_binary(join(re:split("a","a(b*)",[trim]))), + 2}]))), + <<"abcd">> = iolist_to_binary(join(re:split("abcd","ab(c){0,0}d",[]))), + <<"">> = iolist_to_binary(join(re:split("a","a(b*)",[trim]))), <<"::">> = iolist_to_binary(join(re:split("a","a(b*)",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("a","a(b*)",[]))), - <<":b">> = iolist_to_binary(join(re:split("ab","a(b*)",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("a","a(b*)",[]))), + <<":b">> = iolist_to_binary(join(re:split("ab","a(b*)",[trim]))), <<":b:">> = iolist_to_binary(join(re:split("ab","a(b*)",[{parts, - 2}]))), - <<":b:">> = iolist_to_binary(join(re:split("ab","a(b*)",[]))), - <<":bbbb">> = iolist_to_binary(join(re:split("abbbb","a(b*)",[trim]))), + 2}]))), + <<":b:">> = iolist_to_binary(join(re:split("ab","a(b*)",[]))), + <<":bbbb">> = iolist_to_binary(join(re:split("abbbb","a(b*)",[trim]))), <<":bbbb:">> = iolist_to_binary(join(re:split("abbbb","a(b*)",[{parts, - 2}]))), - <<":bbbb:">> = iolist_to_binary(join(re:split("abbbb","a(b*)",[]))), - <<"*** F::ilers">> = iolist_to_binary(join(re:split("*** Failers","a(b*)",[trim]))), + 2}]))), + <<":bbbb:">> = iolist_to_binary(join(re:split("abbbb","a(b*)",[]))), + <<"*** F::ilers">> = iolist_to_binary(join(re:split("*** Failers","a(b*)",[trim]))), <<"*** F::ilers">> = iolist_to_binary(join(re:split("*** Failers","a(b*)",[{parts, - 2}]))), - <<"*** F::ilers">> = iolist_to_binary(join(re:split("*** Failers","a(b*)",[]))), - <<"bbbbb">> = iolist_to_binary(join(re:split("bbbbb","a(b*)",[trim]))), + 2}]))), + <<"*** F::ilers">> = iolist_to_binary(join(re:split("*** Failers","a(b*)",[]))), + <<"bbbbb">> = iolist_to_binary(join(re:split("bbbbb","a(b*)",[trim]))), <<"bbbbb">> = iolist_to_binary(join(re:split("bbbbb","a(b*)",[{parts, - 2}]))), - <<"bbbbb">> = iolist_to_binary(join(re:split("bbbbb","a(b*)",[]))), - <<"">> = iolist_to_binary(join(re:split("abe","ab\\d{0}e",[trim]))), + 2}]))), + <<"bbbbb">> = iolist_to_binary(join(re:split("bbbbb","a(b*)",[]))), + <<"">> = iolist_to_binary(join(re:split("abe","ab\\d{0}e",[trim]))), <<":">> = iolist_to_binary(join(re:split("abe","ab\\d{0}e",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abe","ab\\d{0}e",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab\\d{0}e",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abe","ab\\d{0}e",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab\\d{0}e",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab\\d{0}e",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab\\d{0}e",[]))), - <<"ab1e">> = iolist_to_binary(join(re:split("ab1e","ab\\d{0}e",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab\\d{0}e",[]))), + <<"ab1e">> = iolist_to_binary(join(re:split("ab1e","ab\\d{0}e",[trim]))), <<"ab1e">> = iolist_to_binary(join(re:split("ab1e","ab\\d{0}e",[{parts, - 2}]))), - <<"ab1e">> = iolist_to_binary(join(re:split("ab1e","ab\\d{0}e",[]))), - <<"the :quick: brown fox">> = iolist_to_binary(join(re:split("the \"quick\" brown fox","\"([^\\\\\"]+|\\\\.)*\"",[trim]))), + 2}]))), + <<"ab1e">> = iolist_to_binary(join(re:split("ab1e","ab\\d{0}e",[]))), + <<"the :quick: brown fox">> = iolist_to_binary(join(re:split("the \"quick\" brown fox","\"([^\\\\\"]+|\\\\.)*\"",[trim]))), <<"the :quick: brown fox">> = iolist_to_binary(join(re:split("the \"quick\" brown fox","\"([^\\\\\"]+|\\\\.)*\"",[{parts, - 2}]))), - <<"the :quick: brown fox">> = iolist_to_binary(join(re:split("the \"quick\" brown fox","\"([^\\\\\"]+|\\\\.)*\"",[]))), - <<": brown fox">> = iolist_to_binary(join(re:split("\"the \\\"quick\\\" brown fox\"","\"([^\\\\\"]+|\\\\.)*\"",[trim]))), + 2}]))), + <<"the :quick: brown fox">> = iolist_to_binary(join(re:split("the \"quick\" brown fox","\"([^\\\\\"]+|\\\\.)*\"",[]))), + <<": brown fox">> = iolist_to_binary(join(re:split("\"the \\\"quick\\\" brown fox\"","\"([^\\\\\"]+|\\\\.)*\"",[trim]))), <<": brown fox:">> = iolist_to_binary(join(re:split("\"the \\\"quick\\\" brown fox\"","\"([^\\\\\"]+|\\\\.)*\"",[{parts, - 2}]))), - <<": brown fox:">> = iolist_to_binary(join(re:split("\"the \\\"quick\\\" brown fox\"","\"([^\\\\\"]+|\\\\.)*\"",[]))), - <<"a:b:c">> = iolist_to_binary(join(re:split("abc","",[trim]))), + 2}]))), + <<": brown fox:">> = iolist_to_binary(join(re:split("\"the \\\"quick\\\" brown fox\"","\"([^\\\\\"]+|\\\\.)*\"",[]))), + <<"a:b:c">> = iolist_to_binary(join(re:split("abc","",[trim]))), <<"a:bc">> = iolist_to_binary(join(re:split("abc","",[{parts, - 2}]))), - <<"a:b:c:">> = iolist_to_binary(join(re:split("abc","",[]))), - <<"">> = iolist_to_binary(join(re:split("acb","a[^a]b",[trim]))), + 2}]))), + <<"a:b:c:">> = iolist_to_binary(join(re:split("abc","",[]))), + <<"">> = iolist_to_binary(join(re:split("acb","a[^a]b",[trim]))), <<":">> = iolist_to_binary(join(re:split("acb","a[^a]b",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("acb","a[^a]b",[]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("acb","a[^a]b",[]))), <<"">> = iolist_to_binary(join(re:split("a -b","a[^a]b",[trim]))), +b","a[^a]b",[trim]))), <<":">> = iolist_to_binary(join(re:split("a -b","a[^a]b",[{parts,2}]))), +b","a[^a]b",[{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("a -b","a[^a]b",[]))), - <<"">> = iolist_to_binary(join(re:split("acb","a.b",[trim]))), +b","a[^a]b",[]))), + <<"">> = iolist_to_binary(join(re:split("acb","a.b",[trim]))), <<":">> = iolist_to_binary(join(re:split("acb","a.b",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("acb","a.b",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a.b",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("acb","a.b",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a.b",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a.b",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a.b",[]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a.b",[]))), <<"a b">> = iolist_to_binary(join(re:split("a -b","a.b",[trim]))), +b","a.b",[trim]))), <<"a b">> = iolist_to_binary(join(re:split("a -b","a.b",[{parts,2}]))), +b","a.b",[{parts,2}]))), <<"a b">> = iolist_to_binary(join(re:split("a -b","a.b",[]))), +b","a.b",[]))), <<"">> = iolist_to_binary(join(re:split("acb","a[^a]b",[dotall, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("acb","a[^a]b",[dotall, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("acb","a[^a]b",[dotall]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("acb","a[^a]b",[dotall]))), <<"">> = iolist_to_binary(join(re:split("a -b","a[^a]b",[dotall,trim]))), +b","a[^a]b",[dotall,trim]))), <<":">> = iolist_to_binary(join(re:split("a -b","a[^a]b",[dotall,{parts,2}]))), +b","a[^a]b",[dotall,{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("a -b","a[^a]b",[dotall]))), +b","a[^a]b",[dotall]))), ok. run11() -> <<"">> = iolist_to_binary(join(re:split("acb","a.b",[dotall, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("acb","a.b",[dotall, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("acb","a.b",[dotall]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("acb","a.b",[dotall]))), <<"">> = iolist_to_binary(join(re:split("a -b","a.b",[dotall,trim]))), +b","a.b",[dotall,trim]))), <<":">> = iolist_to_binary(join(re:split("a -b","a.b",[dotall,{parts,2}]))), +b","a.b",[dotall,{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("a -b","a.b",[dotall]))), - <<":a">> = iolist_to_binary(join(re:split("bac","^(b+?|a){1,2}?c",[trim]))), +b","a.b",[dotall]))), + <<":a">> = iolist_to_binary(join(re:split("bac","^(b+?|a){1,2}?c",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("bac","^(b+?|a){1,2}?c",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("bac","^(b+?|a){1,2}?c",[]))), - <<":a">> = iolist_to_binary(join(re:split("bbac","^(b+?|a){1,2}?c",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("bac","^(b+?|a){1,2}?c",[]))), + <<":a">> = iolist_to_binary(join(re:split("bbac","^(b+?|a){1,2}?c",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("bbac","^(b+?|a){1,2}?c",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("bbac","^(b+?|a){1,2}?c",[]))), - <<":a">> = iolist_to_binary(join(re:split("bbbac","^(b+?|a){1,2}?c",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("bbac","^(b+?|a){1,2}?c",[]))), + <<":a">> = iolist_to_binary(join(re:split("bbbac","^(b+?|a){1,2}?c",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("bbbac","^(b+?|a){1,2}?c",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("bbbac","^(b+?|a){1,2}?c",[]))), - <<":a">> = iolist_to_binary(join(re:split("bbbbac","^(b+?|a){1,2}?c",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("bbbac","^(b+?|a){1,2}?c",[]))), + <<":a">> = iolist_to_binary(join(re:split("bbbbac","^(b+?|a){1,2}?c",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("bbbbac","^(b+?|a){1,2}?c",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("bbbbac","^(b+?|a){1,2}?c",[]))), - <<":a">> = iolist_to_binary(join(re:split("bbbbbac","^(b+?|a){1,2}?c",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("bbbbac","^(b+?|a){1,2}?c",[]))), + <<":a">> = iolist_to_binary(join(re:split("bbbbbac","^(b+?|a){1,2}?c",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("bbbbbac","^(b+?|a){1,2}?c",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("bbbbbac","^(b+?|a){1,2}?c",[]))), - <<":a">> = iolist_to_binary(join(re:split("bac","^(b+|a){1,2}?c",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("bbbbbac","^(b+?|a){1,2}?c",[]))), + <<":a">> = iolist_to_binary(join(re:split("bac","^(b+|a){1,2}?c",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("bac","^(b+|a){1,2}?c",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("bac","^(b+|a){1,2}?c",[]))), - <<":a">> = iolist_to_binary(join(re:split("bbac","^(b+|a){1,2}?c",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("bac","^(b+|a){1,2}?c",[]))), + <<":a">> = iolist_to_binary(join(re:split("bbac","^(b+|a){1,2}?c",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("bbac","^(b+|a){1,2}?c",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("bbac","^(b+|a){1,2}?c",[]))), - <<":a">> = iolist_to_binary(join(re:split("bbbac","^(b+|a){1,2}?c",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("bbac","^(b+|a){1,2}?c",[]))), + <<":a">> = iolist_to_binary(join(re:split("bbbac","^(b+|a){1,2}?c",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("bbbac","^(b+|a){1,2}?c",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("bbbac","^(b+|a){1,2}?c",[]))), - <<":a">> = iolist_to_binary(join(re:split("bbbbac","^(b+|a){1,2}?c",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("bbbac","^(b+|a){1,2}?c",[]))), + <<":a">> = iolist_to_binary(join(re:split("bbbbac","^(b+|a){1,2}?c",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("bbbbac","^(b+|a){1,2}?c",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("bbbbac","^(b+|a){1,2}?c",[]))), - <<":a">> = iolist_to_binary(join(re:split("bbbbbac","^(b+|a){1,2}?c",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("bbbbac","^(b+|a){1,2}?c",[]))), + <<":a">> = iolist_to_binary(join(re:split("bbbbbac","^(b+|a){1,2}?c",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("bbbbbac","^(b+|a){1,2}?c",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("bbbbbac","^(b+|a){1,2}?c",[]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("bbbbbac","^(b+|a){1,2}?c",[]))), <<"x b">> = iolist_to_binary(join(re:split("x -b","(?!\\A)x",[multiline,trim]))), +b","(?!\\A)x",[multiline,trim]))), <<"x b">> = iolist_to_binary(join(re:split("x -b","(?!\\A)x",[multiline,{parts,2}]))), +b","(?!\\A)x",[multiline,{parts,2}]))), <<"x b">> = iolist_to_binary(join(re:split("x -b","(?!\\A)x",[multiline]))), +b","(?!\\A)x",[multiline]))), <<"a">> = iolist_to_binary(join(re:split("ax","(?!\\A)x",[multiline, - trim]))), + trim]))), <<"a:">> = iolist_to_binary(join(re:split("ax","(?!\\A)x",[multiline, {parts, - 2}]))), - <<"a:">> = iolist_to_binary(join(re:split("ax","(?!\\A)x",[multiline]))), - <<"{ab}">> = iolist_to_binary(join(re:split("{ab}","\\x0{ab}",[trim]))), + 2}]))), + <<"a:">> = iolist_to_binary(join(re:split("ax","(?!\\A)x",[multiline]))), + <<"{ab}">> = iolist_to_binary(join(re:split("{ab}","\\x0{ab}",[trim]))), <<"{ab}">> = iolist_to_binary(join(re:split("{ab}","\\x0{ab}",[{parts, - 2}]))), - <<"{ab}">> = iolist_to_binary(join(re:split("{ab}","\\x0{ab}",[]))), - <<"">> = iolist_to_binary(join(re:split("CD","(A|B)*?CD",[trim]))), + 2}]))), + <<"{ab}">> = iolist_to_binary(join(re:split("{ab}","\\x0{ab}",[]))), + <<"">> = iolist_to_binary(join(re:split("CD","(A|B)*?CD",[trim]))), <<"::">> = iolist_to_binary(join(re:split("CD","(A|B)*?CD",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("CD","(A|B)*?CD",[]))), - <<"">> = iolist_to_binary(join(re:split("CD","(A|B)*CD",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("CD","(A|B)*?CD",[]))), + <<"">> = iolist_to_binary(join(re:split("CD","(A|B)*CD",[trim]))), <<"::">> = iolist_to_binary(join(re:split("CD","(A|B)*CD",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("CD","(A|B)*CD",[]))), - <<":AB:AB">> = iolist_to_binary(join(re:split("ABABAB","(AB)*?\\1",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("CD","(A|B)*CD",[]))), + <<":AB:AB">> = iolist_to_binary(join(re:split("ABABAB","(AB)*?\\1",[trim]))), <<":AB:AB">> = iolist_to_binary(join(re:split("ABABAB","(AB)*?\\1",[{parts, - 2}]))), - <<":AB:AB">> = iolist_to_binary(join(re:split("ABABAB","(AB)*?\\1",[]))), - <<":AB">> = iolist_to_binary(join(re:split("ABABAB","(AB)*\\1",[trim]))), + 2}]))), + <<":AB:AB">> = iolist_to_binary(join(re:split("ABABAB","(AB)*?\\1",[]))), + <<":AB">> = iolist_to_binary(join(re:split("ABABAB","(AB)*\\1",[trim]))), <<":AB:">> = iolist_to_binary(join(re:split("ABABAB","(AB)*\\1",[{parts, - 2}]))), - <<":AB:">> = iolist_to_binary(join(re:split("ABABAB","(AB)*\\1",[]))), - <<"">> = iolist_to_binary(join(re:split("foo","(?<!bar)foo",[trim]))), + 2}]))), + <<":AB:">> = iolist_to_binary(join(re:split("ABABAB","(AB)*\\1",[]))), + <<"">> = iolist_to_binary(join(re:split("foo","(?<!bar)foo",[trim]))), <<":">> = iolist_to_binary(join(re:split("foo","(?<!bar)foo",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("foo","(?<!bar)foo",[]))), - <<"cat:d">> = iolist_to_binary(join(re:split("catfood","(?<!bar)foo",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("foo","(?<!bar)foo",[]))), + <<"cat:d">> = iolist_to_binary(join(re:split("catfood","(?<!bar)foo",[trim]))), <<"cat:d">> = iolist_to_binary(join(re:split("catfood","(?<!bar)foo",[{parts, - 2}]))), - <<"cat:d">> = iolist_to_binary(join(re:split("catfood","(?<!bar)foo",[]))), - <<"ar:tle">> = iolist_to_binary(join(re:split("arfootle","(?<!bar)foo",[trim]))), + 2}]))), + <<"cat:d">> = iolist_to_binary(join(re:split("catfood","(?<!bar)foo",[]))), + <<"ar:tle">> = iolist_to_binary(join(re:split("arfootle","(?<!bar)foo",[trim]))), <<"ar:tle">> = iolist_to_binary(join(re:split("arfootle","(?<!bar)foo",[{parts, - 2}]))), - <<"ar:tle">> = iolist_to_binary(join(re:split("arfootle","(?<!bar)foo",[]))), - <<"r:sh">> = iolist_to_binary(join(re:split("rfoosh","(?<!bar)foo",[trim]))), + 2}]))), + <<"ar:tle">> = iolist_to_binary(join(re:split("arfootle","(?<!bar)foo",[]))), + <<"r:sh">> = iolist_to_binary(join(re:split("rfoosh","(?<!bar)foo",[trim]))), <<"r:sh">> = iolist_to_binary(join(re:split("rfoosh","(?<!bar)foo",[{parts, - 2}]))), - <<"r:sh">> = iolist_to_binary(join(re:split("rfoosh","(?<!bar)foo",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<!bar)foo",[trim]))), + 2}]))), + <<"r:sh">> = iolist_to_binary(join(re:split("rfoosh","(?<!bar)foo",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<!bar)foo",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<!bar)foo",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<!bar)foo",[]))), - <<"barfoo">> = iolist_to_binary(join(re:split("barfoo","(?<!bar)foo",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<!bar)foo",[]))), + <<"barfoo">> = iolist_to_binary(join(re:split("barfoo","(?<!bar)foo",[trim]))), <<"barfoo">> = iolist_to_binary(join(re:split("barfoo","(?<!bar)foo",[{parts, - 2}]))), - <<"barfoo">> = iolist_to_binary(join(re:split("barfoo","(?<!bar)foo",[]))), - <<"towbarfoo">> = iolist_to_binary(join(re:split("towbarfoo","(?<!bar)foo",[trim]))), + 2}]))), + <<"barfoo">> = iolist_to_binary(join(re:split("barfoo","(?<!bar)foo",[]))), + <<"towbarfoo">> = iolist_to_binary(join(re:split("towbarfoo","(?<!bar)foo",[trim]))), <<"towbarfoo">> = iolist_to_binary(join(re:split("towbarfoo","(?<!bar)foo",[{parts, - 2}]))), - <<"towbarfoo">> = iolist_to_binary(join(re:split("towbarfoo","(?<!bar)foo",[]))), - <<":d">> = iolist_to_binary(join(re:split("catfood","\\w{3}(?<!bar)foo",[trim]))), + 2}]))), + <<"towbarfoo">> = iolist_to_binary(join(re:split("towbarfoo","(?<!bar)foo",[]))), + <<":d">> = iolist_to_binary(join(re:split("catfood","\\w{3}(?<!bar)foo",[trim]))), <<":d">> = iolist_to_binary(join(re:split("catfood","\\w{3}(?<!bar)foo",[{parts, - 2}]))), - <<":d">> = iolist_to_binary(join(re:split("catfood","\\w{3}(?<!bar)foo",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\w{3}(?<!bar)foo",[trim]))), + 2}]))), + <<":d">> = iolist_to_binary(join(re:split("catfood","\\w{3}(?<!bar)foo",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\w{3}(?<!bar)foo",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\w{3}(?<!bar)foo",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\w{3}(?<!bar)foo",[]))), - <<"foo">> = iolist_to_binary(join(re:split("foo","\\w{3}(?<!bar)foo",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\w{3}(?<!bar)foo",[]))), + <<"foo">> = iolist_to_binary(join(re:split("foo","\\w{3}(?<!bar)foo",[trim]))), <<"foo">> = iolist_to_binary(join(re:split("foo","\\w{3}(?<!bar)foo",[{parts, - 2}]))), - <<"foo">> = iolist_to_binary(join(re:split("foo","\\w{3}(?<!bar)foo",[]))), - <<"barfoo">> = iolist_to_binary(join(re:split("barfoo","\\w{3}(?<!bar)foo",[trim]))), + 2}]))), + <<"foo">> = iolist_to_binary(join(re:split("foo","\\w{3}(?<!bar)foo",[]))), + <<"barfoo">> = iolist_to_binary(join(re:split("barfoo","\\w{3}(?<!bar)foo",[trim]))), <<"barfoo">> = iolist_to_binary(join(re:split("barfoo","\\w{3}(?<!bar)foo",[{parts, - 2}]))), - <<"barfoo">> = iolist_to_binary(join(re:split("barfoo","\\w{3}(?<!bar)foo",[]))), - <<"towbarfoo">> = iolist_to_binary(join(re:split("towbarfoo","\\w{3}(?<!bar)foo",[trim]))), + 2}]))), + <<"barfoo">> = iolist_to_binary(join(re:split("barfoo","\\w{3}(?<!bar)foo",[]))), + <<"towbarfoo">> = iolist_to_binary(join(re:split("towbarfoo","\\w{3}(?<!bar)foo",[trim]))), <<"towbarfoo">> = iolist_to_binary(join(re:split("towbarfoo","\\w{3}(?<!bar)foo",[{parts, - 2}]))), - <<"towbarfoo">> = iolist_to_binary(join(re:split("towbarfoo","\\w{3}(?<!bar)foo",[]))), - <<"fooa:foo">> = iolist_to_binary(join(re:split("fooabar","(?<=(foo)a)bar",[trim]))), + 2}]))), + <<"towbarfoo">> = iolist_to_binary(join(re:split("towbarfoo","\\w{3}(?<!bar)foo",[]))), + <<"fooa:foo">> = iolist_to_binary(join(re:split("fooabar","(?<=(foo)a)bar",[trim]))), <<"fooa:foo:">> = iolist_to_binary(join(re:split("fooabar","(?<=(foo)a)bar",[{parts, - 2}]))), - <<"fooa:foo:">> = iolist_to_binary(join(re:split("fooabar","(?<=(foo)a)bar",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=(foo)a)bar",[trim]))), + 2}]))), + <<"fooa:foo:">> = iolist_to_binary(join(re:split("fooabar","(?<=(foo)a)bar",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=(foo)a)bar",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=(foo)a)bar",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=(foo)a)bar",[]))), - <<"bar">> = iolist_to_binary(join(re:split("bar","(?<=(foo)a)bar",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=(foo)a)bar",[]))), + <<"bar">> = iolist_to_binary(join(re:split("bar","(?<=(foo)a)bar",[trim]))), <<"bar">> = iolist_to_binary(join(re:split("bar","(?<=(foo)a)bar",[{parts, - 2}]))), - <<"bar">> = iolist_to_binary(join(re:split("bar","(?<=(foo)a)bar",[]))), - <<"foobbar">> = iolist_to_binary(join(re:split("foobbar","(?<=(foo)a)bar",[trim]))), + 2}]))), + <<"bar">> = iolist_to_binary(join(re:split("bar","(?<=(foo)a)bar",[]))), + <<"foobbar">> = iolist_to_binary(join(re:split("foobbar","(?<=(foo)a)bar",[trim]))), <<"foobbar">> = iolist_to_binary(join(re:split("foobbar","(?<=(foo)a)bar",[{parts, - 2}]))), - <<"foobbar">> = iolist_to_binary(join(re:split("foobbar","(?<=(foo)a)bar",[]))), + 2}]))), + <<"foobbar">> = iolist_to_binary(join(re:split("foobbar","(?<=(foo)a)bar",[]))), <<"">> = iolist_to_binary(join(re:split("abc","\\Aabc\\z",[multiline, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("abc","\\Aabc\\z",[multiline, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","\\Aabc\\z",[multiline]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","\\Aabc\\z",[multiline]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\Aabc\\z",[multiline, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\Aabc\\z",[multiline, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\Aabc\\z",[multiline]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\Aabc\\z",[multiline]))), <<"">> = iolist_to_binary(join(re:split("abc","\\Aabc\\z",[multiline, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("abc","\\Aabc\\z",[multiline, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","\\Aabc\\z",[multiline]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","\\Aabc\\z",[multiline]))), <<"qqq abc">> = iolist_to_binary(join(re:split("qqq -abc","\\Aabc\\z",[multiline,trim]))), +abc","\\Aabc\\z",[multiline,trim]))), <<"qqq abc">> = iolist_to_binary(join(re:split("qqq -abc","\\Aabc\\z",[multiline,{parts,2}]))), +abc","\\Aabc\\z",[multiline,{parts,2}]))), <<"qqq abc">> = iolist_to_binary(join(re:split("qqq -abc","\\Aabc\\z",[multiline]))), +abc","\\Aabc\\z",[multiline]))), <<"abc zzz">> = iolist_to_binary(join(re:split("abc -zzz","\\Aabc\\z",[multiline,trim]))), +zzz","\\Aabc\\z",[multiline,trim]))), <<"abc zzz">> = iolist_to_binary(join(re:split("abc -zzz","\\Aabc\\z",[multiline,{parts,2}]))), +zzz","\\Aabc\\z",[multiline,{parts,2}]))), <<"abc zzz">> = iolist_to_binary(join(re:split("abc -zzz","\\Aabc\\z",[multiline]))), +zzz","\\Aabc\\z",[multiline]))), <<"qqq abc zzz">> = iolist_to_binary(join(re:split("qqq abc -zzz","\\Aabc\\z",[multiline,trim]))), +zzz","\\Aabc\\z",[multiline,trim]))), <<"qqq abc zzz">> = iolist_to_binary(join(re:split("qqq abc -zzz","\\Aabc\\z",[multiline,{parts,2}]))), +zzz","\\Aabc\\z",[multiline,{parts,2}]))), <<"qqq abc zzz">> = iolist_to_binary(join(re:split("qqq abc -zzz","\\Aabc\\z",[multiline]))), - <<"1:.23">> = iolist_to_binary(join(re:split("1.230003938","(?>(\\.\\d\\d[1-9]?))\\d+",[trim]))), +zzz","\\Aabc\\z",[multiline]))), + <<"1:.23">> = iolist_to_binary(join(re:split("1.230003938","(?>(\\.\\d\\d[1-9]?))\\d+",[trim]))), <<"1:.23:">> = iolist_to_binary(join(re:split("1.230003938","(?>(\\.\\d\\d[1-9]?))\\d+",[{parts, - 2}]))), - <<"1:.23:">> = iolist_to_binary(join(re:split("1.230003938","(?>(\\.\\d\\d[1-9]?))\\d+",[]))), - <<"1:.875">> = iolist_to_binary(join(re:split("1.875000282","(?>(\\.\\d\\d[1-9]?))\\d+",[trim]))), + 2}]))), + <<"1:.23:">> = iolist_to_binary(join(re:split("1.230003938","(?>(\\.\\d\\d[1-9]?))\\d+",[]))), + <<"1:.875">> = iolist_to_binary(join(re:split("1.875000282","(?>(\\.\\d\\d[1-9]?))\\d+",[trim]))), <<"1:.875:">> = iolist_to_binary(join(re:split("1.875000282","(?>(\\.\\d\\d[1-9]?))\\d+",[{parts, - 2}]))), - <<"1:.875:">> = iolist_to_binary(join(re:split("1.875000282","(?>(\\.\\d\\d[1-9]?))\\d+",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?>(\\.\\d\\d[1-9]?))\\d+",[trim]))), + 2}]))), + <<"1:.875:">> = iolist_to_binary(join(re:split("1.875000282","(?>(\\.\\d\\d[1-9]?))\\d+",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?>(\\.\\d\\d[1-9]?))\\d+",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?>(\\.\\d\\d[1-9]?))\\d+",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?>(\\.\\d\\d[1-9]?))\\d+",[]))), - <<"1.235">> = iolist_to_binary(join(re:split("1.235","(?>(\\.\\d\\d[1-9]?))\\d+",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?>(\\.\\d\\d[1-9]?))\\d+",[]))), + <<"1.235">> = iolist_to_binary(join(re:split("1.235","(?>(\\.\\d\\d[1-9]?))\\d+",[trim]))), <<"1.235">> = iolist_to_binary(join(re:split("1.235","(?>(\\.\\d\\d[1-9]?))\\d+",[{parts, - 2}]))), - <<"1.235">> = iolist_to_binary(join(re:split("1.235","(?>(\\.\\d\\d[1-9]?))\\d+",[]))), - <<":party">> = iolist_to_binary(join(re:split("now is the time for all good men to come to the aid of the party","^((?>\\w+)|(?>\\s+))*$",[trim]))), + 2}]))), + <<"1.235">> = iolist_to_binary(join(re:split("1.235","(?>(\\.\\d\\d[1-9]?))\\d+",[]))), + <<":party">> = iolist_to_binary(join(re:split("now is the time for all good men to come to the aid of the party","^((?>\\w+)|(?>\\s+))*$",[trim]))), <<":party:">> = iolist_to_binary(join(re:split("now is the time for all good men to come to the aid of the party","^((?>\\w+)|(?>\\s+))*$",[{parts, - 2}]))), - <<":party:">> = iolist_to_binary(join(re:split("now is the time for all good men to come to the aid of the party","^((?>\\w+)|(?>\\s+))*$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^((?>\\w+)|(?>\\s+))*$",[trim]))), + 2}]))), + <<":party:">> = iolist_to_binary(join(re:split("now is the time for all good men to come to the aid of the party","^((?>\\w+)|(?>\\s+))*$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^((?>\\w+)|(?>\\s+))*$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^((?>\\w+)|(?>\\s+))*$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^((?>\\w+)|(?>\\s+))*$",[]))), - <<"this is not a line with only words and spaces!">> = iolist_to_binary(join(re:split("this is not a line with only words and spaces!","^((?>\\w+)|(?>\\s+))*$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^((?>\\w+)|(?>\\s+))*$",[]))), + <<"this is not a line with only words and spaces!">> = iolist_to_binary(join(re:split("this is not a line with only words and spaces!","^((?>\\w+)|(?>\\s+))*$",[trim]))), <<"this is not a line with only words and spaces!">> = iolist_to_binary(join(re:split("this is not a line with only words and spaces!","^((?>\\w+)|(?>\\s+))*$",[{parts, - 2}]))), - <<"this is not a line with only words and spaces!">> = iolist_to_binary(join(re:split("this is not a line with only words and spaces!","^((?>\\w+)|(?>\\s+))*$",[]))), - <<":12345:a">> = iolist_to_binary(join(re:split("12345a","(\\d+)(\\w)",[trim]))), + 2}]))), + <<"this is not a line with only words and spaces!">> = iolist_to_binary(join(re:split("this is not a line with only words and spaces!","^((?>\\w+)|(?>\\s+))*$",[]))), + <<":12345:a">> = iolist_to_binary(join(re:split("12345a","(\\d+)(\\w)",[trim]))), <<":12345:a:">> = iolist_to_binary(join(re:split("12345a","(\\d+)(\\w)",[{parts, - 2}]))), - <<":12345:a:">> = iolist_to_binary(join(re:split("12345a","(\\d+)(\\w)",[]))), - <<":1234:5:+">> = iolist_to_binary(join(re:split("12345+","(\\d+)(\\w)",[trim]))), + 2}]))), + <<":12345:a:">> = iolist_to_binary(join(re:split("12345a","(\\d+)(\\w)",[]))), + <<":1234:5:+">> = iolist_to_binary(join(re:split("12345+","(\\d+)(\\w)",[trim]))), <<":1234:5:+">> = iolist_to_binary(join(re:split("12345+","(\\d+)(\\w)",[{parts, - 2}]))), - <<":1234:5:+">> = iolist_to_binary(join(re:split("12345+","(\\d+)(\\w)",[]))), - <<":12345:a">> = iolist_to_binary(join(re:split("12345a","((?>\\d+))(\\w)",[trim]))), + 2}]))), + <<":1234:5:+">> = iolist_to_binary(join(re:split("12345+","(\\d+)(\\w)",[]))), + <<":12345:a">> = iolist_to_binary(join(re:split("12345a","((?>\\d+))(\\w)",[trim]))), <<":12345:a:">> = iolist_to_binary(join(re:split("12345a","((?>\\d+))(\\w)",[{parts, - 2}]))), - <<":12345:a:">> = iolist_to_binary(join(re:split("12345a","((?>\\d+))(\\w)",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","((?>\\d+))(\\w)",[trim]))), + 2}]))), + <<":12345:a:">> = iolist_to_binary(join(re:split("12345a","((?>\\d+))(\\w)",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","((?>\\d+))(\\w)",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","((?>\\d+))(\\w)",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","((?>\\d+))(\\w)",[]))), - <<"12345+">> = iolist_to_binary(join(re:split("12345+","((?>\\d+))(\\w)",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","((?>\\d+))(\\w)",[]))), + <<"12345+">> = iolist_to_binary(join(re:split("12345+","((?>\\d+))(\\w)",[trim]))), <<"12345+">> = iolist_to_binary(join(re:split("12345+","((?>\\d+))(\\w)",[{parts, - 2}]))), - <<"12345+">> = iolist_to_binary(join(re:split("12345+","((?>\\d+))(\\w)",[]))), - <<"">> = iolist_to_binary(join(re:split("aaab","(?>a+)b",[trim]))), + 2}]))), + <<"12345+">> = iolist_to_binary(join(re:split("12345+","((?>\\d+))(\\w)",[]))), + <<"">> = iolist_to_binary(join(re:split("aaab","(?>a+)b",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaab","(?>a+)b",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaab","(?>a+)b",[]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaab","(?>a+)b",[]))), ok. run12() -> - <<":aaab">> = iolist_to_binary(join(re:split("aaab","((?>a+)b)",[trim]))), + <<":aaab">> = iolist_to_binary(join(re:split("aaab","((?>a+)b)",[trim]))), <<":aaab:">> = iolist_to_binary(join(re:split("aaab","((?>a+)b)",[{parts, - 2}]))), - <<":aaab:">> = iolist_to_binary(join(re:split("aaab","((?>a+)b)",[]))), - <<":aaa">> = iolist_to_binary(join(re:split("aaab","(?>(a+))b",[trim]))), + 2}]))), + <<":aaab:">> = iolist_to_binary(join(re:split("aaab","((?>a+)b)",[]))), + <<":aaa">> = iolist_to_binary(join(re:split("aaab","(?>(a+))b",[trim]))), <<":aaa:">> = iolist_to_binary(join(re:split("aaab","(?>(a+))b",[{parts, - 2}]))), - <<":aaa:">> = iolist_to_binary(join(re:split("aaab","(?>(a+))b",[]))), - <<"aaa:ccc">> = iolist_to_binary(join(re:split("aaabbbccc","(?>b)+",[trim]))), + 2}]))), + <<":aaa:">> = iolist_to_binary(join(re:split("aaab","(?>(a+))b",[]))), + <<"aaa:ccc">> = iolist_to_binary(join(re:split("aaabbbccc","(?>b)+",[trim]))), <<"aaa:ccc">> = iolist_to_binary(join(re:split("aaabbbccc","(?>b)+",[{parts, - 2}]))), - <<"aaa:ccc">> = iolist_to_binary(join(re:split("aaabbbccc","(?>b)+",[]))), - <<"::::d">> = iolist_to_binary(join(re:split("aaabbbbccccd","(?>a+|b+|c+)*c",[trim]))), + 2}]))), + <<"aaa:ccc">> = iolist_to_binary(join(re:split("aaabbbccc","(?>b)+",[]))), + <<"::::d">> = iolist_to_binary(join(re:split("aaabbbbccccd","(?>a+|b+|c+)*c",[trim]))), <<":cccd">> = iolist_to_binary(join(re:split("aaabbbbccccd","(?>a+|b+|c+)*c",[{parts, - 2}]))), - <<"::::d">> = iolist_to_binary(join(re:split("aaabbbbccccd","(?>a+|b+|c+)*c",[]))), - <<"((:x">> = iolist_to_binary(join(re:split("((abc(ade)ufh()()x","((?>[^()]+)|\\([^()]*\\))+",[trim]))), + 2}]))), + <<"::::d">> = iolist_to_binary(join(re:split("aaabbbbccccd","(?>a+|b+|c+)*c",[]))), + <<"((:x">> = iolist_to_binary(join(re:split("((abc(ade)ufh()()x","((?>[^()]+)|\\([^()]*\\))+",[trim]))), <<"((:x:">> = iolist_to_binary(join(re:split("((abc(ade)ufh()()x","((?>[^()]+)|\\([^()]*\\))+",[{parts, - 2}]))), - <<"((:x:">> = iolist_to_binary(join(re:split("((abc(ade)ufh()()x","((?>[^()]+)|\\([^()]*\\))+",[]))), - <<":abc">> = iolist_to_binary(join(re:split("(abc)","\\(((?>[^()]+)|\\([^()]+\\))+\\)",[trim]))), + 2}]))), + <<"((:x:">> = iolist_to_binary(join(re:split("((abc(ade)ufh()()x","((?>[^()]+)|\\([^()]*\\))+",[]))), + <<":abc">> = iolist_to_binary(join(re:split("(abc)","\\(((?>[^()]+)|\\([^()]+\\))+\\)",[trim]))), <<":abc:">> = iolist_to_binary(join(re:split("(abc)","\\(((?>[^()]+)|\\([^()]+\\))+\\)",[{parts, - 2}]))), - <<":abc:">> = iolist_to_binary(join(re:split("(abc)","\\(((?>[^()]+)|\\([^()]+\\))+\\)",[]))), - <<":xyz">> = iolist_to_binary(join(re:split("(abc(def)xyz)","\\(((?>[^()]+)|\\([^()]+\\))+\\)",[trim]))), + 2}]))), + <<":abc:">> = iolist_to_binary(join(re:split("(abc)","\\(((?>[^()]+)|\\([^()]+\\))+\\)",[]))), + <<":xyz">> = iolist_to_binary(join(re:split("(abc(def)xyz)","\\(((?>[^()]+)|\\([^()]+\\))+\\)",[trim]))), <<":xyz:">> = iolist_to_binary(join(re:split("(abc(def)xyz)","\\(((?>[^()]+)|\\([^()]+\\))+\\)",[{parts, - 2}]))), - <<":xyz:">> = iolist_to_binary(join(re:split("(abc(def)xyz)","\\(((?>[^()]+)|\\([^()]+\\))+\\)",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\(((?>[^()]+)|\\([^()]+\\))+\\)",[trim]))), + 2}]))), + <<":xyz:">> = iolist_to_binary(join(re:split("(abc(def)xyz)","\\(((?>[^()]+)|\\([^()]+\\))+\\)",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\(((?>[^()]+)|\\([^()]+\\))+\\)",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\(((?>[^()]+)|\\([^()]+\\))+\\)",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\(((?>[^()]+)|\\([^()]+\\))+\\)",[]))), - <<"((()aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("((()aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","\\(((?>[^()]+)|\\([^()]+\\))+\\)",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\(((?>[^()]+)|\\([^()]+\\))+\\)",[]))), + <<"((()aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("((()aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","\\(((?>[^()]+)|\\([^()]+\\))+\\)",[trim]))), <<"((()aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("((()aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","\\(((?>[^()]+)|\\([^()]+\\))+\\)",[{parts, - 2}]))), - <<"((()aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("((()aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","\\(((?>[^()]+)|\\([^()]+\\))+\\)",[]))), + 2}]))), + <<"((()aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("((()aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","\\(((?>[^()]+)|\\([^()]+\\))+\\)",[]))), <<"">> = iolist_to_binary(join(re:split("ab","a(?-i)b",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ab","a(?-i)b",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ab","a(?-i)b",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ab","a(?-i)b",[caseless]))), <<"">> = iolist_to_binary(join(re:split("Ab","a(?-i)b",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("Ab","a(?-i)b",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("Ab","a(?-i)b",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("Ab","a(?-i)b",[caseless]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?-i)b",[caseless, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?-i)b",[caseless, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?-i)b",[caseless]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?-i)b",[caseless]))), <<"aB">> = iolist_to_binary(join(re:split("aB","a(?-i)b",[caseless, - trim]))), + trim]))), <<"aB">> = iolist_to_binary(join(re:split("aB","a(?-i)b",[caseless, {parts, - 2}]))), - <<"aB">> = iolist_to_binary(join(re:split("aB","a(?-i)b",[caseless]))), + 2}]))), + <<"aB">> = iolist_to_binary(join(re:split("aB","a(?-i)b",[caseless]))), <<"AB">> = iolist_to_binary(join(re:split("AB","a(?-i)b",[caseless, - trim]))), + trim]))), <<"AB">> = iolist_to_binary(join(re:split("AB","a(?-i)b",[caseless, {parts, - 2}]))), - <<"AB">> = iolist_to_binary(join(re:split("AB","a(?-i)b",[caseless]))), - <<":a bc">> = iolist_to_binary(join(re:split("a bcd e","(a (?x)b c)d e",[trim]))), + 2}]))), + <<"AB">> = iolist_to_binary(join(re:split("AB","a(?-i)b",[caseless]))), + <<":a bc">> = iolist_to_binary(join(re:split("a bcd e","(a (?x)b c)d e",[trim]))), <<":a bc:">> = iolist_to_binary(join(re:split("a bcd e","(a (?x)b c)d e",[{parts, - 2}]))), - <<":a bc:">> = iolist_to_binary(join(re:split("a bcd e","(a (?x)b c)d e",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a (?x)b c)d e",[trim]))), + 2}]))), + <<":a bc:">> = iolist_to_binary(join(re:split("a bcd e","(a (?x)b c)d e",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a (?x)b c)d e",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a (?x)b c)d e",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a (?x)b c)d e",[]))), - <<"a b cd e">> = iolist_to_binary(join(re:split("a b cd e","(a (?x)b c)d e",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a (?x)b c)d e",[]))), + <<"a b cd e">> = iolist_to_binary(join(re:split("a b cd e","(a (?x)b c)d e",[trim]))), <<"a b cd e">> = iolist_to_binary(join(re:split("a b cd e","(a (?x)b c)d e",[{parts, - 2}]))), - <<"a b cd e">> = iolist_to_binary(join(re:split("a b cd e","(a (?x)b c)d e",[]))), - <<"abcd e">> = iolist_to_binary(join(re:split("abcd e","(a (?x)b c)d e",[trim]))), + 2}]))), + <<"a b cd e">> = iolist_to_binary(join(re:split("a b cd e","(a (?x)b c)d e",[]))), + <<"abcd e">> = iolist_to_binary(join(re:split("abcd e","(a (?x)b c)d e",[trim]))), <<"abcd e">> = iolist_to_binary(join(re:split("abcd e","(a (?x)b c)d e",[{parts, - 2}]))), - <<"abcd e">> = iolist_to_binary(join(re:split("abcd e","(a (?x)b c)d e",[]))), - <<"a bcde">> = iolist_to_binary(join(re:split("a bcde","(a (?x)b c)d e",[trim]))), + 2}]))), + <<"abcd e">> = iolist_to_binary(join(re:split("abcd e","(a (?x)b c)d e",[]))), + <<"a bcde">> = iolist_to_binary(join(re:split("a bcde","(a (?x)b c)d e",[trim]))), <<"a bcde">> = iolist_to_binary(join(re:split("a bcde","(a (?x)b c)d e",[{parts, - 2}]))), - <<"a bcde">> = iolist_to_binary(join(re:split("a bcde","(a (?x)b c)d e",[]))), - <<":a bcde f">> = iolist_to_binary(join(re:split("a bcde f","(a b(?x)c d (?-x)e f)",[trim]))), + 2}]))), + <<"a bcde">> = iolist_to_binary(join(re:split("a bcde","(a (?x)b c)d e",[]))), + <<":a bcde f">> = iolist_to_binary(join(re:split("a bcde f","(a b(?x)c d (?-x)e f)",[trim]))), <<":a bcde f:">> = iolist_to_binary(join(re:split("a bcde f","(a b(?x)c d (?-x)e f)",[{parts, - 2}]))), - <<":a bcde f:">> = iolist_to_binary(join(re:split("a bcde f","(a b(?x)c d (?-x)e f)",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a b(?x)c d (?-x)e f)",[trim]))), + 2}]))), + <<":a bcde f:">> = iolist_to_binary(join(re:split("a bcde f","(a b(?x)c d (?-x)e f)",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a b(?x)c d (?-x)e f)",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a b(?x)c d (?-x)e f)",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a b(?x)c d (?-x)e f)",[]))), - <<"abcdef">> = iolist_to_binary(join(re:split("abcdef","(a b(?x)c d (?-x)e f)",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a b(?x)c d (?-x)e f)",[]))), + <<"abcdef">> = iolist_to_binary(join(re:split("abcdef","(a b(?x)c d (?-x)e f)",[trim]))), <<"abcdef">> = iolist_to_binary(join(re:split("abcdef","(a b(?x)c d (?-x)e f)",[{parts, - 2}]))), - <<"abcdef">> = iolist_to_binary(join(re:split("abcdef","(a b(?x)c d (?-x)e f)",[]))), - <<":ab">> = iolist_to_binary(join(re:split("abc","(a(?i)b)c",[trim]))), + 2}]))), + <<"abcdef">> = iolist_to_binary(join(re:split("abcdef","(a b(?x)c d (?-x)e f)",[]))), + <<":ab">> = iolist_to_binary(join(re:split("abc","(a(?i)b)c",[trim]))), <<":ab:">> = iolist_to_binary(join(re:split("abc","(a(?i)b)c",[{parts, - 2}]))), - <<":ab:">> = iolist_to_binary(join(re:split("abc","(a(?i)b)c",[]))), - <<":aB">> = iolist_to_binary(join(re:split("aBc","(a(?i)b)c",[trim]))), + 2}]))), + <<":ab:">> = iolist_to_binary(join(re:split("abc","(a(?i)b)c",[]))), + <<":aB">> = iolist_to_binary(join(re:split("aBc","(a(?i)b)c",[trim]))), <<":aB:">> = iolist_to_binary(join(re:split("aBc","(a(?i)b)c",[{parts, - 2}]))), - <<":aB:">> = iolist_to_binary(join(re:split("aBc","(a(?i)b)c",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a(?i)b)c",[trim]))), + 2}]))), + <<":aB:">> = iolist_to_binary(join(re:split("aBc","(a(?i)b)c",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a(?i)b)c",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a(?i)b)c",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a(?i)b)c",[]))), - <<"abC">> = iolist_to_binary(join(re:split("abC","(a(?i)b)c",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a(?i)b)c",[]))), + <<"abC">> = iolist_to_binary(join(re:split("abC","(a(?i)b)c",[trim]))), <<"abC">> = iolist_to_binary(join(re:split("abC","(a(?i)b)c",[{parts, - 2}]))), - <<"abC">> = iolist_to_binary(join(re:split("abC","(a(?i)b)c",[]))), - <<"aBC">> = iolist_to_binary(join(re:split("aBC","(a(?i)b)c",[trim]))), + 2}]))), + <<"abC">> = iolist_to_binary(join(re:split("abC","(a(?i)b)c",[]))), + <<"aBC">> = iolist_to_binary(join(re:split("aBC","(a(?i)b)c",[trim]))), <<"aBC">> = iolist_to_binary(join(re:split("aBC","(a(?i)b)c",[{parts, - 2}]))), - <<"aBC">> = iolist_to_binary(join(re:split("aBC","(a(?i)b)c",[]))), - <<"Abc">> = iolist_to_binary(join(re:split("Abc","(a(?i)b)c",[trim]))), + 2}]))), + <<"aBC">> = iolist_to_binary(join(re:split("aBC","(a(?i)b)c",[]))), + <<"Abc">> = iolist_to_binary(join(re:split("Abc","(a(?i)b)c",[trim]))), <<"Abc">> = iolist_to_binary(join(re:split("Abc","(a(?i)b)c",[{parts, - 2}]))), - <<"Abc">> = iolist_to_binary(join(re:split("Abc","(a(?i)b)c",[]))), - <<"ABc">> = iolist_to_binary(join(re:split("ABc","(a(?i)b)c",[trim]))), + 2}]))), + <<"Abc">> = iolist_to_binary(join(re:split("Abc","(a(?i)b)c",[]))), + <<"ABc">> = iolist_to_binary(join(re:split("ABc","(a(?i)b)c",[trim]))), <<"ABc">> = iolist_to_binary(join(re:split("ABc","(a(?i)b)c",[{parts, - 2}]))), - <<"ABc">> = iolist_to_binary(join(re:split("ABc","(a(?i)b)c",[]))), - <<"ABC">> = iolist_to_binary(join(re:split("ABC","(a(?i)b)c",[trim]))), + 2}]))), + <<"ABc">> = iolist_to_binary(join(re:split("ABc","(a(?i)b)c",[]))), + <<"ABC">> = iolist_to_binary(join(re:split("ABC","(a(?i)b)c",[trim]))), <<"ABC">> = iolist_to_binary(join(re:split("ABC","(a(?i)b)c",[{parts, - 2}]))), - <<"ABC">> = iolist_to_binary(join(re:split("ABC","(a(?i)b)c",[]))), - <<"AbC">> = iolist_to_binary(join(re:split("AbC","(a(?i)b)c",[trim]))), + 2}]))), + <<"ABC">> = iolist_to_binary(join(re:split("ABC","(a(?i)b)c",[]))), + <<"AbC">> = iolist_to_binary(join(re:split("AbC","(a(?i)b)c",[trim]))), <<"AbC">> = iolist_to_binary(join(re:split("AbC","(a(?i)b)c",[{parts, - 2}]))), - <<"AbC">> = iolist_to_binary(join(re:split("AbC","(a(?i)b)c",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","a(?i:b)c",[trim]))), + 2}]))), + <<"AbC">> = iolist_to_binary(join(re:split("AbC","(a(?i)b)c",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","a(?i:b)c",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","a(?i:b)c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","a(?i:b)c",[]))), - <<"">> = iolist_to_binary(join(re:split("aBc","a(?i:b)c",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","a(?i:b)c",[]))), + <<"">> = iolist_to_binary(join(re:split("aBc","a(?i:b)c",[trim]))), <<":">> = iolist_to_binary(join(re:split("aBc","a(?i:b)c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aBc","a(?i:b)c",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?i:b)c",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aBc","a(?i:b)c",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?i:b)c",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?i:b)c",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?i:b)c",[]))), - <<"ABC">> = iolist_to_binary(join(re:split("ABC","a(?i:b)c",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?i:b)c",[]))), + <<"ABC">> = iolist_to_binary(join(re:split("ABC","a(?i:b)c",[trim]))), <<"ABC">> = iolist_to_binary(join(re:split("ABC","a(?i:b)c",[{parts, - 2}]))), - <<"ABC">> = iolist_to_binary(join(re:split("ABC","a(?i:b)c",[]))), - <<"abC">> = iolist_to_binary(join(re:split("abC","a(?i:b)c",[trim]))), + 2}]))), + <<"ABC">> = iolist_to_binary(join(re:split("ABC","a(?i:b)c",[]))), + <<"abC">> = iolist_to_binary(join(re:split("abC","a(?i:b)c",[trim]))), <<"abC">> = iolist_to_binary(join(re:split("abC","a(?i:b)c",[{parts, - 2}]))), - <<"abC">> = iolist_to_binary(join(re:split("abC","a(?i:b)c",[]))), - <<"aBC">> = iolist_to_binary(join(re:split("aBC","a(?i:b)c",[trim]))), + 2}]))), + <<"abC">> = iolist_to_binary(join(re:split("abC","a(?i:b)c",[]))), + <<"aBC">> = iolist_to_binary(join(re:split("aBC","a(?i:b)c",[trim]))), <<"aBC">> = iolist_to_binary(join(re:split("aBC","a(?i:b)c",[{parts, - 2}]))), - <<"aBC">> = iolist_to_binary(join(re:split("aBC","a(?i:b)c",[]))), - <<"">> = iolist_to_binary(join(re:split("aBc","a(?i:b)*c",[trim]))), + 2}]))), + <<"aBC">> = iolist_to_binary(join(re:split("aBC","a(?i:b)c",[]))), + <<"">> = iolist_to_binary(join(re:split("aBc","a(?i:b)*c",[trim]))), <<":">> = iolist_to_binary(join(re:split("aBc","a(?i:b)*c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aBc","a(?i:b)*c",[]))), - <<"">> = iolist_to_binary(join(re:split("aBBc","a(?i:b)*c",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aBc","a(?i:b)*c",[]))), + <<"">> = iolist_to_binary(join(re:split("aBBc","a(?i:b)*c",[trim]))), <<":">> = iolist_to_binary(join(re:split("aBBc","a(?i:b)*c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aBBc","a(?i:b)*c",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?i:b)*c",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aBBc","a(?i:b)*c",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?i:b)*c",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?i:b)*c",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?i:b)*c",[]))), - <<"aBC">> = iolist_to_binary(join(re:split("aBC","a(?i:b)*c",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?i:b)*c",[]))), + <<"aBC">> = iolist_to_binary(join(re:split("aBC","a(?i:b)*c",[trim]))), <<"aBC">> = iolist_to_binary(join(re:split("aBC","a(?i:b)*c",[{parts, - 2}]))), - <<"aBC">> = iolist_to_binary(join(re:split("aBC","a(?i:b)*c",[]))), - <<"aBBC">> = iolist_to_binary(join(re:split("aBBC","a(?i:b)*c",[trim]))), + 2}]))), + <<"aBC">> = iolist_to_binary(join(re:split("aBC","a(?i:b)*c",[]))), + <<"aBBC">> = iolist_to_binary(join(re:split("aBBC","a(?i:b)*c",[trim]))), <<"aBBC">> = iolist_to_binary(join(re:split("aBBC","a(?i:b)*c",[{parts, - 2}]))), - <<"aBBC">> = iolist_to_binary(join(re:split("aBBC","a(?i:b)*c",[]))), - <<"">> = iolist_to_binary(join(re:split("abcd","a(?=b(?i)c)\\w\\wd",[trim]))), + 2}]))), + <<"aBBC">> = iolist_to_binary(join(re:split("aBBC","a(?i:b)*c",[]))), + <<"">> = iolist_to_binary(join(re:split("abcd","a(?=b(?i)c)\\w\\wd",[trim]))), <<":">> = iolist_to_binary(join(re:split("abcd","a(?=b(?i)c)\\w\\wd",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abcd","a(?=b(?i)c)\\w\\wd",[]))), - <<"">> = iolist_to_binary(join(re:split("abCd","a(?=b(?i)c)\\w\\wd",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abcd","a(?=b(?i)c)\\w\\wd",[]))), + <<"">> = iolist_to_binary(join(re:split("abCd","a(?=b(?i)c)\\w\\wd",[trim]))), <<":">> = iolist_to_binary(join(re:split("abCd","a(?=b(?i)c)\\w\\wd",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abCd","a(?=b(?i)c)\\w\\wd",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?=b(?i)c)\\w\\wd",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abCd","a(?=b(?i)c)\\w\\wd",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?=b(?i)c)\\w\\wd",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?=b(?i)c)\\w\\wd",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?=b(?i)c)\\w\\wd",[]))), - <<"aBCd">> = iolist_to_binary(join(re:split("aBCd","a(?=b(?i)c)\\w\\wd",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?=b(?i)c)\\w\\wd",[]))), + <<"aBCd">> = iolist_to_binary(join(re:split("aBCd","a(?=b(?i)c)\\w\\wd",[trim]))), <<"aBCd">> = iolist_to_binary(join(re:split("aBCd","a(?=b(?i)c)\\w\\wd",[{parts, - 2}]))), - <<"aBCd">> = iolist_to_binary(join(re:split("aBCd","a(?=b(?i)c)\\w\\wd",[]))), - <<"abcD">> = iolist_to_binary(join(re:split("abcD","a(?=b(?i)c)\\w\\wd",[trim]))), + 2}]))), + <<"aBCd">> = iolist_to_binary(join(re:split("aBCd","a(?=b(?i)c)\\w\\wd",[]))), + <<"abcD">> = iolist_to_binary(join(re:split("abcD","a(?=b(?i)c)\\w\\wd",[trim]))), <<"abcD">> = iolist_to_binary(join(re:split("abcD","a(?=b(?i)c)\\w\\wd",[{parts, - 2}]))), - <<"abcD">> = iolist_to_binary(join(re:split("abcD","a(?=b(?i)c)\\w\\wd",[]))), + 2}]))), + <<"abcD">> = iolist_to_binary(join(re:split("abcD","a(?=b(?i)c)\\w\\wd",[]))), <<"">> = iolist_to_binary(join(re:split("more than million","(?s-i:more.*than).*million",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("more than million","(?s-i:more.*than).*million",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("more than million","(?s-i:more.*than).*million",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("more than million","(?s-i:more.*than).*million",[caseless]))), <<"">> = iolist_to_binary(join(re:split("more than MILLION","(?s-i:more.*than).*million",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("more than MILLION","(?s-i:more.*than).*million",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("more than MILLION","(?s-i:more.*than).*million",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("more than MILLION","(?s-i:more.*than).*million",[caseless]))), <<"">> = iolist_to_binary(join(re:split("more - than Million","(?s-i:more.*than).*million",[caseless,trim]))), + than Million","(?s-i:more.*than).*million",[caseless,trim]))), <<":">> = iolist_to_binary(join(re:split("more - than Million","(?s-i:more.*than).*million",[caseless,{parts,2}]))), + than Million","(?s-i:more.*than).*million",[caseless,{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("more - than Million","(?s-i:more.*than).*million",[caseless]))), + than Million","(?s-i:more.*than).*million",[caseless]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?s-i:more.*than).*million",[caseless, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?s-i:more.*than).*million",[caseless, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?s-i:more.*than).*million",[caseless]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?s-i:more.*than).*million",[caseless]))), <<"MORE THAN MILLION">> = iolist_to_binary(join(re:split("MORE THAN MILLION","(?s-i:more.*than).*million",[caseless, - trim]))), + trim]))), <<"MORE THAN MILLION">> = iolist_to_binary(join(re:split("MORE THAN MILLION","(?s-i:more.*than).*million",[caseless, {parts, - 2}]))), - <<"MORE THAN MILLION">> = iolist_to_binary(join(re:split("MORE THAN MILLION","(?s-i:more.*than).*million",[caseless]))), + 2}]))), + <<"MORE THAN MILLION">> = iolist_to_binary(join(re:split("MORE THAN MILLION","(?s-i:more.*than).*million",[caseless]))), <<"more than million">> = iolist_to_binary(join(re:split("more than - million","(?s-i:more.*than).*million",[caseless,trim]))), + million","(?s-i:more.*than).*million",[caseless,trim]))), <<"more than million">> = iolist_to_binary(join(re:split("more than - million","(?s-i:more.*than).*million",[caseless,{parts,2}]))), + million","(?s-i:more.*than).*million",[caseless,{parts,2}]))), <<"more than million">> = iolist_to_binary(join(re:split("more than - million","(?s-i:more.*than).*million",[caseless]))), + million","(?s-i:more.*than).*million",[caseless]))), <<"">> = iolist_to_binary(join(re:split("more than million","(?:(?s-i)more.*than).*million",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("more than million","(?:(?s-i)more.*than).*million",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("more than million","(?:(?s-i)more.*than).*million",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("more than million","(?:(?s-i)more.*than).*million",[caseless]))), <<"">> = iolist_to_binary(join(re:split("more than MILLION","(?:(?s-i)more.*than).*million",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("more than MILLION","(?:(?s-i)more.*than).*million",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("more than MILLION","(?:(?s-i)more.*than).*million",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("more than MILLION","(?:(?s-i)more.*than).*million",[caseless]))), <<"">> = iolist_to_binary(join(re:split("more - than Million","(?:(?s-i)more.*than).*million",[caseless,trim]))), + than Million","(?:(?s-i)more.*than).*million",[caseless,trim]))), <<":">> = iolist_to_binary(join(re:split("more - than Million","(?:(?s-i)more.*than).*million",[caseless,{parts,2}]))), + than Million","(?:(?s-i)more.*than).*million",[caseless,{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("more - than Million","(?:(?s-i)more.*than).*million",[caseless]))), + than Million","(?:(?s-i)more.*than).*million",[caseless]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?:(?s-i)more.*than).*million",[caseless, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?:(?s-i)more.*than).*million",[caseless, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?:(?s-i)more.*than).*million",[caseless]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?:(?s-i)more.*than).*million",[caseless]))), <<"MORE THAN MILLION">> = iolist_to_binary(join(re:split("MORE THAN MILLION","(?:(?s-i)more.*than).*million",[caseless, - trim]))), + trim]))), <<"MORE THAN MILLION">> = iolist_to_binary(join(re:split("MORE THAN MILLION","(?:(?s-i)more.*than).*million",[caseless, {parts, - 2}]))), - <<"MORE THAN MILLION">> = iolist_to_binary(join(re:split("MORE THAN MILLION","(?:(?s-i)more.*than).*million",[caseless]))), + 2}]))), + <<"MORE THAN MILLION">> = iolist_to_binary(join(re:split("MORE THAN MILLION","(?:(?s-i)more.*than).*million",[caseless]))), <<"more than million">> = iolist_to_binary(join(re:split("more than - million","(?:(?s-i)more.*than).*million",[caseless,trim]))), + million","(?:(?s-i)more.*than).*million",[caseless,trim]))), <<"more than million">> = iolist_to_binary(join(re:split("more than - million","(?:(?s-i)more.*than).*million",[caseless,{parts,2}]))), + million","(?:(?s-i)more.*than).*million",[caseless,{parts,2}]))), <<"more than million">> = iolist_to_binary(join(re:split("more than - million","(?:(?s-i)more.*than).*million",[caseless]))), - <<"">> = iolist_to_binary(join(re:split("abc","(?>a(?i)b+)+c",[trim]))), + million","(?:(?s-i)more.*than).*million",[caseless]))), + <<"">> = iolist_to_binary(join(re:split("abc","(?>a(?i)b+)+c",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","(?>a(?i)b+)+c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","(?>a(?i)b+)+c",[]))), - <<"">> = iolist_to_binary(join(re:split("aBbc","(?>a(?i)b+)+c",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","(?>a(?i)b+)+c",[]))), + <<"">> = iolist_to_binary(join(re:split("aBbc","(?>a(?i)b+)+c",[trim]))), <<":">> = iolist_to_binary(join(re:split("aBbc","(?>a(?i)b+)+c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aBbc","(?>a(?i)b+)+c",[]))), - <<"">> = iolist_to_binary(join(re:split("aBBc","(?>a(?i)b+)+c",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aBbc","(?>a(?i)b+)+c",[]))), + <<"">> = iolist_to_binary(join(re:split("aBBc","(?>a(?i)b+)+c",[trim]))), <<":">> = iolist_to_binary(join(re:split("aBBc","(?>a(?i)b+)+c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aBBc","(?>a(?i)b+)+c",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?>a(?i)b+)+c",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aBBc","(?>a(?i)b+)+c",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?>a(?i)b+)+c",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?>a(?i)b+)+c",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?>a(?i)b+)+c",[]))), - <<"Abc">> = iolist_to_binary(join(re:split("Abc","(?>a(?i)b+)+c",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?>a(?i)b+)+c",[]))), + <<"Abc">> = iolist_to_binary(join(re:split("Abc","(?>a(?i)b+)+c",[trim]))), <<"Abc">> = iolist_to_binary(join(re:split("Abc","(?>a(?i)b+)+c",[{parts, - 2}]))), - <<"Abc">> = iolist_to_binary(join(re:split("Abc","(?>a(?i)b+)+c",[]))), - <<"abAb">> = iolist_to_binary(join(re:split("abAb","(?>a(?i)b+)+c",[trim]))), + 2}]))), + <<"Abc">> = iolist_to_binary(join(re:split("Abc","(?>a(?i)b+)+c",[]))), + <<"abAb">> = iolist_to_binary(join(re:split("abAb","(?>a(?i)b+)+c",[trim]))), <<"abAb">> = iolist_to_binary(join(re:split("abAb","(?>a(?i)b+)+c",[{parts, - 2}]))), - <<"abAb">> = iolist_to_binary(join(re:split("abAb","(?>a(?i)b+)+c",[]))), - <<"abbC">> = iolist_to_binary(join(re:split("abbC","(?>a(?i)b+)+c",[trim]))), + 2}]))), + <<"abAb">> = iolist_to_binary(join(re:split("abAb","(?>a(?i)b+)+c",[]))), + <<"abbC">> = iolist_to_binary(join(re:split("abbC","(?>a(?i)b+)+c",[trim]))), <<"abbC">> = iolist_to_binary(join(re:split("abbC","(?>a(?i)b+)+c",[{parts, - 2}]))), - <<"abbC">> = iolist_to_binary(join(re:split("abbC","(?>a(?i)b+)+c",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","(?=a(?i)b)\\w\\wc",[trim]))), + 2}]))), + <<"abbC">> = iolist_to_binary(join(re:split("abbC","(?>a(?i)b+)+c",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","(?=a(?i)b)\\w\\wc",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","(?=a(?i)b)\\w\\wc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","(?=a(?i)b)\\w\\wc",[]))), - <<"">> = iolist_to_binary(join(re:split("aBc","(?=a(?i)b)\\w\\wc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","(?=a(?i)b)\\w\\wc",[]))), + <<"">> = iolist_to_binary(join(re:split("aBc","(?=a(?i)b)\\w\\wc",[trim]))), <<":">> = iolist_to_binary(join(re:split("aBc","(?=a(?i)b)\\w\\wc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aBc","(?=a(?i)b)\\w\\wc",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?=a(?i)b)\\w\\wc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aBc","(?=a(?i)b)\\w\\wc",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?=a(?i)b)\\w\\wc",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?=a(?i)b)\\w\\wc",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?=a(?i)b)\\w\\wc",[]))), - <<"Ab">> = iolist_to_binary(join(re:split("Ab","(?=a(?i)b)\\w\\wc",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?=a(?i)b)\\w\\wc",[]))), + <<"Ab">> = iolist_to_binary(join(re:split("Ab","(?=a(?i)b)\\w\\wc",[trim]))), <<"Ab">> = iolist_to_binary(join(re:split("Ab","(?=a(?i)b)\\w\\wc",[{parts, - 2}]))), - <<"Ab">> = iolist_to_binary(join(re:split("Ab","(?=a(?i)b)\\w\\wc",[]))), - <<"abC">> = iolist_to_binary(join(re:split("abC","(?=a(?i)b)\\w\\wc",[trim]))), + 2}]))), + <<"Ab">> = iolist_to_binary(join(re:split("Ab","(?=a(?i)b)\\w\\wc",[]))), + <<"abC">> = iolist_to_binary(join(re:split("abC","(?=a(?i)b)\\w\\wc",[trim]))), <<"abC">> = iolist_to_binary(join(re:split("abC","(?=a(?i)b)\\w\\wc",[{parts, - 2}]))), - <<"abC">> = iolist_to_binary(join(re:split("abC","(?=a(?i)b)\\w\\wc",[]))), - <<"aBC">> = iolist_to_binary(join(re:split("aBC","(?=a(?i)b)\\w\\wc",[trim]))), + 2}]))), + <<"abC">> = iolist_to_binary(join(re:split("abC","(?=a(?i)b)\\w\\wc",[]))), + <<"aBC">> = iolist_to_binary(join(re:split("aBC","(?=a(?i)b)\\w\\wc",[trim]))), <<"aBC">> = iolist_to_binary(join(re:split("aBC","(?=a(?i)b)\\w\\wc",[{parts, - 2}]))), - <<"aBC">> = iolist_to_binary(join(re:split("aBC","(?=a(?i)b)\\w\\wc",[]))), - <<"ab:xx">> = iolist_to_binary(join(re:split("abxxc","(?<=a(?i)b)(\\w\\w)c",[trim]))), + 2}]))), + <<"aBC">> = iolist_to_binary(join(re:split("aBC","(?=a(?i)b)\\w\\wc",[]))), + <<"ab:xx">> = iolist_to_binary(join(re:split("abxxc","(?<=a(?i)b)(\\w\\w)c",[trim]))), <<"ab:xx:">> = iolist_to_binary(join(re:split("abxxc","(?<=a(?i)b)(\\w\\w)c",[{parts, - 2}]))), - <<"ab:xx:">> = iolist_to_binary(join(re:split("abxxc","(?<=a(?i)b)(\\w\\w)c",[]))), - <<"aB:xx">> = iolist_to_binary(join(re:split("aBxxc","(?<=a(?i)b)(\\w\\w)c",[trim]))), + 2}]))), + <<"ab:xx:">> = iolist_to_binary(join(re:split("abxxc","(?<=a(?i)b)(\\w\\w)c",[]))), + <<"aB:xx">> = iolist_to_binary(join(re:split("aBxxc","(?<=a(?i)b)(\\w\\w)c",[trim]))), <<"aB:xx:">> = iolist_to_binary(join(re:split("aBxxc","(?<=a(?i)b)(\\w\\w)c",[{parts, - 2}]))), - <<"aB:xx:">> = iolist_to_binary(join(re:split("aBxxc","(?<=a(?i)b)(\\w\\w)c",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=a(?i)b)(\\w\\w)c",[trim]))), + 2}]))), + <<"aB:xx:">> = iolist_to_binary(join(re:split("aBxxc","(?<=a(?i)b)(\\w\\w)c",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=a(?i)b)(\\w\\w)c",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=a(?i)b)(\\w\\w)c",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=a(?i)b)(\\w\\w)c",[]))), - <<"Abxxc">> = iolist_to_binary(join(re:split("Abxxc","(?<=a(?i)b)(\\w\\w)c",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=a(?i)b)(\\w\\w)c",[]))), + <<"Abxxc">> = iolist_to_binary(join(re:split("Abxxc","(?<=a(?i)b)(\\w\\w)c",[trim]))), <<"Abxxc">> = iolist_to_binary(join(re:split("Abxxc","(?<=a(?i)b)(\\w\\w)c",[{parts, - 2}]))), - <<"Abxxc">> = iolist_to_binary(join(re:split("Abxxc","(?<=a(?i)b)(\\w\\w)c",[]))), - <<"ABxxc">> = iolist_to_binary(join(re:split("ABxxc","(?<=a(?i)b)(\\w\\w)c",[trim]))), + 2}]))), + <<"Abxxc">> = iolist_to_binary(join(re:split("Abxxc","(?<=a(?i)b)(\\w\\w)c",[]))), + <<"ABxxc">> = iolist_to_binary(join(re:split("ABxxc","(?<=a(?i)b)(\\w\\w)c",[trim]))), <<"ABxxc">> = iolist_to_binary(join(re:split("ABxxc","(?<=a(?i)b)(\\w\\w)c",[{parts, - 2}]))), - <<"ABxxc">> = iolist_to_binary(join(re:split("ABxxc","(?<=a(?i)b)(\\w\\w)c",[]))), - <<"abxxC">> = iolist_to_binary(join(re:split("abxxC","(?<=a(?i)b)(\\w\\w)c",[trim]))), + 2}]))), + <<"ABxxc">> = iolist_to_binary(join(re:split("ABxxc","(?<=a(?i)b)(\\w\\w)c",[]))), + <<"abxxC">> = iolist_to_binary(join(re:split("abxxC","(?<=a(?i)b)(\\w\\w)c",[trim]))), <<"abxxC">> = iolist_to_binary(join(re:split("abxxC","(?<=a(?i)b)(\\w\\w)c",[{parts, - 2}]))), - <<"abxxC">> = iolist_to_binary(join(re:split("abxxC","(?<=a(?i)b)(\\w\\w)c",[]))), - <<":a">> = iolist_to_binary(join(re:split("aA","(?:(a)|b)(?(1)A|B)",[trim]))), + 2}]))), + <<"abxxC">> = iolist_to_binary(join(re:split("abxxC","(?<=a(?i)b)(\\w\\w)c",[]))), + <<":a">> = iolist_to_binary(join(re:split("aA","(?:(a)|b)(?(1)A|B)",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aA","(?:(a)|b)(?(1)A|B)",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aA","(?:(a)|b)(?(1)A|B)",[]))), - <<"">> = iolist_to_binary(join(re:split("bB","(?:(a)|b)(?(1)A|B)",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aA","(?:(a)|b)(?(1)A|B)",[]))), + <<"">> = iolist_to_binary(join(re:split("bB","(?:(a)|b)(?(1)A|B)",[trim]))), <<"::">> = iolist_to_binary(join(re:split("bB","(?:(a)|b)(?(1)A|B)",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("bB","(?:(a)|b)(?(1)A|B)",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?:(a)|b)(?(1)A|B)",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("bB","(?:(a)|b)(?(1)A|B)",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?:(a)|b)(?(1)A|B)",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?:(a)|b)(?(1)A|B)",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?:(a)|b)(?(1)A|B)",[]))), - <<"aB">> = iolist_to_binary(join(re:split("aB","(?:(a)|b)(?(1)A|B)",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?:(a)|b)(?(1)A|B)",[]))), + <<"aB">> = iolist_to_binary(join(re:split("aB","(?:(a)|b)(?(1)A|B)",[trim]))), <<"aB">> = iolist_to_binary(join(re:split("aB","(?:(a)|b)(?(1)A|B)",[{parts, - 2}]))), - <<"aB">> = iolist_to_binary(join(re:split("aB","(?:(a)|b)(?(1)A|B)",[]))), - <<"bA">> = iolist_to_binary(join(re:split("bA","(?:(a)|b)(?(1)A|B)",[trim]))), + 2}]))), + <<"aB">> = iolist_to_binary(join(re:split("aB","(?:(a)|b)(?(1)A|B)",[]))), + <<"bA">> = iolist_to_binary(join(re:split("bA","(?:(a)|b)(?(1)A|B)",[trim]))), <<"bA">> = iolist_to_binary(join(re:split("bA","(?:(a)|b)(?(1)A|B)",[{parts, - 2}]))), - <<"bA">> = iolist_to_binary(join(re:split("bA","(?:(a)|b)(?(1)A|B)",[]))), - <<":a">> = iolist_to_binary(join(re:split("aa","^(a)?(?(1)a|b)+$",[trim]))), + 2}]))), + <<"bA">> = iolist_to_binary(join(re:split("bA","(?:(a)|b)(?(1)A|B)",[]))), + <<":a">> = iolist_to_binary(join(re:split("aa","^(a)?(?(1)a|b)+$",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aa","^(a)?(?(1)a|b)+$",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aa","^(a)?(?(1)a|b)+$",[]))), - <<"">> = iolist_to_binary(join(re:split("b","^(a)?(?(1)a|b)+$",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aa","^(a)?(?(1)a|b)+$",[]))), + <<"">> = iolist_to_binary(join(re:split("b","^(a)?(?(1)a|b)+$",[trim]))), <<"::">> = iolist_to_binary(join(re:split("b","^(a)?(?(1)a|b)+$",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("b","^(a)?(?(1)a|b)+$",[]))), - <<"">> = iolist_to_binary(join(re:split("bb","^(a)?(?(1)a|b)+$",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("b","^(a)?(?(1)a|b)+$",[]))), + <<"">> = iolist_to_binary(join(re:split("bb","^(a)?(?(1)a|b)+$",[trim]))), <<"::">> = iolist_to_binary(join(re:split("bb","^(a)?(?(1)a|b)+$",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("bb","^(a)?(?(1)a|b)+$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a)?(?(1)a|b)+$",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("bb","^(a)?(?(1)a|b)+$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a)?(?(1)a|b)+$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a)?(?(1)a|b)+$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a)?(?(1)a|b)+$",[]))), - <<"ab">> = iolist_to_binary(join(re:split("ab","^(a)?(?(1)a|b)+$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a)?(?(1)a|b)+$",[]))), + <<"ab">> = iolist_to_binary(join(re:split("ab","^(a)?(?(1)a|b)+$",[trim]))), <<"ab">> = iolist_to_binary(join(re:split("ab","^(a)?(?(1)a|b)+$",[{parts, - 2}]))), - <<"ab">> = iolist_to_binary(join(re:split("ab","^(a)?(?(1)a|b)+$",[]))), + 2}]))), + <<"ab">> = iolist_to_binary(join(re:split("ab","^(a)?(?(1)a|b)+$",[]))), ok. run13() -> - <<"">> = iolist_to_binary(join(re:split("abc:","^(?(?=abc)\\w{3}:|\\d\\d)$",[trim]))), + <<"">> = iolist_to_binary(join(re:split("abc:","^(?(?=abc)\\w{3}:|\\d\\d)$",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc:","^(?(?=abc)\\w{3}:|\\d\\d)$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc:","^(?(?=abc)\\w{3}:|\\d\\d)$",[]))), - <<"">> = iolist_to_binary(join(re:split("12","^(?(?=abc)\\w{3}:|\\d\\d)$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc:","^(?(?=abc)\\w{3}:|\\d\\d)$",[]))), + <<"">> = iolist_to_binary(join(re:split("12","^(?(?=abc)\\w{3}:|\\d\\d)$",[trim]))), <<":">> = iolist_to_binary(join(re:split("12","^(?(?=abc)\\w{3}:|\\d\\d)$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("12","^(?(?=abc)\\w{3}:|\\d\\d)$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?(?=abc)\\w{3}:|\\d\\d)$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("12","^(?(?=abc)\\w{3}:|\\d\\d)$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?(?=abc)\\w{3}:|\\d\\d)$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?(?=abc)\\w{3}:|\\d\\d)$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?(?=abc)\\w{3}:|\\d\\d)$",[]))), - <<"123">> = iolist_to_binary(join(re:split("123","^(?(?=abc)\\w{3}:|\\d\\d)$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?(?=abc)\\w{3}:|\\d\\d)$",[]))), + <<"123">> = iolist_to_binary(join(re:split("123","^(?(?=abc)\\w{3}:|\\d\\d)$",[trim]))), <<"123">> = iolist_to_binary(join(re:split("123","^(?(?=abc)\\w{3}:|\\d\\d)$",[{parts, - 2}]))), - <<"123">> = iolist_to_binary(join(re:split("123","^(?(?=abc)\\w{3}:|\\d\\d)$",[]))), - <<"xyz">> = iolist_to_binary(join(re:split("xyz","^(?(?=abc)\\w{3}:|\\d\\d)$",[trim]))), + 2}]))), + <<"123">> = iolist_to_binary(join(re:split("123","^(?(?=abc)\\w{3}:|\\d\\d)$",[]))), + <<"xyz">> = iolist_to_binary(join(re:split("xyz","^(?(?=abc)\\w{3}:|\\d\\d)$",[trim]))), <<"xyz">> = iolist_to_binary(join(re:split("xyz","^(?(?=abc)\\w{3}:|\\d\\d)$",[{parts, - 2}]))), - <<"xyz">> = iolist_to_binary(join(re:split("xyz","^(?(?=abc)\\w{3}:|\\d\\d)$",[]))), - <<"">> = iolist_to_binary(join(re:split("abc:","^(?(?!abc)\\d\\d|\\w{3}:)$",[trim]))), + 2}]))), + <<"xyz">> = iolist_to_binary(join(re:split("xyz","^(?(?=abc)\\w{3}:|\\d\\d)$",[]))), + <<"">> = iolist_to_binary(join(re:split("abc:","^(?(?!abc)\\d\\d|\\w{3}:)$",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc:","^(?(?!abc)\\d\\d|\\w{3}:)$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc:","^(?(?!abc)\\d\\d|\\w{3}:)$",[]))), - <<"">> = iolist_to_binary(join(re:split("12","^(?(?!abc)\\d\\d|\\w{3}:)$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc:","^(?(?!abc)\\d\\d|\\w{3}:)$",[]))), + <<"">> = iolist_to_binary(join(re:split("12","^(?(?!abc)\\d\\d|\\w{3}:)$",[trim]))), <<":">> = iolist_to_binary(join(re:split("12","^(?(?!abc)\\d\\d|\\w{3}:)$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("12","^(?(?!abc)\\d\\d|\\w{3}:)$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?(?!abc)\\d\\d|\\w{3}:)$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("12","^(?(?!abc)\\d\\d|\\w{3}:)$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?(?!abc)\\d\\d|\\w{3}:)$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?(?!abc)\\d\\d|\\w{3}:)$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?(?!abc)\\d\\d|\\w{3}:)$",[]))), - <<"123">> = iolist_to_binary(join(re:split("123","^(?(?!abc)\\d\\d|\\w{3}:)$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?(?!abc)\\d\\d|\\w{3}:)$",[]))), + <<"123">> = iolist_to_binary(join(re:split("123","^(?(?!abc)\\d\\d|\\w{3}:)$",[trim]))), <<"123">> = iolist_to_binary(join(re:split("123","^(?(?!abc)\\d\\d|\\w{3}:)$",[{parts, - 2}]))), - <<"123">> = iolist_to_binary(join(re:split("123","^(?(?!abc)\\d\\d|\\w{3}:)$",[]))), - <<"xyz">> = iolist_to_binary(join(re:split("xyz","^(?(?!abc)\\d\\d|\\w{3}:)$",[trim]))), + 2}]))), + <<"123">> = iolist_to_binary(join(re:split("123","^(?(?!abc)\\d\\d|\\w{3}:)$",[]))), + <<"xyz">> = iolist_to_binary(join(re:split("xyz","^(?(?!abc)\\d\\d|\\w{3}:)$",[trim]))), <<"xyz">> = iolist_to_binary(join(re:split("xyz","^(?(?!abc)\\d\\d|\\w{3}:)$",[{parts, - 2}]))), - <<"xyz">> = iolist_to_binary(join(re:split("xyz","^(?(?!abc)\\d\\d|\\w{3}:)$",[]))), - <<"foo">> = iolist_to_binary(join(re:split("foobar","(?(?<=foo)bar|cat)",[trim]))), + 2}]))), + <<"xyz">> = iolist_to_binary(join(re:split("xyz","^(?(?!abc)\\d\\d|\\w{3}:)$",[]))), + <<"foo">> = iolist_to_binary(join(re:split("foobar","(?(?<=foo)bar|cat)",[trim]))), <<"foo:">> = iolist_to_binary(join(re:split("foobar","(?(?<=foo)bar|cat)",[{parts, - 2}]))), - <<"foo:">> = iolist_to_binary(join(re:split("foobar","(?(?<=foo)bar|cat)",[]))), - <<"">> = iolist_to_binary(join(re:split("cat","(?(?<=foo)bar|cat)",[trim]))), + 2}]))), + <<"foo:">> = iolist_to_binary(join(re:split("foobar","(?(?<=foo)bar|cat)",[]))), + <<"">> = iolist_to_binary(join(re:split("cat","(?(?<=foo)bar|cat)",[trim]))), <<":">> = iolist_to_binary(join(re:split("cat","(?(?<=foo)bar|cat)",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("cat","(?(?<=foo)bar|cat)",[]))), - <<"f">> = iolist_to_binary(join(re:split("fcat","(?(?<=foo)bar|cat)",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("cat","(?(?<=foo)bar|cat)",[]))), + <<"f">> = iolist_to_binary(join(re:split("fcat","(?(?<=foo)bar|cat)",[trim]))), <<"f:">> = iolist_to_binary(join(re:split("fcat","(?(?<=foo)bar|cat)",[{parts, - 2}]))), - <<"f:">> = iolist_to_binary(join(re:split("fcat","(?(?<=foo)bar|cat)",[]))), - <<"fo">> = iolist_to_binary(join(re:split("focat","(?(?<=foo)bar|cat)",[trim]))), + 2}]))), + <<"f:">> = iolist_to_binary(join(re:split("fcat","(?(?<=foo)bar|cat)",[]))), + <<"fo">> = iolist_to_binary(join(re:split("focat","(?(?<=foo)bar|cat)",[trim]))), <<"fo:">> = iolist_to_binary(join(re:split("focat","(?(?<=foo)bar|cat)",[{parts, - 2}]))), - <<"fo:">> = iolist_to_binary(join(re:split("focat","(?(?<=foo)bar|cat)",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?(?<=foo)bar|cat)",[trim]))), + 2}]))), + <<"fo:">> = iolist_to_binary(join(re:split("focat","(?(?<=foo)bar|cat)",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?(?<=foo)bar|cat)",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?(?<=foo)bar|cat)",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?(?<=foo)bar|cat)",[]))), - <<"foocat">> = iolist_to_binary(join(re:split("foocat","(?(?<=foo)bar|cat)",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?(?<=foo)bar|cat)",[]))), + <<"foocat">> = iolist_to_binary(join(re:split("foocat","(?(?<=foo)bar|cat)",[trim]))), <<"foocat">> = iolist_to_binary(join(re:split("foocat","(?(?<=foo)bar|cat)",[{parts, - 2}]))), - <<"foocat">> = iolist_to_binary(join(re:split("foocat","(?(?<=foo)bar|cat)",[]))), - <<"foo">> = iolist_to_binary(join(re:split("foobar","(?(?<!foo)cat|bar)",[trim]))), + 2}]))), + <<"foocat">> = iolist_to_binary(join(re:split("foocat","(?(?<=foo)bar|cat)",[]))), + <<"foo">> = iolist_to_binary(join(re:split("foobar","(?(?<!foo)cat|bar)",[trim]))), <<"foo:">> = iolist_to_binary(join(re:split("foobar","(?(?<!foo)cat|bar)",[{parts, - 2}]))), - <<"foo:">> = iolist_to_binary(join(re:split("foobar","(?(?<!foo)cat|bar)",[]))), - <<"">> = iolist_to_binary(join(re:split("cat","(?(?<!foo)cat|bar)",[trim]))), + 2}]))), + <<"foo:">> = iolist_to_binary(join(re:split("foobar","(?(?<!foo)cat|bar)",[]))), + <<"">> = iolist_to_binary(join(re:split("cat","(?(?<!foo)cat|bar)",[trim]))), <<":">> = iolist_to_binary(join(re:split("cat","(?(?<!foo)cat|bar)",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("cat","(?(?<!foo)cat|bar)",[]))), - <<"f">> = iolist_to_binary(join(re:split("fcat","(?(?<!foo)cat|bar)",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("cat","(?(?<!foo)cat|bar)",[]))), + <<"f">> = iolist_to_binary(join(re:split("fcat","(?(?<!foo)cat|bar)",[trim]))), <<"f:">> = iolist_to_binary(join(re:split("fcat","(?(?<!foo)cat|bar)",[{parts, - 2}]))), - <<"f:">> = iolist_to_binary(join(re:split("fcat","(?(?<!foo)cat|bar)",[]))), - <<"fo">> = iolist_to_binary(join(re:split("focat","(?(?<!foo)cat|bar)",[trim]))), + 2}]))), + <<"f:">> = iolist_to_binary(join(re:split("fcat","(?(?<!foo)cat|bar)",[]))), + <<"fo">> = iolist_to_binary(join(re:split("focat","(?(?<!foo)cat|bar)",[trim]))), <<"fo:">> = iolist_to_binary(join(re:split("focat","(?(?<!foo)cat|bar)",[{parts, - 2}]))), - <<"fo:">> = iolist_to_binary(join(re:split("focat","(?(?<!foo)cat|bar)",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?(?<!foo)cat|bar)",[trim]))), + 2}]))), + <<"fo:">> = iolist_to_binary(join(re:split("focat","(?(?<!foo)cat|bar)",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?(?<!foo)cat|bar)",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?(?<!foo)cat|bar)",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?(?<!foo)cat|bar)",[]))), - <<"foocat">> = iolist_to_binary(join(re:split("foocat","(?(?<!foo)cat|bar)",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?(?<!foo)cat|bar)",[]))), + <<"foocat">> = iolist_to_binary(join(re:split("foocat","(?(?<!foo)cat|bar)",[trim]))), <<"foocat">> = iolist_to_binary(join(re:split("foocat","(?(?<!foo)cat|bar)",[{parts, - 2}]))), - <<"foocat">> = iolist_to_binary(join(re:split("foocat","(?(?<!foo)cat|bar)",[]))), + 2}]))), + <<"foocat">> = iolist_to_binary(join(re:split("foocat","(?(?<!foo)cat|bar)",[]))), <<"">> = iolist_to_binary(join(re:split("abcd","( \\( )? [^()]+ (?(1) \\) |) ",[extended, - trim]))), + trim]))), <<"::">> = iolist_to_binary(join(re:split("abcd","( \\( )? [^()]+ (?(1) \\) |) ",[extended, {parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("abcd","( \\( )? [^()]+ (?(1) \\) |) ",[extended]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("abcd","( \\( )? [^()]+ (?(1) \\) |) ",[extended]))), <<":(">> = iolist_to_binary(join(re:split("(abcd)","( \\( )? [^()]+ (?(1) \\) |) ",[extended, - trim]))), + trim]))), <<":(:">> = iolist_to_binary(join(re:split("(abcd)","( \\( )? [^()]+ (?(1) \\) |) ",[extended, {parts, - 2}]))), - <<":(:">> = iolist_to_binary(join(re:split("(abcd)","( \\( )? [^()]+ (?(1) \\) |) ",[extended]))), + 2}]))), + <<":(:">> = iolist_to_binary(join(re:split("(abcd)","( \\( )? [^()]+ (?(1) \\) |) ",[extended]))), <<":::(">> = iolist_to_binary(join(re:split("the quick (abcd) fox","( \\( )? [^()]+ (?(1) \\) |) ",[extended, - trim]))), + trim]))), <<"::(abcd) fox">> = iolist_to_binary(join(re:split("the quick (abcd) fox","( \\( )? [^()]+ (?(1) \\) |) ",[extended, {parts, - 2}]))), - <<":::(:::">> = iolist_to_binary(join(re:split("the quick (abcd) fox","( \\( )? [^()]+ (?(1) \\) |) ",[extended]))), + 2}]))), + <<":::(:::">> = iolist_to_binary(join(re:split("the quick (abcd) fox","( \\( )? [^()]+ (?(1) \\) |) ",[extended]))), <<"(">> = iolist_to_binary(join(re:split("(abcd","( \\( )? [^()]+ (?(1) \\) |) ",[extended, - trim]))), + trim]))), <<"(::">> = iolist_to_binary(join(re:split("(abcd","( \\( )? [^()]+ (?(1) \\) |) ",[extended, {parts, - 2}]))), - <<"(::">> = iolist_to_binary(join(re:split("(abcd","( \\( )? [^()]+ (?(1) \\) |) ",[extended]))), + 2}]))), + <<"(::">> = iolist_to_binary(join(re:split("(abcd","( \\( )? [^()]+ (?(1) \\) |) ",[extended]))), <<"">> = iolist_to_binary(join(re:split("abcd","( \\( )? [^()]+ (?(1) \\) ) ",[extended, - trim]))), + trim]))), <<"::">> = iolist_to_binary(join(re:split("abcd","( \\( )? [^()]+ (?(1) \\) ) ",[extended, {parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("abcd","( \\( )? [^()]+ (?(1) \\) ) ",[extended]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("abcd","( \\( )? [^()]+ (?(1) \\) ) ",[extended]))), <<":(">> = iolist_to_binary(join(re:split("(abcd)","( \\( )? [^()]+ (?(1) \\) ) ",[extended, - trim]))), + trim]))), <<":(:">> = iolist_to_binary(join(re:split("(abcd)","( \\( )? [^()]+ (?(1) \\) ) ",[extended, {parts, - 2}]))), - <<":(:">> = iolist_to_binary(join(re:split("(abcd)","( \\( )? [^()]+ (?(1) \\) ) ",[extended]))), + 2}]))), + <<":(:">> = iolist_to_binary(join(re:split("(abcd)","( \\( )? [^()]+ (?(1) \\) ) ",[extended]))), <<":::(">> = iolist_to_binary(join(re:split("the quick (abcd) fox","( \\( )? [^()]+ (?(1) \\) ) ",[extended, - trim]))), + trim]))), <<"::(abcd) fox">> = iolist_to_binary(join(re:split("the quick (abcd) fox","( \\( )? [^()]+ (?(1) \\) ) ",[extended, {parts, - 2}]))), - <<":::(:::">> = iolist_to_binary(join(re:split("the quick (abcd) fox","( \\( )? [^()]+ (?(1) \\) ) ",[extended]))), + 2}]))), + <<":::(:::">> = iolist_to_binary(join(re:split("the quick (abcd) fox","( \\( )? [^()]+ (?(1) \\) ) ",[extended]))), <<"(">> = iolist_to_binary(join(re:split("(abcd","( \\( )? [^()]+ (?(1) \\) ) ",[extended, - trim]))), + trim]))), <<"(::">> = iolist_to_binary(join(re:split("(abcd","( \\( )? [^()]+ (?(1) \\) ) ",[extended, {parts, - 2}]))), - <<"(::">> = iolist_to_binary(join(re:split("(abcd","( \\( )? [^()]+ (?(1) \\) ) ",[extended]))), - <<":1:2">> = iolist_to_binary(join(re:split("12","^(?(2)a|(1)(2))+$",[trim]))), + 2}]))), + <<"(::">> = iolist_to_binary(join(re:split("(abcd","( \\( )? [^()]+ (?(1) \\) ) ",[extended]))), + <<":1:2">> = iolist_to_binary(join(re:split("12","^(?(2)a|(1)(2))+$",[trim]))), <<":1:2:">> = iolist_to_binary(join(re:split("12","^(?(2)a|(1)(2))+$",[{parts, - 2}]))), - <<":1:2:">> = iolist_to_binary(join(re:split("12","^(?(2)a|(1)(2))+$",[]))), - <<":1:2">> = iolist_to_binary(join(re:split("12a","^(?(2)a|(1)(2))+$",[trim]))), + 2}]))), + <<":1:2:">> = iolist_to_binary(join(re:split("12","^(?(2)a|(1)(2))+$",[]))), + <<":1:2">> = iolist_to_binary(join(re:split("12a","^(?(2)a|(1)(2))+$",[trim]))), <<":1:2:">> = iolist_to_binary(join(re:split("12a","^(?(2)a|(1)(2))+$",[{parts, - 2}]))), - <<":1:2:">> = iolist_to_binary(join(re:split("12a","^(?(2)a|(1)(2))+$",[]))), - <<":1:2">> = iolist_to_binary(join(re:split("12aa","^(?(2)a|(1)(2))+$",[trim]))), + 2}]))), + <<":1:2:">> = iolist_to_binary(join(re:split("12a","^(?(2)a|(1)(2))+$",[]))), + <<":1:2">> = iolist_to_binary(join(re:split("12aa","^(?(2)a|(1)(2))+$",[trim]))), <<":1:2:">> = iolist_to_binary(join(re:split("12aa","^(?(2)a|(1)(2))+$",[{parts, - 2}]))), - <<":1:2:">> = iolist_to_binary(join(re:split("12aa","^(?(2)a|(1)(2))+$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?(2)a|(1)(2))+$",[trim]))), + 2}]))), + <<":1:2:">> = iolist_to_binary(join(re:split("12aa","^(?(2)a|(1)(2))+$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?(2)a|(1)(2))+$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?(2)a|(1)(2))+$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?(2)a|(1)(2))+$",[]))), - <<"1234">> = iolist_to_binary(join(re:split("1234","^(?(2)a|(1)(2))+$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?(2)a|(1)(2))+$",[]))), + <<"1234">> = iolist_to_binary(join(re:split("1234","^(?(2)a|(1)(2))+$",[trim]))), <<"1234">> = iolist_to_binary(join(re:split("1234","^(?(2)a|(1)(2))+$",[{parts, - 2}]))), - <<"1234">> = iolist_to_binary(join(re:split("1234","^(?(2)a|(1)(2))+$",[]))), - <<":blah">> = iolist_to_binary(join(re:split("blah blah","((?i)blah)\\s+\\1",[trim]))), + 2}]))), + <<"1234">> = iolist_to_binary(join(re:split("1234","^(?(2)a|(1)(2))+$",[]))), + <<":blah">> = iolist_to_binary(join(re:split("blah blah","((?i)blah)\\s+\\1",[trim]))), <<":blah:">> = iolist_to_binary(join(re:split("blah blah","((?i)blah)\\s+\\1",[{parts, - 2}]))), - <<":blah:">> = iolist_to_binary(join(re:split("blah blah","((?i)blah)\\s+\\1",[]))), - <<":BLAH">> = iolist_to_binary(join(re:split("BLAH BLAH","((?i)blah)\\s+\\1",[trim]))), + 2}]))), + <<":blah:">> = iolist_to_binary(join(re:split("blah blah","((?i)blah)\\s+\\1",[]))), + <<":BLAH">> = iolist_to_binary(join(re:split("BLAH BLAH","((?i)blah)\\s+\\1",[trim]))), <<":BLAH:">> = iolist_to_binary(join(re:split("BLAH BLAH","((?i)blah)\\s+\\1",[{parts, - 2}]))), - <<":BLAH:">> = iolist_to_binary(join(re:split("BLAH BLAH","((?i)blah)\\s+\\1",[]))), - <<":Blah">> = iolist_to_binary(join(re:split("Blah Blah","((?i)blah)\\s+\\1",[trim]))), + 2}]))), + <<":BLAH:">> = iolist_to_binary(join(re:split("BLAH BLAH","((?i)blah)\\s+\\1",[]))), + <<":Blah">> = iolist_to_binary(join(re:split("Blah Blah","((?i)blah)\\s+\\1",[trim]))), <<":Blah:">> = iolist_to_binary(join(re:split("Blah Blah","((?i)blah)\\s+\\1",[{parts, - 2}]))), - <<":Blah:">> = iolist_to_binary(join(re:split("Blah Blah","((?i)blah)\\s+\\1",[]))), - <<":blaH">> = iolist_to_binary(join(re:split("blaH blaH","((?i)blah)\\s+\\1",[trim]))), + 2}]))), + <<":Blah:">> = iolist_to_binary(join(re:split("Blah Blah","((?i)blah)\\s+\\1",[]))), + <<":blaH">> = iolist_to_binary(join(re:split("blaH blaH","((?i)blah)\\s+\\1",[trim]))), <<":blaH:">> = iolist_to_binary(join(re:split("blaH blaH","((?i)blah)\\s+\\1",[{parts, - 2}]))), - <<":blaH:">> = iolist_to_binary(join(re:split("blaH blaH","((?i)blah)\\s+\\1",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","((?i)blah)\\s+\\1",[trim]))), + 2}]))), + <<":blaH:">> = iolist_to_binary(join(re:split("blaH blaH","((?i)blah)\\s+\\1",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","((?i)blah)\\s+\\1",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","((?i)blah)\\s+\\1",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","((?i)blah)\\s+\\1",[]))), - <<"blah BLAH">> = iolist_to_binary(join(re:split("blah BLAH","((?i)blah)\\s+\\1",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","((?i)blah)\\s+\\1",[]))), + <<"blah BLAH">> = iolist_to_binary(join(re:split("blah BLAH","((?i)blah)\\s+\\1",[trim]))), <<"blah BLAH">> = iolist_to_binary(join(re:split("blah BLAH","((?i)blah)\\s+\\1",[{parts, - 2}]))), - <<"blah BLAH">> = iolist_to_binary(join(re:split("blah BLAH","((?i)blah)\\s+\\1",[]))), - <<"Blah blah">> = iolist_to_binary(join(re:split("Blah blah","((?i)blah)\\s+\\1",[trim]))), + 2}]))), + <<"blah BLAH">> = iolist_to_binary(join(re:split("blah BLAH","((?i)blah)\\s+\\1",[]))), + <<"Blah blah">> = iolist_to_binary(join(re:split("Blah blah","((?i)blah)\\s+\\1",[trim]))), <<"Blah blah">> = iolist_to_binary(join(re:split("Blah blah","((?i)blah)\\s+\\1",[{parts, - 2}]))), - <<"Blah blah">> = iolist_to_binary(join(re:split("Blah blah","((?i)blah)\\s+\\1",[]))), - <<"blaH blah">> = iolist_to_binary(join(re:split("blaH blah","((?i)blah)\\s+\\1",[trim]))), + 2}]))), + <<"Blah blah">> = iolist_to_binary(join(re:split("Blah blah","((?i)blah)\\s+\\1",[]))), + <<"blaH blah">> = iolist_to_binary(join(re:split("blaH blah","((?i)blah)\\s+\\1",[trim]))), <<"blaH blah">> = iolist_to_binary(join(re:split("blaH blah","((?i)blah)\\s+\\1",[{parts, - 2}]))), - <<"blaH blah">> = iolist_to_binary(join(re:split("blaH blah","((?i)blah)\\s+\\1",[]))), - <<":blah">> = iolist_to_binary(join(re:split("blah blah","((?i)blah)\\s+(?i:\\1)",[trim]))), + 2}]))), + <<"blaH blah">> = iolist_to_binary(join(re:split("blaH blah","((?i)blah)\\s+\\1",[]))), + <<":blah">> = iolist_to_binary(join(re:split("blah blah","((?i)blah)\\s+(?i:\\1)",[trim]))), <<":blah:">> = iolist_to_binary(join(re:split("blah blah","((?i)blah)\\s+(?i:\\1)",[{parts, - 2}]))), - <<":blah:">> = iolist_to_binary(join(re:split("blah blah","((?i)blah)\\s+(?i:\\1)",[]))), - <<":BLAH">> = iolist_to_binary(join(re:split("BLAH BLAH","((?i)blah)\\s+(?i:\\1)",[trim]))), + 2}]))), + <<":blah:">> = iolist_to_binary(join(re:split("blah blah","((?i)blah)\\s+(?i:\\1)",[]))), + <<":BLAH">> = iolist_to_binary(join(re:split("BLAH BLAH","((?i)blah)\\s+(?i:\\1)",[trim]))), <<":BLAH:">> = iolist_to_binary(join(re:split("BLAH BLAH","((?i)blah)\\s+(?i:\\1)",[{parts, - 2}]))), - <<":BLAH:">> = iolist_to_binary(join(re:split("BLAH BLAH","((?i)blah)\\s+(?i:\\1)",[]))), - <<":Blah">> = iolist_to_binary(join(re:split("Blah Blah","((?i)blah)\\s+(?i:\\1)",[trim]))), + 2}]))), + <<":BLAH:">> = iolist_to_binary(join(re:split("BLAH BLAH","((?i)blah)\\s+(?i:\\1)",[]))), + <<":Blah">> = iolist_to_binary(join(re:split("Blah Blah","((?i)blah)\\s+(?i:\\1)",[trim]))), <<":Blah:">> = iolist_to_binary(join(re:split("Blah Blah","((?i)blah)\\s+(?i:\\1)",[{parts, - 2}]))), - <<":Blah:">> = iolist_to_binary(join(re:split("Blah Blah","((?i)blah)\\s+(?i:\\1)",[]))), - <<":blaH">> = iolist_to_binary(join(re:split("blaH blaH","((?i)blah)\\s+(?i:\\1)",[trim]))), + 2}]))), + <<":Blah:">> = iolist_to_binary(join(re:split("Blah Blah","((?i)blah)\\s+(?i:\\1)",[]))), + <<":blaH">> = iolist_to_binary(join(re:split("blaH blaH","((?i)blah)\\s+(?i:\\1)",[trim]))), <<":blaH:">> = iolist_to_binary(join(re:split("blaH blaH","((?i)blah)\\s+(?i:\\1)",[{parts, - 2}]))), - <<":blaH:">> = iolist_to_binary(join(re:split("blaH blaH","((?i)blah)\\s+(?i:\\1)",[]))), - <<":blah">> = iolist_to_binary(join(re:split("blah BLAH","((?i)blah)\\s+(?i:\\1)",[trim]))), + 2}]))), + <<":blaH:">> = iolist_to_binary(join(re:split("blaH blaH","((?i)blah)\\s+(?i:\\1)",[]))), + <<":blah">> = iolist_to_binary(join(re:split("blah BLAH","((?i)blah)\\s+(?i:\\1)",[trim]))), <<":blah:">> = iolist_to_binary(join(re:split("blah BLAH","((?i)blah)\\s+(?i:\\1)",[{parts, - 2}]))), - <<":blah:">> = iolist_to_binary(join(re:split("blah BLAH","((?i)blah)\\s+(?i:\\1)",[]))), - <<":Blah">> = iolist_to_binary(join(re:split("Blah blah","((?i)blah)\\s+(?i:\\1)",[trim]))), + 2}]))), + <<":blah:">> = iolist_to_binary(join(re:split("blah BLAH","((?i)blah)\\s+(?i:\\1)",[]))), + <<":Blah">> = iolist_to_binary(join(re:split("Blah blah","((?i)blah)\\s+(?i:\\1)",[trim]))), <<":Blah:">> = iolist_to_binary(join(re:split("Blah blah","((?i)blah)\\s+(?i:\\1)",[{parts, - 2}]))), - <<":Blah:">> = iolist_to_binary(join(re:split("Blah blah","((?i)blah)\\s+(?i:\\1)",[]))), - <<":blaH">> = iolist_to_binary(join(re:split("blaH blah","((?i)blah)\\s+(?i:\\1)",[trim]))), + 2}]))), + <<":Blah:">> = iolist_to_binary(join(re:split("Blah blah","((?i)blah)\\s+(?i:\\1)",[]))), + <<":blaH">> = iolist_to_binary(join(re:split("blaH blah","((?i)blah)\\s+(?i:\\1)",[trim]))), <<":blaH:">> = iolist_to_binary(join(re:split("blaH blah","((?i)blah)\\s+(?i:\\1)",[{parts, - 2}]))), - <<":blaH:">> = iolist_to_binary(join(re:split("blaH blah","((?i)blah)\\s+(?i:\\1)",[]))), - <<"">> = iolist_to_binary(join(re:split("a","(?>a*)*",[trim]))), + 2}]))), + <<":blaH:">> = iolist_to_binary(join(re:split("blaH blah","((?i)blah)\\s+(?i:\\1)",[]))), + <<"">> = iolist_to_binary(join(re:split("a","(?>a*)*",[trim]))), <<":">> = iolist_to_binary(join(re:split("a","(?>a*)*",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a","(?>a*)*",[]))), - <<"">> = iolist_to_binary(join(re:split("aa","(?>a*)*",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a","(?>a*)*",[]))), + <<"">> = iolist_to_binary(join(re:split("aa","(?>a*)*",[trim]))), <<":">> = iolist_to_binary(join(re:split("aa","(?>a*)*",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aa","(?>a*)*",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaa","(?>a*)*",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aa","(?>a*)*",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaa","(?>a*)*",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaa","(?>a*)*",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaa","(?>a*)*",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","(abc|)+",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaa","(?>a*)*",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","(abc|)+",[trim]))), <<"::">> = iolist_to_binary(join(re:split("abc","(abc|)+",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("abc","(abc|)+",[]))), - <<"">> = iolist_to_binary(join(re:split("abcabc","(abc|)+",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("abc","(abc|)+",[]))), + <<"">> = iolist_to_binary(join(re:split("abcabc","(abc|)+",[trim]))), <<"::">> = iolist_to_binary(join(re:split("abcabc","(abc|)+",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("abcabc","(abc|)+",[]))), - <<"">> = iolist_to_binary(join(re:split("abcabcabc","(abc|)+",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("abcabc","(abc|)+",[]))), + <<"">> = iolist_to_binary(join(re:split("abcabcabc","(abc|)+",[trim]))), <<"::">> = iolist_to_binary(join(re:split("abcabcabc","(abc|)+",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("abcabcabc","(abc|)+",[]))), - <<"x::y::z">> = iolist_to_binary(join(re:split("xyz","(abc|)+",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("abcabcabc","(abc|)+",[]))), + <<"x::y::z">> = iolist_to_binary(join(re:split("xyz","(abc|)+",[trim]))), <<"x::yz">> = iolist_to_binary(join(re:split("xyz","(abc|)+",[{parts, - 2}]))), - <<"x::y::z::">> = iolist_to_binary(join(re:split("xyz","(abc|)+",[]))), - <<"">> = iolist_to_binary(join(re:split("a","([a]*)*",[trim]))), + 2}]))), + <<"x::y::z::">> = iolist_to_binary(join(re:split("xyz","(abc|)+",[]))), + <<"">> = iolist_to_binary(join(re:split("a","([a]*)*",[trim]))), <<"::">> = iolist_to_binary(join(re:split("a","([a]*)*",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("a","([a]*)*",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaaa","([a]*)*",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("a","([a]*)*",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaaa","([a]*)*",[trim]))), <<"::">> = iolist_to_binary(join(re:split("aaaaa","([a]*)*",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("aaaaa","([a]*)*",[]))), - <<"">> = iolist_to_binary(join(re:split("a","([ab]*)*",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("aaaaa","([a]*)*",[]))), + <<"">> = iolist_to_binary(join(re:split("a","([ab]*)*",[trim]))), <<"::">> = iolist_to_binary(join(re:split("a","([ab]*)*",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("a","([ab]*)*",[]))), - <<"">> = iolist_to_binary(join(re:split("b","([ab]*)*",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("a","([ab]*)*",[]))), + <<"">> = iolist_to_binary(join(re:split("b","([ab]*)*",[trim]))), <<"::">> = iolist_to_binary(join(re:split("b","([ab]*)*",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("b","([ab]*)*",[]))), - <<"">> = iolist_to_binary(join(re:split("ababab","([ab]*)*",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("b","([ab]*)*",[]))), + <<"">> = iolist_to_binary(join(re:split("ababab","([ab]*)*",[trim]))), <<"::">> = iolist_to_binary(join(re:split("ababab","([ab]*)*",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("ababab","([ab]*)*",[]))), - <<"::c::d::e">> = iolist_to_binary(join(re:split("aaaabcde","([ab]*)*",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("ababab","([ab]*)*",[]))), + <<"::c::d::e">> = iolist_to_binary(join(re:split("aaaabcde","([ab]*)*",[trim]))), <<"::cde">> = iolist_to_binary(join(re:split("aaaabcde","([ab]*)*",[{parts, - 2}]))), - <<"::c::d::e::">> = iolist_to_binary(join(re:split("aaaabcde","([ab]*)*",[]))), - <<"">> = iolist_to_binary(join(re:split("bbbb","([ab]*)*",[trim]))), + 2}]))), + <<"::c::d::e::">> = iolist_to_binary(join(re:split("aaaabcde","([ab]*)*",[]))), + <<"">> = iolist_to_binary(join(re:split("bbbb","([ab]*)*",[trim]))), <<"::">> = iolist_to_binary(join(re:split("bbbb","([ab]*)*",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("bbbb","([ab]*)*",[]))), - <<"">> = iolist_to_binary(join(re:split("b","([^a]*)*",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("bbbb","([ab]*)*",[]))), + <<"">> = iolist_to_binary(join(re:split("b","([^a]*)*",[trim]))), <<"::">> = iolist_to_binary(join(re:split("b","([^a]*)*",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("b","([^a]*)*",[]))), - <<"">> = iolist_to_binary(join(re:split("bbbb","([^a]*)*",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("b","([^a]*)*",[]))), + <<"">> = iolist_to_binary(join(re:split("bbbb","([^a]*)*",[trim]))), <<"::">> = iolist_to_binary(join(re:split("bbbb","([^a]*)*",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("bbbb","([^a]*)*",[]))), - <<"a::a::a">> = iolist_to_binary(join(re:split("aaa","([^a]*)*",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("bbbb","([^a]*)*",[]))), + <<"a::a::a">> = iolist_to_binary(join(re:split("aaa","([^a]*)*",[trim]))), <<"a::aa">> = iolist_to_binary(join(re:split("aaa","([^a]*)*",[{parts, - 2}]))), - <<"a::a::a::">> = iolist_to_binary(join(re:split("aaa","([^a]*)*",[]))), - <<"">> = iolist_to_binary(join(re:split("cccc","([^ab]*)*",[trim]))), + 2}]))), + <<"a::a::a::">> = iolist_to_binary(join(re:split("aaa","([^a]*)*",[]))), + <<"">> = iolist_to_binary(join(re:split("cccc","([^ab]*)*",[trim]))), <<"::">> = iolist_to_binary(join(re:split("cccc","([^ab]*)*",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("cccc","([^ab]*)*",[]))), - <<"a::b::a::b">> = iolist_to_binary(join(re:split("abab","([^ab]*)*",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("cccc","([^ab]*)*",[]))), + <<"a::b::a::b">> = iolist_to_binary(join(re:split("abab","([^ab]*)*",[trim]))), <<"a::bab">> = iolist_to_binary(join(re:split("abab","([^ab]*)*",[{parts, - 2}]))), - <<"a::b::a::b::">> = iolist_to_binary(join(re:split("abab","([^ab]*)*",[]))), - <<"">> = iolist_to_binary(join(re:split("a","([a]*?)*",[trim]))), + 2}]))), + <<"a::b::a::b::">> = iolist_to_binary(join(re:split("abab","([^ab]*)*",[]))), + <<"">> = iolist_to_binary(join(re:split("a","([a]*?)*",[trim]))), <<"::">> = iolist_to_binary(join(re:split("a","([a]*?)*",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("a","([a]*?)*",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaa","([a]*?)*",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("a","([a]*?)*",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaa","([a]*?)*",[trim]))), <<"::aaa">> = iolist_to_binary(join(re:split("aaaa","([a]*?)*",[{parts, - 2}]))), - <<"::::::::">> = iolist_to_binary(join(re:split("aaaa","([a]*?)*",[]))), - <<"">> = iolist_to_binary(join(re:split("a","([ab]*?)*",[trim]))), + 2}]))), + <<"::::::::">> = iolist_to_binary(join(re:split("aaaa","([a]*?)*",[]))), + <<"">> = iolist_to_binary(join(re:split("a","([ab]*?)*",[trim]))), <<"::">> = iolist_to_binary(join(re:split("a","([ab]*?)*",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("a","([ab]*?)*",[]))), - <<"">> = iolist_to_binary(join(re:split("b","([ab]*?)*",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("a","([ab]*?)*",[]))), + <<"">> = iolist_to_binary(join(re:split("b","([ab]*?)*",[trim]))), <<"::">> = iolist_to_binary(join(re:split("b","([ab]*?)*",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("b","([ab]*?)*",[]))), - <<"">> = iolist_to_binary(join(re:split("abab","([ab]*?)*",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("b","([ab]*?)*",[]))), + <<"">> = iolist_to_binary(join(re:split("abab","([ab]*?)*",[trim]))), <<"::bab">> = iolist_to_binary(join(re:split("abab","([ab]*?)*",[{parts, - 2}]))), - <<"::::::::">> = iolist_to_binary(join(re:split("abab","([ab]*?)*",[]))), - <<"">> = iolist_to_binary(join(re:split("baba","([ab]*?)*",[trim]))), + 2}]))), + <<"::::::::">> = iolist_to_binary(join(re:split("abab","([ab]*?)*",[]))), + <<"">> = iolist_to_binary(join(re:split("baba","([ab]*?)*",[trim]))), <<"::aba">> = iolist_to_binary(join(re:split("baba","([ab]*?)*",[{parts, - 2}]))), - <<"::::::::">> = iolist_to_binary(join(re:split("baba","([ab]*?)*",[]))), - <<"">> = iolist_to_binary(join(re:split("b","([^a]*?)*",[trim]))), + 2}]))), + <<"::::::::">> = iolist_to_binary(join(re:split("baba","([ab]*?)*",[]))), + <<"">> = iolist_to_binary(join(re:split("b","([^a]*?)*",[trim]))), <<"::">> = iolist_to_binary(join(re:split("b","([^a]*?)*",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("b","([^a]*?)*",[]))), - <<"">> = iolist_to_binary(join(re:split("bbbb","([^a]*?)*",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("b","([^a]*?)*",[]))), + <<"">> = iolist_to_binary(join(re:split("bbbb","([^a]*?)*",[trim]))), <<"::bbb">> = iolist_to_binary(join(re:split("bbbb","([^a]*?)*",[{parts, - 2}]))), - <<"::::::::">> = iolist_to_binary(join(re:split("bbbb","([^a]*?)*",[]))), - <<"a::a::a">> = iolist_to_binary(join(re:split("aaa","([^a]*?)*",[trim]))), + 2}]))), + <<"::::::::">> = iolist_to_binary(join(re:split("bbbb","([^a]*?)*",[]))), + <<"a::a::a">> = iolist_to_binary(join(re:split("aaa","([^a]*?)*",[trim]))), <<"a::aa">> = iolist_to_binary(join(re:split("aaa","([^a]*?)*",[{parts, - 2}]))), - <<"a::a::a::">> = iolist_to_binary(join(re:split("aaa","([^a]*?)*",[]))), - <<"">> = iolist_to_binary(join(re:split("c","([^ab]*?)*",[trim]))), + 2}]))), + <<"a::a::a::">> = iolist_to_binary(join(re:split("aaa","([^a]*?)*",[]))), + <<"">> = iolist_to_binary(join(re:split("c","([^ab]*?)*",[trim]))), <<"::">> = iolist_to_binary(join(re:split("c","([^ab]*?)*",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("c","([^ab]*?)*",[]))), - <<"">> = iolist_to_binary(join(re:split("cccc","([^ab]*?)*",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("c","([^ab]*?)*",[]))), + <<"">> = iolist_to_binary(join(re:split("cccc","([^ab]*?)*",[trim]))), <<"::ccc">> = iolist_to_binary(join(re:split("cccc","([^ab]*?)*",[{parts, - 2}]))), - <<"::::::::">> = iolist_to_binary(join(re:split("cccc","([^ab]*?)*",[]))), - <<"b::a::b::a">> = iolist_to_binary(join(re:split("baba","([^ab]*?)*",[trim]))), + 2}]))), + <<"::::::::">> = iolist_to_binary(join(re:split("cccc","([^ab]*?)*",[]))), + <<"b::a::b::a">> = iolist_to_binary(join(re:split("baba","([^ab]*?)*",[trim]))), <<"b::aba">> = iolist_to_binary(join(re:split("baba","([^ab]*?)*",[{parts, - 2}]))), - <<"b::a::b::a::">> = iolist_to_binary(join(re:split("baba","([^ab]*?)*",[]))), - <<"">> = iolist_to_binary(join(re:split("a","(?>a*)*",[trim]))), + 2}]))), + <<"b::a::b::a::">> = iolist_to_binary(join(re:split("baba","([^ab]*?)*",[]))), + <<"">> = iolist_to_binary(join(re:split("a","(?>a*)*",[trim]))), <<":">> = iolist_to_binary(join(re:split("a","(?>a*)*",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a","(?>a*)*",[]))), - <<":b:c:d:e">> = iolist_to_binary(join(re:split("aaabcde","(?>a*)*",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a","(?>a*)*",[]))), + <<":b:c:d:e">> = iolist_to_binary(join(re:split("aaabcde","(?>a*)*",[trim]))), <<":bcde">> = iolist_to_binary(join(re:split("aaabcde","(?>a*)*",[{parts, - 2}]))), - <<":b:c:d:e:">> = iolist_to_binary(join(re:split("aaabcde","(?>a*)*",[]))), + 2}]))), + <<":b:c:d:e:">> = iolist_to_binary(join(re:split("aaabcde","(?>a*)*",[]))), ok. run14() -> - <<"">> = iolist_to_binary(join(re:split("aaaaa","((?>a*))*",[trim]))), + <<"">> = iolist_to_binary(join(re:split("aaaaa","((?>a*))*",[trim]))), <<"::">> = iolist_to_binary(join(re:split("aaaaa","((?>a*))*",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("aaaaa","((?>a*))*",[]))), - <<"::b::b">> = iolist_to_binary(join(re:split("aabbaa","((?>a*))*",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("aaaaa","((?>a*))*",[]))), + <<"::b::b">> = iolist_to_binary(join(re:split("aabbaa","((?>a*))*",[trim]))), <<"::bbaa">> = iolist_to_binary(join(re:split("aabbaa","((?>a*))*",[{parts, - 2}]))), - <<"::b::b::">> = iolist_to_binary(join(re:split("aabbaa","((?>a*))*",[]))), - <<"a::a::a::a::a">> = iolist_to_binary(join(re:split("aaaaa","((?>a*?))*",[trim]))), + 2}]))), + <<"::b::b::">> = iolist_to_binary(join(re:split("aabbaa","((?>a*))*",[]))), + <<"a::a::a::a::a">> = iolist_to_binary(join(re:split("aaaaa","((?>a*?))*",[trim]))), <<"a::aaaa">> = iolist_to_binary(join(re:split("aaaaa","((?>a*?))*",[{parts, - 2}]))), - <<"a::a::a::a::a::">> = iolist_to_binary(join(re:split("aaaaa","((?>a*?))*",[]))), - <<"a::a::b::b::a::a">> = iolist_to_binary(join(re:split("aabbaa","((?>a*?))*",[trim]))), + 2}]))), + <<"a::a::a::a::a::">> = iolist_to_binary(join(re:split("aaaaa","((?>a*?))*",[]))), + <<"a::a::b::b::a::a">> = iolist_to_binary(join(re:split("aabbaa","((?>a*?))*",[trim]))), <<"a::abbaa">> = iolist_to_binary(join(re:split("aabbaa","((?>a*?))*",[{parts, - 2}]))), - <<"a::a::b::b::a::a::">> = iolist_to_binary(join(re:split("aabbaa","((?>a*?))*",[]))), + 2}]))), + <<"a::a::b::b::a::a::">> = iolist_to_binary(join(re:split("aabbaa","((?>a*?))*",[]))), <<"">> = iolist_to_binary(join(re:split("12-sep-98","(?(?=[^a-z]+[a-z]) \\d{2}-[a-z]{3}-\\d{2} | \\d{2}-\\d{2}-\\d{2} ) ",[extended, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("12-sep-98","(?(?=[^a-z]+[a-z]) \\d{2}-[a-z]{3}-\\d{2} | \\d{2}-\\d{2}-\\d{2} ) ",[extended, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("12-sep-98","(?(?=[^a-z]+[a-z]) \\d{2}-[a-z]{3}-\\d{2} | \\d{2}-\\d{2}-\\d{2} ) ",[extended]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("12-sep-98","(?(?=[^a-z]+[a-z]) \\d{2}-[a-z]{3}-\\d{2} | \\d{2}-\\d{2}-\\d{2} ) ",[extended]))), <<"">> = iolist_to_binary(join(re:split("12-09-98","(?(?=[^a-z]+[a-z]) \\d{2}-[a-z]{3}-\\d{2} | \\d{2}-\\d{2}-\\d{2} ) ",[extended, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("12-09-98","(?(?=[^a-z]+[a-z]) \\d{2}-[a-z]{3}-\\d{2} | \\d{2}-\\d{2}-\\d{2} ) ",[extended, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("12-09-98","(?(?=[^a-z]+[a-z]) \\d{2}-[a-z]{3}-\\d{2} | \\d{2}-\\d{2}-\\d{2} ) ",[extended]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("12-09-98","(?(?=[^a-z]+[a-z]) \\d{2}-[a-z]{3}-\\d{2} | \\d{2}-\\d{2}-\\d{2} ) ",[extended]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?(?=[^a-z]+[a-z]) \\d{2}-[a-z]{3}-\\d{2} | \\d{2}-\\d{2}-\\d{2} ) ",[extended, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?(?=[^a-z]+[a-z]) \\d{2}-[a-z]{3}-\\d{2} | \\d{2}-\\d{2}-\\d{2} ) ",[extended, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?(?=[^a-z]+[a-z]) \\d{2}-[a-z]{3}-\\d{2} | \\d{2}-\\d{2}-\\d{2} ) ",[extended]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?(?=[^a-z]+[a-z]) \\d{2}-[a-z]{3}-\\d{2} | \\d{2}-\\d{2}-\\d{2} ) ",[extended]))), <<"sep-12-98">> = iolist_to_binary(join(re:split("sep-12-98","(?(?=[^a-z]+[a-z]) \\d{2}-[a-z]{3}-\\d{2} | \\d{2}-\\d{2}-\\d{2} ) ",[extended, - trim]))), + trim]))), <<"sep-12-98">> = iolist_to_binary(join(re:split("sep-12-98","(?(?=[^a-z]+[a-z]) \\d{2}-[a-z]{3}-\\d{2} | \\d{2}-\\d{2}-\\d{2} ) ",[extended, {parts, - 2}]))), - <<"sep-12-98">> = iolist_to_binary(join(re:split("sep-12-98","(?(?=[^a-z]+[a-z]) \\d{2}-[a-z]{3}-\\d{2} | \\d{2}-\\d{2}-\\d{2} ) ",[extended]))), - <<"foo:foo">> = iolist_to_binary(join(re:split("foobarfoo","(?<=(foo))bar\\1",[trim]))), + 2}]))), + <<"sep-12-98">> = iolist_to_binary(join(re:split("sep-12-98","(?(?=[^a-z]+[a-z]) \\d{2}-[a-z]{3}-\\d{2} | \\d{2}-\\d{2}-\\d{2} ) ",[extended]))), + <<"foo:foo">> = iolist_to_binary(join(re:split("foobarfoo","(?<=(foo))bar\\1",[trim]))), <<"foo:foo:">> = iolist_to_binary(join(re:split("foobarfoo","(?<=(foo))bar\\1",[{parts, - 2}]))), - <<"foo:foo:">> = iolist_to_binary(join(re:split("foobarfoo","(?<=(foo))bar\\1",[]))), - <<"foo:foo:tling">> = iolist_to_binary(join(re:split("foobarfootling","(?<=(foo))bar\\1",[trim]))), + 2}]))), + <<"foo:foo:">> = iolist_to_binary(join(re:split("foobarfoo","(?<=(foo))bar\\1",[]))), + <<"foo:foo:tling">> = iolist_to_binary(join(re:split("foobarfootling","(?<=(foo))bar\\1",[trim]))), <<"foo:foo:tling">> = iolist_to_binary(join(re:split("foobarfootling","(?<=(foo))bar\\1",[{parts, - 2}]))), - <<"foo:foo:tling">> = iolist_to_binary(join(re:split("foobarfootling","(?<=(foo))bar\\1",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=(foo))bar\\1",[trim]))), + 2}]))), + <<"foo:foo:tling">> = iolist_to_binary(join(re:split("foobarfootling","(?<=(foo))bar\\1",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=(foo))bar\\1",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=(foo))bar\\1",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=(foo))bar\\1",[]))), - <<"foobar">> = iolist_to_binary(join(re:split("foobar","(?<=(foo))bar\\1",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=(foo))bar\\1",[]))), + <<"foobar">> = iolist_to_binary(join(re:split("foobar","(?<=(foo))bar\\1",[trim]))), <<"foobar">> = iolist_to_binary(join(re:split("foobar","(?<=(foo))bar\\1",[{parts, - 2}]))), - <<"foobar">> = iolist_to_binary(join(re:split("foobar","(?<=(foo))bar\\1",[]))), - <<"barfoo">> = iolist_to_binary(join(re:split("barfoo","(?<=(foo))bar\\1",[trim]))), + 2}]))), + <<"foobar">> = iolist_to_binary(join(re:split("foobar","(?<=(foo))bar\\1",[]))), + <<"barfoo">> = iolist_to_binary(join(re:split("barfoo","(?<=(foo))bar\\1",[trim]))), <<"barfoo">> = iolist_to_binary(join(re:split("barfoo","(?<=(foo))bar\\1",[{parts, - 2}]))), - <<"barfoo">> = iolist_to_binary(join(re:split("barfoo","(?<=(foo))bar\\1",[]))), - <<"">> = iolist_to_binary(join(re:split("saturday","(?i:saturday|sunday)",[trim]))), + 2}]))), + <<"barfoo">> = iolist_to_binary(join(re:split("barfoo","(?<=(foo))bar\\1",[]))), + <<"">> = iolist_to_binary(join(re:split("saturday","(?i:saturday|sunday)",[trim]))), <<":">> = iolist_to_binary(join(re:split("saturday","(?i:saturday|sunday)",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("saturday","(?i:saturday|sunday)",[]))), - <<"">> = iolist_to_binary(join(re:split("sunday","(?i:saturday|sunday)",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("saturday","(?i:saturday|sunday)",[]))), + <<"">> = iolist_to_binary(join(re:split("sunday","(?i:saturday|sunday)",[trim]))), <<":">> = iolist_to_binary(join(re:split("sunday","(?i:saturday|sunday)",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("sunday","(?i:saturday|sunday)",[]))), - <<"">> = iolist_to_binary(join(re:split("Saturday","(?i:saturday|sunday)",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("sunday","(?i:saturday|sunday)",[]))), + <<"">> = iolist_to_binary(join(re:split("Saturday","(?i:saturday|sunday)",[trim]))), <<":">> = iolist_to_binary(join(re:split("Saturday","(?i:saturday|sunday)",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("Saturday","(?i:saturday|sunday)",[]))), - <<"">> = iolist_to_binary(join(re:split("Sunday","(?i:saturday|sunday)",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("Saturday","(?i:saturday|sunday)",[]))), + <<"">> = iolist_to_binary(join(re:split("Sunday","(?i:saturday|sunday)",[trim]))), <<":">> = iolist_to_binary(join(re:split("Sunday","(?i:saturday|sunday)",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("Sunday","(?i:saturday|sunday)",[]))), - <<"">> = iolist_to_binary(join(re:split("SATURDAY","(?i:saturday|sunday)",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("Sunday","(?i:saturday|sunday)",[]))), + <<"">> = iolist_to_binary(join(re:split("SATURDAY","(?i:saturday|sunday)",[trim]))), <<":">> = iolist_to_binary(join(re:split("SATURDAY","(?i:saturday|sunday)",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("SATURDAY","(?i:saturday|sunday)",[]))), - <<"">> = iolist_to_binary(join(re:split("SUNDAY","(?i:saturday|sunday)",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("SATURDAY","(?i:saturday|sunday)",[]))), + <<"">> = iolist_to_binary(join(re:split("SUNDAY","(?i:saturday|sunday)",[trim]))), <<":">> = iolist_to_binary(join(re:split("SUNDAY","(?i:saturday|sunday)",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("SUNDAY","(?i:saturday|sunday)",[]))), - <<"">> = iolist_to_binary(join(re:split("SunDay","(?i:saturday|sunday)",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("SUNDAY","(?i:saturday|sunday)",[]))), + <<"">> = iolist_to_binary(join(re:split("SunDay","(?i:saturday|sunday)",[trim]))), <<":">> = iolist_to_binary(join(re:split("SunDay","(?i:saturday|sunday)",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("SunDay","(?i:saturday|sunday)",[]))), - <<":abc">> = iolist_to_binary(join(re:split("abcx","(a(?i)bc|BB)x",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("SunDay","(?i:saturday|sunday)",[]))), + <<":abc">> = iolist_to_binary(join(re:split("abcx","(a(?i)bc|BB)x",[trim]))), <<":abc:">> = iolist_to_binary(join(re:split("abcx","(a(?i)bc|BB)x",[{parts, - 2}]))), - <<":abc:">> = iolist_to_binary(join(re:split("abcx","(a(?i)bc|BB)x",[]))), - <<":aBC">> = iolist_to_binary(join(re:split("aBCx","(a(?i)bc|BB)x",[trim]))), + 2}]))), + <<":abc:">> = iolist_to_binary(join(re:split("abcx","(a(?i)bc|BB)x",[]))), + <<":aBC">> = iolist_to_binary(join(re:split("aBCx","(a(?i)bc|BB)x",[trim]))), <<":aBC:">> = iolist_to_binary(join(re:split("aBCx","(a(?i)bc|BB)x",[{parts, - 2}]))), - <<":aBC:">> = iolist_to_binary(join(re:split("aBCx","(a(?i)bc|BB)x",[]))), - <<":bb">> = iolist_to_binary(join(re:split("bbx","(a(?i)bc|BB)x",[trim]))), + 2}]))), + <<":aBC:">> = iolist_to_binary(join(re:split("aBCx","(a(?i)bc|BB)x",[]))), + <<":bb">> = iolist_to_binary(join(re:split("bbx","(a(?i)bc|BB)x",[trim]))), <<":bb:">> = iolist_to_binary(join(re:split("bbx","(a(?i)bc|BB)x",[{parts, - 2}]))), - <<":bb:">> = iolist_to_binary(join(re:split("bbx","(a(?i)bc|BB)x",[]))), - <<":BB">> = iolist_to_binary(join(re:split("BBx","(a(?i)bc|BB)x",[trim]))), + 2}]))), + <<":bb:">> = iolist_to_binary(join(re:split("bbx","(a(?i)bc|BB)x",[]))), + <<":BB">> = iolist_to_binary(join(re:split("BBx","(a(?i)bc|BB)x",[trim]))), <<":BB:">> = iolist_to_binary(join(re:split("BBx","(a(?i)bc|BB)x",[{parts, - 2}]))), - <<":BB:">> = iolist_to_binary(join(re:split("BBx","(a(?i)bc|BB)x",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a(?i)bc|BB)x",[trim]))), + 2}]))), + <<":BB:">> = iolist_to_binary(join(re:split("BBx","(a(?i)bc|BB)x",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a(?i)bc|BB)x",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a(?i)bc|BB)x",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a(?i)bc|BB)x",[]))), - <<"abcX">> = iolist_to_binary(join(re:split("abcX","(a(?i)bc|BB)x",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a(?i)bc|BB)x",[]))), + <<"abcX">> = iolist_to_binary(join(re:split("abcX","(a(?i)bc|BB)x",[trim]))), <<"abcX">> = iolist_to_binary(join(re:split("abcX","(a(?i)bc|BB)x",[{parts, - 2}]))), - <<"abcX">> = iolist_to_binary(join(re:split("abcX","(a(?i)bc|BB)x",[]))), - <<"aBCX">> = iolist_to_binary(join(re:split("aBCX","(a(?i)bc|BB)x",[trim]))), + 2}]))), + <<"abcX">> = iolist_to_binary(join(re:split("abcX","(a(?i)bc|BB)x",[]))), + <<"aBCX">> = iolist_to_binary(join(re:split("aBCX","(a(?i)bc|BB)x",[trim]))), <<"aBCX">> = iolist_to_binary(join(re:split("aBCX","(a(?i)bc|BB)x",[{parts, - 2}]))), - <<"aBCX">> = iolist_to_binary(join(re:split("aBCX","(a(?i)bc|BB)x",[]))), - <<"bbX">> = iolist_to_binary(join(re:split("bbX","(a(?i)bc|BB)x",[trim]))), + 2}]))), + <<"aBCX">> = iolist_to_binary(join(re:split("aBCX","(a(?i)bc|BB)x",[]))), + <<"bbX">> = iolist_to_binary(join(re:split("bbX","(a(?i)bc|BB)x",[trim]))), <<"bbX">> = iolist_to_binary(join(re:split("bbX","(a(?i)bc|BB)x",[{parts, - 2}]))), - <<"bbX">> = iolist_to_binary(join(re:split("bbX","(a(?i)bc|BB)x",[]))), - <<"BBX">> = iolist_to_binary(join(re:split("BBX","(a(?i)bc|BB)x",[trim]))), + 2}]))), + <<"bbX">> = iolist_to_binary(join(re:split("bbX","(a(?i)bc|BB)x",[]))), + <<"BBX">> = iolist_to_binary(join(re:split("BBX","(a(?i)bc|BB)x",[trim]))), <<"BBX">> = iolist_to_binary(join(re:split("BBX","(a(?i)bc|BB)x",[{parts, - 2}]))), - <<"BBX">> = iolist_to_binary(join(re:split("BBX","(a(?i)bc|BB)x",[]))), - <<":ac">> = iolist_to_binary(join(re:split("ac","^([ab](?i)[cd]|[ef])",[trim]))), + 2}]))), + <<"BBX">> = iolist_to_binary(join(re:split("BBX","(a(?i)bc|BB)x",[]))), + <<":ac">> = iolist_to_binary(join(re:split("ac","^([ab](?i)[cd]|[ef])",[trim]))), <<":ac:">> = iolist_to_binary(join(re:split("ac","^([ab](?i)[cd]|[ef])",[{parts, - 2}]))), - <<":ac:">> = iolist_to_binary(join(re:split("ac","^([ab](?i)[cd]|[ef])",[]))), - <<":aC">> = iolist_to_binary(join(re:split("aC","^([ab](?i)[cd]|[ef])",[trim]))), + 2}]))), + <<":ac:">> = iolist_to_binary(join(re:split("ac","^([ab](?i)[cd]|[ef])",[]))), + <<":aC">> = iolist_to_binary(join(re:split("aC","^([ab](?i)[cd]|[ef])",[trim]))), <<":aC:">> = iolist_to_binary(join(re:split("aC","^([ab](?i)[cd]|[ef])",[{parts, - 2}]))), - <<":aC:">> = iolist_to_binary(join(re:split("aC","^([ab](?i)[cd]|[ef])",[]))), - <<":bD">> = iolist_to_binary(join(re:split("bD","^([ab](?i)[cd]|[ef])",[trim]))), + 2}]))), + <<":aC:">> = iolist_to_binary(join(re:split("aC","^([ab](?i)[cd]|[ef])",[]))), + <<":bD">> = iolist_to_binary(join(re:split("bD","^([ab](?i)[cd]|[ef])",[trim]))), <<":bD:">> = iolist_to_binary(join(re:split("bD","^([ab](?i)[cd]|[ef])",[{parts, - 2}]))), - <<":bD:">> = iolist_to_binary(join(re:split("bD","^([ab](?i)[cd]|[ef])",[]))), - <<":e:lephant">> = iolist_to_binary(join(re:split("elephant","^([ab](?i)[cd]|[ef])",[trim]))), + 2}]))), + <<":bD:">> = iolist_to_binary(join(re:split("bD","^([ab](?i)[cd]|[ef])",[]))), + <<":e:lephant">> = iolist_to_binary(join(re:split("elephant","^([ab](?i)[cd]|[ef])",[trim]))), <<":e:lephant">> = iolist_to_binary(join(re:split("elephant","^([ab](?i)[cd]|[ef])",[{parts, - 2}]))), - <<":e:lephant">> = iolist_to_binary(join(re:split("elephant","^([ab](?i)[cd]|[ef])",[]))), - <<":E:urope">> = iolist_to_binary(join(re:split("Europe","^([ab](?i)[cd]|[ef])",[trim]))), + 2}]))), + <<":e:lephant">> = iolist_to_binary(join(re:split("elephant","^([ab](?i)[cd]|[ef])",[]))), + <<":E:urope">> = iolist_to_binary(join(re:split("Europe","^([ab](?i)[cd]|[ef])",[trim]))), <<":E:urope">> = iolist_to_binary(join(re:split("Europe","^([ab](?i)[cd]|[ef])",[{parts, - 2}]))), - <<":E:urope">> = iolist_to_binary(join(re:split("Europe","^([ab](?i)[cd]|[ef])",[]))), - <<":f:rog">> = iolist_to_binary(join(re:split("frog","^([ab](?i)[cd]|[ef])",[trim]))), + 2}]))), + <<":E:urope">> = iolist_to_binary(join(re:split("Europe","^([ab](?i)[cd]|[ef])",[]))), + <<":f:rog">> = iolist_to_binary(join(re:split("frog","^([ab](?i)[cd]|[ef])",[trim]))), <<":f:rog">> = iolist_to_binary(join(re:split("frog","^([ab](?i)[cd]|[ef])",[{parts, - 2}]))), - <<":f:rog">> = iolist_to_binary(join(re:split("frog","^([ab](?i)[cd]|[ef])",[]))), - <<":F:rance">> = iolist_to_binary(join(re:split("France","^([ab](?i)[cd]|[ef])",[trim]))), + 2}]))), + <<":f:rog">> = iolist_to_binary(join(re:split("frog","^([ab](?i)[cd]|[ef])",[]))), + <<":F:rance">> = iolist_to_binary(join(re:split("France","^([ab](?i)[cd]|[ef])",[trim]))), <<":F:rance">> = iolist_to_binary(join(re:split("France","^([ab](?i)[cd]|[ef])",[{parts, - 2}]))), - <<":F:rance">> = iolist_to_binary(join(re:split("France","^([ab](?i)[cd]|[ef])",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^([ab](?i)[cd]|[ef])",[trim]))), + 2}]))), + <<":F:rance">> = iolist_to_binary(join(re:split("France","^([ab](?i)[cd]|[ef])",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^([ab](?i)[cd]|[ef])",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^([ab](?i)[cd]|[ef])",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^([ab](?i)[cd]|[ef])",[]))), - <<"Africa">> = iolist_to_binary(join(re:split("Africa","^([ab](?i)[cd]|[ef])",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^([ab](?i)[cd]|[ef])",[]))), + <<"Africa">> = iolist_to_binary(join(re:split("Africa","^([ab](?i)[cd]|[ef])",[trim]))), <<"Africa">> = iolist_to_binary(join(re:split("Africa","^([ab](?i)[cd]|[ef])",[{parts, - 2}]))), - <<"Africa">> = iolist_to_binary(join(re:split("Africa","^([ab](?i)[cd]|[ef])",[]))), - <<":ab">> = iolist_to_binary(join(re:split("ab","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[trim]))), + 2}]))), + <<"Africa">> = iolist_to_binary(join(re:split("Africa","^([ab](?i)[cd]|[ef])",[]))), + <<":ab">> = iolist_to_binary(join(re:split("ab","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[trim]))), <<":ab:">> = iolist_to_binary(join(re:split("ab","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[{parts, - 2}]))), - <<":ab:">> = iolist_to_binary(join(re:split("ab","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[]))), - <<":aBd">> = iolist_to_binary(join(re:split("aBd","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[trim]))), + 2}]))), + <<":ab:">> = iolist_to_binary(join(re:split("ab","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[]))), + <<":aBd">> = iolist_to_binary(join(re:split("aBd","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[trim]))), <<":aBd:">> = iolist_to_binary(join(re:split("aBd","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[{parts, - 2}]))), - <<":aBd:">> = iolist_to_binary(join(re:split("aBd","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[]))), - <<":xy">> = iolist_to_binary(join(re:split("xy","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[trim]))), + 2}]))), + <<":aBd:">> = iolist_to_binary(join(re:split("aBd","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[]))), + <<":xy">> = iolist_to_binary(join(re:split("xy","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[trim]))), <<":xy:">> = iolist_to_binary(join(re:split("xy","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[{parts, - 2}]))), - <<":xy:">> = iolist_to_binary(join(re:split("xy","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[]))), - <<":xY">> = iolist_to_binary(join(re:split("xY","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[trim]))), + 2}]))), + <<":xy:">> = iolist_to_binary(join(re:split("xy","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[]))), + <<":xY">> = iolist_to_binary(join(re:split("xY","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[trim]))), <<":xY:">> = iolist_to_binary(join(re:split("xY","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[{parts, - 2}]))), - <<":xY:">> = iolist_to_binary(join(re:split("xY","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[]))), - <<":z:ebra">> = iolist_to_binary(join(re:split("zebra","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[trim]))), + 2}]))), + <<":xY:">> = iolist_to_binary(join(re:split("xY","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[]))), + <<":z:ebra">> = iolist_to_binary(join(re:split("zebra","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[trim]))), <<":z:ebra">> = iolist_to_binary(join(re:split("zebra","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[{parts, - 2}]))), - <<":z:ebra">> = iolist_to_binary(join(re:split("zebra","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[]))), - <<":Z:ambesi">> = iolist_to_binary(join(re:split("Zambesi","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[trim]))), + 2}]))), + <<":z:ebra">> = iolist_to_binary(join(re:split("zebra","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[]))), + <<":Z:ambesi">> = iolist_to_binary(join(re:split("Zambesi","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[trim]))), <<":Z:ambesi">> = iolist_to_binary(join(re:split("Zambesi","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[{parts, - 2}]))), - <<":Z:ambesi">> = iolist_to_binary(join(re:split("Zambesi","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[trim]))), + 2}]))), + <<":Z:ambesi">> = iolist_to_binary(join(re:split("Zambesi","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[]))), - <<"aCD">> = iolist_to_binary(join(re:split("aCD","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[]))), + <<"aCD">> = iolist_to_binary(join(re:split("aCD","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[trim]))), <<"aCD">> = iolist_to_binary(join(re:split("aCD","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[{parts, - 2}]))), - <<"aCD">> = iolist_to_binary(join(re:split("aCD","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[]))), - <<"XY">> = iolist_to_binary(join(re:split("XY","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[trim]))), + 2}]))), + <<"aCD">> = iolist_to_binary(join(re:split("aCD","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[]))), + <<"XY">> = iolist_to_binary(join(re:split("XY","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[trim]))), <<"XY">> = iolist_to_binary(join(re:split("XY","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[{parts, - 2}]))), - <<"XY">> = iolist_to_binary(join(re:split("XY","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[]))), + 2}]))), + <<"XY">> = iolist_to_binary(join(re:split("XY","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[]))), <<"foo ">> = iolist_to_binary(join(re:split("foo -bar","(?<=foo\\n)^bar",[multiline,trim]))), +bar","(?<=foo\\n)^bar",[multiline,trim]))), <<"foo :">> = iolist_to_binary(join(re:split("foo -bar","(?<=foo\\n)^bar",[multiline,{parts,2}]))), +bar","(?<=foo\\n)^bar",[multiline,{parts,2}]))), <<"foo :">> = iolist_to_binary(join(re:split("foo -bar","(?<=foo\\n)^bar",[multiline]))), +bar","(?<=foo\\n)^bar",[multiline]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=foo\\n)^bar",[multiline, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=foo\\n)^bar",[multiline, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=foo\\n)^bar",[multiline]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=foo\\n)^bar",[multiline]))), <<"bar">> = iolist_to_binary(join(re:split("bar","(?<=foo\\n)^bar",[multiline, - trim]))), + trim]))), <<"bar">> = iolist_to_binary(join(re:split("bar","(?<=foo\\n)^bar",[multiline, {parts, - 2}]))), - <<"bar">> = iolist_to_binary(join(re:split("bar","(?<=foo\\n)^bar",[multiline]))), + 2}]))), + <<"bar">> = iolist_to_binary(join(re:split("bar","(?<=foo\\n)^bar",[multiline]))), <<"baz bar">> = iolist_to_binary(join(re:split("baz -bar","(?<=foo\\n)^bar",[multiline,trim]))), +bar","(?<=foo\\n)^bar",[multiline,trim]))), <<"baz bar">> = iolist_to_binary(join(re:split("baz -bar","(?<=foo\\n)^bar",[multiline,{parts,2}]))), +bar","(?<=foo\\n)^bar",[multiline,{parts,2}]))), <<"baz bar">> = iolist_to_binary(join(re:split("baz -bar","(?<=foo\\n)^bar",[multiline]))), - <<"bar">> = iolist_to_binary(join(re:split("barbaz","(?<=(?<!foo)bar)baz",[trim]))), +bar","(?<=foo\\n)^bar",[multiline]))), + <<"bar">> = iolist_to_binary(join(re:split("barbaz","(?<=(?<!foo)bar)baz",[trim]))), <<"bar:">> = iolist_to_binary(join(re:split("barbaz","(?<=(?<!foo)bar)baz",[{parts, - 2}]))), - <<"bar:">> = iolist_to_binary(join(re:split("barbaz","(?<=(?<!foo)bar)baz",[]))), - <<"barbar">> = iolist_to_binary(join(re:split("barbarbaz","(?<=(?<!foo)bar)baz",[trim]))), + 2}]))), + <<"bar:">> = iolist_to_binary(join(re:split("barbaz","(?<=(?<!foo)bar)baz",[]))), + <<"barbar">> = iolist_to_binary(join(re:split("barbarbaz","(?<=(?<!foo)bar)baz",[trim]))), <<"barbar:">> = iolist_to_binary(join(re:split("barbarbaz","(?<=(?<!foo)bar)baz",[{parts, - 2}]))), - <<"barbar:">> = iolist_to_binary(join(re:split("barbarbaz","(?<=(?<!foo)bar)baz",[]))), - <<"koobar">> = iolist_to_binary(join(re:split("koobarbaz","(?<=(?<!foo)bar)baz",[trim]))), + 2}]))), + <<"barbar:">> = iolist_to_binary(join(re:split("barbarbaz","(?<=(?<!foo)bar)baz",[]))), + <<"koobar">> = iolist_to_binary(join(re:split("koobarbaz","(?<=(?<!foo)bar)baz",[trim]))), <<"koobar:">> = iolist_to_binary(join(re:split("koobarbaz","(?<=(?<!foo)bar)baz",[{parts, - 2}]))), - <<"koobar:">> = iolist_to_binary(join(re:split("koobarbaz","(?<=(?<!foo)bar)baz",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=(?<!foo)bar)baz",[trim]))), + 2}]))), + <<"koobar:">> = iolist_to_binary(join(re:split("koobarbaz","(?<=(?<!foo)bar)baz",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=(?<!foo)bar)baz",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=(?<!foo)bar)baz",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=(?<!foo)bar)baz",[]))), - <<"baz">> = iolist_to_binary(join(re:split("baz","(?<=(?<!foo)bar)baz",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=(?<!foo)bar)baz",[]))), + <<"baz">> = iolist_to_binary(join(re:split("baz","(?<=(?<!foo)bar)baz",[trim]))), <<"baz">> = iolist_to_binary(join(re:split("baz","(?<=(?<!foo)bar)baz",[{parts, - 2}]))), - <<"baz">> = iolist_to_binary(join(re:split("baz","(?<=(?<!foo)bar)baz",[]))), - <<"foobarbaz">> = iolist_to_binary(join(re:split("foobarbaz","(?<=(?<!foo)bar)baz",[trim]))), + 2}]))), + <<"baz">> = iolist_to_binary(join(re:split("baz","(?<=(?<!foo)bar)baz",[]))), + <<"foobarbaz">> = iolist_to_binary(join(re:split("foobarbaz","(?<=(?<!foo)bar)baz",[trim]))), <<"foobarbaz">> = iolist_to_binary(join(re:split("foobarbaz","(?<=(?<!foo)bar)baz",[{parts, - 2}]))), - <<"foobarbaz">> = iolist_to_binary(join(re:split("foobarbaz","(?<=(?<!foo)bar)baz",[]))), - <<"a">> = iolist_to_binary(join(re:split("a","^(a\\1?){4}$",[trim]))), + 2}]))), + <<"foobarbaz">> = iolist_to_binary(join(re:split("foobarbaz","(?<=(?<!foo)bar)baz",[]))), + <<"a">> = iolist_to_binary(join(re:split("a","^(a\\1?){4}$",[trim]))), <<"a">> = iolist_to_binary(join(re:split("a","^(a\\1?){4}$",[{parts, - 2}]))), - <<"a">> = iolist_to_binary(join(re:split("a","^(a\\1?){4}$",[]))), - <<"aa">> = iolist_to_binary(join(re:split("aa","^(a\\1?){4}$",[trim]))), + 2}]))), + <<"a">> = iolist_to_binary(join(re:split("a","^(a\\1?){4}$",[]))), + <<"aa">> = iolist_to_binary(join(re:split("aa","^(a\\1?){4}$",[trim]))), <<"aa">> = iolist_to_binary(join(re:split("aa","^(a\\1?){4}$",[{parts, - 2}]))), - <<"aa">> = iolist_to_binary(join(re:split("aa","^(a\\1?){4}$",[]))), - <<"aaa">> = iolist_to_binary(join(re:split("aaa","^(a\\1?){4}$",[trim]))), + 2}]))), + <<"aa">> = iolist_to_binary(join(re:split("aa","^(a\\1?){4}$",[]))), + <<"aaa">> = iolist_to_binary(join(re:split("aaa","^(a\\1?){4}$",[trim]))), <<"aaa">> = iolist_to_binary(join(re:split("aaa","^(a\\1?){4}$",[{parts, - 2}]))), - <<"aaa">> = iolist_to_binary(join(re:split("aaa","^(a\\1?){4}$",[]))), - <<":a">> = iolist_to_binary(join(re:split("aaaaa","^(a\\1?){4}$",[trim]))), + 2}]))), + <<"aaa">> = iolist_to_binary(join(re:split("aaa","^(a\\1?){4}$",[]))), + <<":a">> = iolist_to_binary(join(re:split("aaaaa","^(a\\1?){4}$",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aaaaa","^(a\\1?){4}$",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aaaaa","^(a\\1?){4}$",[]))), - <<":a">> = iolist_to_binary(join(re:split("aaaaaaa","^(a\\1?){4}$",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aaaaa","^(a\\1?){4}$",[]))), + <<":a">> = iolist_to_binary(join(re:split("aaaaaaa","^(a\\1?){4}$",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aaaaaaa","^(a\\1?){4}$",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aaaaaaa","^(a\\1?){4}$",[]))), - <<"aaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaa","^(a\\1?){4}$",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aaaaaaa","^(a\\1?){4}$",[]))), + <<"aaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaa","^(a\\1?){4}$",[trim]))), <<"aaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaa","^(a\\1?){4}$",[{parts, - 2}]))), - <<"aaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaa","^(a\\1?){4}$",[]))), - <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a\\1?){4}$",[trim]))), + 2}]))), + <<"aaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaa","^(a\\1?){4}$",[]))), + <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a\\1?){4}$",[trim]))), <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a\\1?){4}$",[{parts, - 2}]))), - <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a\\1?){4}$",[]))), - <<":aaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaa","^(a\\1?){4}$",[trim]))), + 2}]))), + <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a\\1?){4}$",[]))), + <<":aaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaa","^(a\\1?){4}$",[trim]))), <<":aaaa:">> = iolist_to_binary(join(re:split("aaaaaaaaaa","^(a\\1?){4}$",[{parts, - 2}]))), - <<":aaaa:">> = iolist_to_binary(join(re:split("aaaaaaaaaa","^(a\\1?){4}$",[]))), - <<"aaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaa","^(a\\1?){4}$",[trim]))), + 2}]))), + <<":aaaa:">> = iolist_to_binary(join(re:split("aaaaaaaaaa","^(a\\1?){4}$",[]))), + <<"aaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaa","^(a\\1?){4}$",[trim]))), <<"aaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaa","^(a\\1?){4}$",[{parts, - 2}]))), - <<"aaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaa","^(a\\1?){4}$",[]))), - <<"aaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaa","^(a\\1?){4}$",[trim]))), + 2}]))), + <<"aaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaa","^(a\\1?){4}$",[]))), + <<"aaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaa","^(a\\1?){4}$",[trim]))), <<"aaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaa","^(a\\1?){4}$",[{parts, - 2}]))), - <<"aaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaa","^(a\\1?){4}$",[]))), - <<"aaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaa","^(a\\1?){4}$",[trim]))), + 2}]))), + <<"aaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaa","^(a\\1?){4}$",[]))), + <<"aaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaa","^(a\\1?){4}$",[trim]))), <<"aaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaa","^(a\\1?){4}$",[{parts, - 2}]))), - <<"aaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaa","^(a\\1?){4}$",[]))), - <<"aaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaa","^(a\\1?){4}$",[trim]))), + 2}]))), + <<"aaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaa","^(a\\1?){4}$",[]))), + <<"aaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaa","^(a\\1?){4}$",[trim]))), <<"aaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaa","^(a\\1?){4}$",[{parts, - 2}]))), - <<"aaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaa","^(a\\1?){4}$",[]))), - <<"aaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaa","^(a\\1?){4}$",[trim]))), + 2}]))), + <<"aaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaa","^(a\\1?){4}$",[]))), + <<"aaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaa","^(a\\1?){4}$",[trim]))), <<"aaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaa","^(a\\1?){4}$",[{parts, - 2}]))), - <<"aaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaa","^(a\\1?){4}$",[]))), - <<"aaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaa","^(a\\1?){4}$",[trim]))), + 2}]))), + <<"aaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaa","^(a\\1?){4}$",[]))), + <<"aaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaa","^(a\\1?){4}$",[trim]))), <<"aaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaa","^(a\\1?){4}$",[{parts, - 2}]))), - <<"aaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaa","^(a\\1?){4}$",[]))), - <<"a">> = iolist_to_binary(join(re:split("a","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), + 2}]))), + <<"aaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaa","^(a\\1?){4}$",[]))), + <<"a">> = iolist_to_binary(join(re:split("a","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), <<"a">> = iolist_to_binary(join(re:split("a","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[{parts, - 2}]))), - <<"a">> = iolist_to_binary(join(re:split("a","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), - <<"aa">> = iolist_to_binary(join(re:split("aa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), + 2}]))), + <<"a">> = iolist_to_binary(join(re:split("a","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), + <<"aa">> = iolist_to_binary(join(re:split("aa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), <<"aa">> = iolist_to_binary(join(re:split("aa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[{parts, - 2}]))), - <<"aa">> = iolist_to_binary(join(re:split("aa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), - <<"aaa">> = iolist_to_binary(join(re:split("aaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), + 2}]))), + <<"aa">> = iolist_to_binary(join(re:split("aa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), + <<"aaa">> = iolist_to_binary(join(re:split("aaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), <<"aaa">> = iolist_to_binary(join(re:split("aaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[{parts, - 2}]))), - <<"aaa">> = iolist_to_binary(join(re:split("aaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), - <<":a:a:a:a">> = iolist_to_binary(join(re:split("aaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), + 2}]))), + <<"aaa">> = iolist_to_binary(join(re:split("aaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), + <<":a:a:a:a">> = iolist_to_binary(join(re:split("aaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), <<":a:a:a:a:">> = iolist_to_binary(join(re:split("aaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[{parts, - 2}]))), - <<":a:a:a:a:">> = iolist_to_binary(join(re:split("aaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), - <<":a:aa:a:a">> = iolist_to_binary(join(re:split("aaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), + 2}]))), + <<":a:a:a:a:">> = iolist_to_binary(join(re:split("aaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), + <<":a:aa:a:a">> = iolist_to_binary(join(re:split("aaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), <<":a:aa:a:a:">> = iolist_to_binary(join(re:split("aaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[{parts, - 2}]))), - <<":a:aa:a:a:">> = iolist_to_binary(join(re:split("aaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), - <<":a:aa:a:aa">> = iolist_to_binary(join(re:split("aaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), + 2}]))), + <<":a:aa:a:a:">> = iolist_to_binary(join(re:split("aaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), + <<":a:aa:a:aa">> = iolist_to_binary(join(re:split("aaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), <<":a:aa:a:aa:">> = iolist_to_binary(join(re:split("aaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[{parts, - 2}]))), - <<":a:aa:a:aa:">> = iolist_to_binary(join(re:split("aaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), - <<":a:aa:aaa:a">> = iolist_to_binary(join(re:split("aaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), + 2}]))), + <<":a:aa:a:aa:">> = iolist_to_binary(join(re:split("aaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), + <<":a:aa:aaa:a">> = iolist_to_binary(join(re:split("aaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), <<":a:aa:aaa:a:">> = iolist_to_binary(join(re:split("aaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[{parts, - 2}]))), - <<":a:aa:aaa:a:">> = iolist_to_binary(join(re:split("aaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), - <<"aaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), + 2}]))), + <<":a:aa:aaa:a:">> = iolist_to_binary(join(re:split("aaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), + <<"aaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), <<"aaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[{parts, - 2}]))), - <<"aaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), - <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), + 2}]))), + <<"aaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), + <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[{parts, - 2}]))), - <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), - <<":a:aa:aaa:aaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), + 2}]))), + <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), + <<":a:aa:aaa:aaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), <<":a:aa:aaa:aaaa:">> = iolist_to_binary(join(re:split("aaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[{parts, - 2}]))), - <<":a:aa:aaa:aaaa:">> = iolist_to_binary(join(re:split("aaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), - <<"aaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), + 2}]))), + <<":a:aa:aaa:aaaa:">> = iolist_to_binary(join(re:split("aaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), + <<"aaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), <<"aaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[{parts, - 2}]))), - <<"aaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), - <<"aaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), + 2}]))), + <<"aaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), + <<"aaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), <<"aaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[{parts, - 2}]))), - <<"aaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), - <<"aaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), + 2}]))), + <<"aaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), + <<"aaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), <<"aaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[{parts, - 2}]))), - <<"aaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), - <<"aaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), + 2}]))), + <<"aaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), + <<"aaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), <<"aaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[{parts, - 2}]))), - <<"aaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), - <<"aaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), + 2}]))), + <<"aaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), + <<"aaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), <<"aaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[{parts, - 2}]))), - <<"aaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), - <<"aaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), + 2}]))), + <<"aaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), + <<"aaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), <<"aaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[{parts, - 2}]))), - <<"aaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","abc",[trim]))), + 2}]))), + <<"aaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","abc",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","abc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","abc",[]))), - <<"x:y">> = iolist_to_binary(join(re:split("xabcy","abc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","abc",[]))), + <<"x:y">> = iolist_to_binary(join(re:split("xabcy","abc",[trim]))), <<"x:y">> = iolist_to_binary(join(re:split("xabcy","abc",[{parts, - 2}]))), - <<"x:y">> = iolist_to_binary(join(re:split("xabcy","abc",[]))), - <<"ab">> = iolist_to_binary(join(re:split("ababc","abc",[trim]))), + 2}]))), + <<"x:y">> = iolist_to_binary(join(re:split("xabcy","abc",[]))), + <<"ab">> = iolist_to_binary(join(re:split("ababc","abc",[trim]))), <<"ab:">> = iolist_to_binary(join(re:split("ababc","abc",[{parts, - 2}]))), - <<"ab:">> = iolist_to_binary(join(re:split("ababc","abc",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc",[trim]))), + 2}]))), + <<"ab:">> = iolist_to_binary(join(re:split("ababc","abc",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc",[]))), - <<"xbc">> = iolist_to_binary(join(re:split("xbc","abc",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc",[]))), + <<"xbc">> = iolist_to_binary(join(re:split("xbc","abc",[trim]))), <<"xbc">> = iolist_to_binary(join(re:split("xbc","abc",[{parts, - 2}]))), - <<"xbc">> = iolist_to_binary(join(re:split("xbc","abc",[]))), - <<"axc">> = iolist_to_binary(join(re:split("axc","abc",[trim]))), + 2}]))), + <<"xbc">> = iolist_to_binary(join(re:split("xbc","abc",[]))), + <<"axc">> = iolist_to_binary(join(re:split("axc","abc",[trim]))), <<"axc">> = iolist_to_binary(join(re:split("axc","abc",[{parts, - 2}]))), - <<"axc">> = iolist_to_binary(join(re:split("axc","abc",[]))), - <<"abx">> = iolist_to_binary(join(re:split("abx","abc",[trim]))), + 2}]))), + <<"axc">> = iolist_to_binary(join(re:split("axc","abc",[]))), + <<"abx">> = iolist_to_binary(join(re:split("abx","abc",[trim]))), <<"abx">> = iolist_to_binary(join(re:split("abx","abc",[{parts, - 2}]))), - <<"abx">> = iolist_to_binary(join(re:split("abx","abc",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","ab*c",[trim]))), + 2}]))), + <<"abx">> = iolist_to_binary(join(re:split("abx","abc",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","ab*c",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","ab*c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","ab*c",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","ab*bc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","ab*c",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","ab*bc",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","ab*bc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","ab*bc",[]))), - <<"">> = iolist_to_binary(join(re:split("abbc","ab*bc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","ab*bc",[]))), + <<"">> = iolist_to_binary(join(re:split("abbc","ab*bc",[trim]))), <<":">> = iolist_to_binary(join(re:split("abbc","ab*bc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abbc","ab*bc",[]))), - <<"">> = iolist_to_binary(join(re:split("abbbbc","ab*bc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abbc","ab*bc",[]))), + <<"">> = iolist_to_binary(join(re:split("abbbbc","ab*bc",[trim]))), <<":">> = iolist_to_binary(join(re:split("abbbbc","ab*bc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abbbbc","ab*bc",[]))), - <<"">> = iolist_to_binary(join(re:split("abbbbc",".{1}",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abbbbc","ab*bc",[]))), + <<"">> = iolist_to_binary(join(re:split("abbbbc",".{1}",[trim]))), <<":bbbbc">> = iolist_to_binary(join(re:split("abbbbc",".{1}",[{parts, - 2}]))), - <<"::::::">> = iolist_to_binary(join(re:split("abbbbc",".{1}",[]))), - <<":bc">> = iolist_to_binary(join(re:split("abbbbc",".{3,4}",[trim]))), + 2}]))), + <<"::::::">> = iolist_to_binary(join(re:split("abbbbc",".{1}",[]))), + <<":bc">> = iolist_to_binary(join(re:split("abbbbc",".{3,4}",[trim]))), <<":bc">> = iolist_to_binary(join(re:split("abbbbc",".{3,4}",[{parts, - 2}]))), - <<":bc">> = iolist_to_binary(join(re:split("abbbbc",".{3,4}",[]))), - <<"">> = iolist_to_binary(join(re:split("abbbbc","ab{0,}bc",[trim]))), + 2}]))), + <<":bc">> = iolist_to_binary(join(re:split("abbbbc",".{3,4}",[]))), + <<"">> = iolist_to_binary(join(re:split("abbbbc","ab{0,}bc",[trim]))), <<":">> = iolist_to_binary(join(re:split("abbbbc","ab{0,}bc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abbbbc","ab{0,}bc",[]))), - <<"">> = iolist_to_binary(join(re:split("abbc","ab+bc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abbbbc","ab{0,}bc",[]))), + <<"">> = iolist_to_binary(join(re:split("abbc","ab+bc",[trim]))), <<":">> = iolist_to_binary(join(re:split("abbc","ab+bc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abbc","ab+bc",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab+bc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abbc","ab+bc",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab+bc",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab+bc",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab+bc",[]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","ab+bc",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab+bc",[]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","ab+bc",[trim]))), <<"abc">> = iolist_to_binary(join(re:split("abc","ab+bc",[{parts, - 2}]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","ab+bc",[]))), - <<"abq">> = iolist_to_binary(join(re:split("abq","ab+bc",[trim]))), + 2}]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","ab+bc",[]))), + <<"abq">> = iolist_to_binary(join(re:split("abq","ab+bc",[trim]))), <<"abq">> = iolist_to_binary(join(re:split("abq","ab+bc",[{parts, - 2}]))), - <<"abq">> = iolist_to_binary(join(re:split("abq","ab+bc",[]))), + 2}]))), + <<"abq">> = iolist_to_binary(join(re:split("abq","ab+bc",[]))), ok. run15() -> - <<"">> = iolist_to_binary(join(re:split("abbbbc","ab+bc",[trim]))), + <<"">> = iolist_to_binary(join(re:split("abbbbc","ab+bc",[trim]))), <<":">> = iolist_to_binary(join(re:split("abbbbc","ab+bc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abbbbc","ab+bc",[]))), - <<"">> = iolist_to_binary(join(re:split("abbbbc","ab{1,}bc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abbbbc","ab+bc",[]))), + <<"">> = iolist_to_binary(join(re:split("abbbbc","ab{1,}bc",[trim]))), <<":">> = iolist_to_binary(join(re:split("abbbbc","ab{1,}bc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abbbbc","ab{1,}bc",[]))), - <<"">> = iolist_to_binary(join(re:split("abbbbc","ab{1,3}bc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abbbbc","ab{1,}bc",[]))), + <<"">> = iolist_to_binary(join(re:split("abbbbc","ab{1,3}bc",[trim]))), <<":">> = iolist_to_binary(join(re:split("abbbbc","ab{1,3}bc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abbbbc","ab{1,3}bc",[]))), - <<"">> = iolist_to_binary(join(re:split("abbbbc","ab{3,4}bc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abbbbc","ab{1,3}bc",[]))), + <<"">> = iolist_to_binary(join(re:split("abbbbc","ab{3,4}bc",[trim]))), <<":">> = iolist_to_binary(join(re:split("abbbbc","ab{3,4}bc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abbbbc","ab{3,4}bc",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab{4,5}bc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abbbbc","ab{3,4}bc",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab{4,5}bc",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab{4,5}bc",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab{4,5}bc",[]))), - <<"abq">> = iolist_to_binary(join(re:split("abq","ab{4,5}bc",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab{4,5}bc",[]))), + <<"abq">> = iolist_to_binary(join(re:split("abq","ab{4,5}bc",[trim]))), <<"abq">> = iolist_to_binary(join(re:split("abq","ab{4,5}bc",[{parts, - 2}]))), - <<"abq">> = iolist_to_binary(join(re:split("abq","ab{4,5}bc",[]))), - <<"abbbbc">> = iolist_to_binary(join(re:split("abbbbc","ab{4,5}bc",[trim]))), + 2}]))), + <<"abq">> = iolist_to_binary(join(re:split("abq","ab{4,5}bc",[]))), + <<"abbbbc">> = iolist_to_binary(join(re:split("abbbbc","ab{4,5}bc",[trim]))), <<"abbbbc">> = iolist_to_binary(join(re:split("abbbbc","ab{4,5}bc",[{parts, - 2}]))), - <<"abbbbc">> = iolist_to_binary(join(re:split("abbbbc","ab{4,5}bc",[]))), - <<"">> = iolist_to_binary(join(re:split("abbc","ab?bc",[trim]))), + 2}]))), + <<"abbbbc">> = iolist_to_binary(join(re:split("abbbbc","ab{4,5}bc",[]))), + <<"">> = iolist_to_binary(join(re:split("abbc","ab?bc",[trim]))), <<":">> = iolist_to_binary(join(re:split("abbc","ab?bc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abbc","ab?bc",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","ab?bc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abbc","ab?bc",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","ab?bc",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","ab?bc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","ab?bc",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","ab{0,1}bc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","ab?bc",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","ab{0,1}bc",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","ab{0,1}bc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","ab{0,1}bc",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","ab?c",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","ab{0,1}bc",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","ab?c",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","ab?c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","ab?c",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","ab{0,1}c",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","ab?c",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","ab{0,1}c",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","ab{0,1}c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","ab{0,1}c",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","^abc$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","ab{0,1}c",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","^abc$",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","^abc$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","^abc$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^abc$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","^abc$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^abc$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^abc$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^abc$",[]))), - <<"abbbbc">> = iolist_to_binary(join(re:split("abbbbc","^abc$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^abc$",[]))), + <<"abbbbc">> = iolist_to_binary(join(re:split("abbbbc","^abc$",[trim]))), <<"abbbbc">> = iolist_to_binary(join(re:split("abbbbc","^abc$",[{parts, - 2}]))), - <<"abbbbc">> = iolist_to_binary(join(re:split("abbbbc","^abc$",[]))), - <<"abcc">> = iolist_to_binary(join(re:split("abcc","^abc$",[trim]))), + 2}]))), + <<"abbbbc">> = iolist_to_binary(join(re:split("abbbbc","^abc$",[]))), + <<"abcc">> = iolist_to_binary(join(re:split("abcc","^abc$",[trim]))), <<"abcc">> = iolist_to_binary(join(re:split("abcc","^abc$",[{parts, - 2}]))), - <<"abcc">> = iolist_to_binary(join(re:split("abcc","^abc$",[]))), - <<":c">> = iolist_to_binary(join(re:split("abcc","^abc",[trim]))), + 2}]))), + <<"abcc">> = iolist_to_binary(join(re:split("abcc","^abc$",[]))), + <<":c">> = iolist_to_binary(join(re:split("abcc","^abc",[trim]))), <<":c">> = iolist_to_binary(join(re:split("abcc","^abc",[{parts, - 2}]))), - <<":c">> = iolist_to_binary(join(re:split("abcc","^abc",[]))), - <<"a">> = iolist_to_binary(join(re:split("aabc","abc$",[trim]))), + 2}]))), + <<":c">> = iolist_to_binary(join(re:split("abcc","^abc",[]))), + <<"a">> = iolist_to_binary(join(re:split("aabc","abc$",[trim]))), <<"a:">> = iolist_to_binary(join(re:split("aabc","abc$",[{parts, - 2}]))), - <<"a:">> = iolist_to_binary(join(re:split("aabc","abc$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc$",[trim]))), + 2}]))), + <<"a:">> = iolist_to_binary(join(re:split("aabc","abc$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc$",[]))), - <<"a">> = iolist_to_binary(join(re:split("aabc","abc$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc$",[]))), + <<"a">> = iolist_to_binary(join(re:split("aabc","abc$",[trim]))), <<"a:">> = iolist_to_binary(join(re:split("aabc","abc$",[{parts, - 2}]))), - <<"a:">> = iolist_to_binary(join(re:split("aabc","abc$",[]))), - <<"aabcd">> = iolist_to_binary(join(re:split("aabcd","abc$",[trim]))), + 2}]))), + <<"a:">> = iolist_to_binary(join(re:split("aabc","abc$",[]))), + <<"aabcd">> = iolist_to_binary(join(re:split("aabcd","abc$",[trim]))), <<"aabcd">> = iolist_to_binary(join(re:split("aabcd","abc$",[{parts, - 2}]))), - <<"aabcd">> = iolist_to_binary(join(re:split("aabcd","abc$",[]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","^",[trim]))), + 2}]))), + <<"aabcd">> = iolist_to_binary(join(re:split("aabcd","abc$",[]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","^",[trim]))), <<"abc">> = iolist_to_binary(join(re:split("abc","^",[{parts, - 2}]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","^",[]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","$",[trim]))), + 2}]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","^",[]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","$",[trim]))), <<"abc:">> = iolist_to_binary(join(re:split("abc","$",[{parts, - 2}]))), - <<"abc:">> = iolist_to_binary(join(re:split("abc","$",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","a.c",[trim]))), + 2}]))), + <<"abc:">> = iolist_to_binary(join(re:split("abc","$",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","a.c",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","a.c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","a.c",[]))), - <<"">> = iolist_to_binary(join(re:split("axc","a.c",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","a.c",[]))), + <<"">> = iolist_to_binary(join(re:split("axc","a.c",[trim]))), <<":">> = iolist_to_binary(join(re:split("axc","a.c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("axc","a.c",[]))), - <<"">> = iolist_to_binary(join(re:split("axyzc","a.*c",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("axc","a.c",[]))), + <<"">> = iolist_to_binary(join(re:split("axyzc","a.*c",[trim]))), <<":">> = iolist_to_binary(join(re:split("axyzc","a.*c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("axyzc","a.*c",[]))), - <<"">> = iolist_to_binary(join(re:split("abd","a[bc]d",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("axyzc","a.*c",[]))), + <<"">> = iolist_to_binary(join(re:split("abd","a[bc]d",[trim]))), <<":">> = iolist_to_binary(join(re:split("abd","a[bc]d",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abd","a[bc]d",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[bc]d",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abd","a[bc]d",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[bc]d",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[bc]d",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[bc]d",[]))), - <<"axyzd">> = iolist_to_binary(join(re:split("axyzd","a[bc]d",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[bc]d",[]))), + <<"axyzd">> = iolist_to_binary(join(re:split("axyzd","a[bc]d",[trim]))), <<"axyzd">> = iolist_to_binary(join(re:split("axyzd","a[bc]d",[{parts, - 2}]))), - <<"axyzd">> = iolist_to_binary(join(re:split("axyzd","a[bc]d",[]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","a[bc]d",[trim]))), + 2}]))), + <<"axyzd">> = iolist_to_binary(join(re:split("axyzd","a[bc]d",[]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","a[bc]d",[trim]))), <<"abc">> = iolist_to_binary(join(re:split("abc","a[bc]d",[{parts, - 2}]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","a[bc]d",[]))), - <<"">> = iolist_to_binary(join(re:split("ace","a[b-d]e",[trim]))), + 2}]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","a[bc]d",[]))), + <<"">> = iolist_to_binary(join(re:split("ace","a[b-d]e",[trim]))), <<":">> = iolist_to_binary(join(re:split("ace","a[b-d]e",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ace","a[b-d]e",[]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ace","a[b-d]e",[]))), ok. run16() -> - <<"a">> = iolist_to_binary(join(re:split("aac","a[b-d]",[trim]))), + <<"a">> = iolist_to_binary(join(re:split("aac","a[b-d]",[trim]))), <<"a:">> = iolist_to_binary(join(re:split("aac","a[b-d]",[{parts, - 2}]))), - <<"a:">> = iolist_to_binary(join(re:split("aac","a[b-d]",[]))), - <<"">> = iolist_to_binary(join(re:split("a-","a[-b]",[trim]))), + 2}]))), + <<"a:">> = iolist_to_binary(join(re:split("aac","a[b-d]",[]))), + <<"">> = iolist_to_binary(join(re:split("a-","a[-b]",[trim]))), <<":">> = iolist_to_binary(join(re:split("a-","a[-b]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a-","a[-b]",[]))), - <<"">> = iolist_to_binary(join(re:split("a-","a[b-]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a-","a[-b]",[]))), + <<"">> = iolist_to_binary(join(re:split("a-","a[b-]",[trim]))), <<":">> = iolist_to_binary(join(re:split("a-","a[b-]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a-","a[b-]",[]))), - <<"">> = iolist_to_binary(join(re:split("a]","a]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a-","a[b-]",[]))), + <<"">> = iolist_to_binary(join(re:split("a]","a]",[trim]))), <<":">> = iolist_to_binary(join(re:split("a]","a]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a]","a]",[]))), - <<"">> = iolist_to_binary(join(re:split("a]b","a[]]b",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a]","a]",[]))), + <<"">> = iolist_to_binary(join(re:split("a]b","a[]]b",[trim]))), <<":">> = iolist_to_binary(join(re:split("a]b","a[]]b",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a]b","a[]]b",[]))), - <<"">> = iolist_to_binary(join(re:split("aed","a[^bc]d",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a]b","a[]]b",[]))), + <<"">> = iolist_to_binary(join(re:split("aed","a[^bc]d",[trim]))), <<":">> = iolist_to_binary(join(re:split("aed","a[^bc]d",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aed","a[^bc]d",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[^bc]d",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aed","a[^bc]d",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[^bc]d",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[^bc]d",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[^bc]d",[]))), - <<"abd">> = iolist_to_binary(join(re:split("abd","a[^bc]d",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[^bc]d",[]))), + <<"abd">> = iolist_to_binary(join(re:split("abd","a[^bc]d",[trim]))), <<"abd">> = iolist_to_binary(join(re:split("abd","a[^bc]d",[{parts, - 2}]))), - <<"abd">> = iolist_to_binary(join(re:split("abd","a[^bc]d",[]))), - <<"abd">> = iolist_to_binary(join(re:split("abd","a[^bc]d",[trim]))), + 2}]))), + <<"abd">> = iolist_to_binary(join(re:split("abd","a[^bc]d",[]))), + <<"abd">> = iolist_to_binary(join(re:split("abd","a[^bc]d",[trim]))), <<"abd">> = iolist_to_binary(join(re:split("abd","a[^bc]d",[{parts, - 2}]))), - <<"abd">> = iolist_to_binary(join(re:split("abd","a[^bc]d",[]))), - <<"">> = iolist_to_binary(join(re:split("adc","a[^-b]c",[trim]))), + 2}]))), + <<"abd">> = iolist_to_binary(join(re:split("abd","a[^bc]d",[]))), + <<"">> = iolist_to_binary(join(re:split("adc","a[^-b]c",[trim]))), <<":">> = iolist_to_binary(join(re:split("adc","a[^-b]c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("adc","a[^-b]c",[]))), - <<"">> = iolist_to_binary(join(re:split("adc","a[^]b]c",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("adc","a[^-b]c",[]))), + <<"">> = iolist_to_binary(join(re:split("adc","a[^]b]c",[trim]))), <<":">> = iolist_to_binary(join(re:split("adc","a[^]b]c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("adc","a[^]b]c",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[^]b]c",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("adc","a[^]b]c",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[^]b]c",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[^]b]c",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[^]b]c",[]))), - <<"">> = iolist_to_binary(join(re:split("a-c","a[^]b]c",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[^]b]c",[]))), + <<"">> = iolist_to_binary(join(re:split("a-c","a[^]b]c",[trim]))), <<":">> = iolist_to_binary(join(re:split("a-c","a[^]b]c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a-c","a[^]b]c",[]))), - <<"a]c">> = iolist_to_binary(join(re:split("a]c","a[^]b]c",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a-c","a[^]b]c",[]))), + <<"a]c">> = iolist_to_binary(join(re:split("a]c","a[^]b]c",[trim]))), <<"a]c">> = iolist_to_binary(join(re:split("a]c","a[^]b]c",[{parts, - 2}]))), - <<"a]c">> = iolist_to_binary(join(re:split("a]c","a[^]b]c",[]))), - <<":-">> = iolist_to_binary(join(re:split("a-","\\ba\\b",[trim]))), + 2}]))), + <<"a]c">> = iolist_to_binary(join(re:split("a]c","a[^]b]c",[]))), + <<":-">> = iolist_to_binary(join(re:split("a-","\\ba\\b",[trim]))), <<":-">> = iolist_to_binary(join(re:split("a-","\\ba\\b",[{parts, - 2}]))), - <<":-">> = iolist_to_binary(join(re:split("a-","\\ba\\b",[]))), - <<"-">> = iolist_to_binary(join(re:split("-a","\\ba\\b",[trim]))), + 2}]))), + <<":-">> = iolist_to_binary(join(re:split("a-","\\ba\\b",[]))), + <<"-">> = iolist_to_binary(join(re:split("-a","\\ba\\b",[trim]))), <<"-:">> = iolist_to_binary(join(re:split("-a","\\ba\\b",[{parts, - 2}]))), - <<"-:">> = iolist_to_binary(join(re:split("-a","\\ba\\b",[]))), - <<"-:-">> = iolist_to_binary(join(re:split("-a-","\\ba\\b",[trim]))), + 2}]))), + <<"-:">> = iolist_to_binary(join(re:split("-a","\\ba\\b",[]))), + <<"-:-">> = iolist_to_binary(join(re:split("-a-","\\ba\\b",[trim]))), <<"-:-">> = iolist_to_binary(join(re:split("-a-","\\ba\\b",[{parts, - 2}]))), - <<"-:-">> = iolist_to_binary(join(re:split("-a-","\\ba\\b",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\by\\b",[trim]))), + 2}]))), + <<"-:-">> = iolist_to_binary(join(re:split("-a-","\\ba\\b",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\by\\b",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\by\\b",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\by\\b",[]))), - <<"xy">> = iolist_to_binary(join(re:split("xy","\\by\\b",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\by\\b",[]))), + <<"xy">> = iolist_to_binary(join(re:split("xy","\\by\\b",[trim]))), <<"xy">> = iolist_to_binary(join(re:split("xy","\\by\\b",[{parts, - 2}]))), - <<"xy">> = iolist_to_binary(join(re:split("xy","\\by\\b",[]))), - <<"yz">> = iolist_to_binary(join(re:split("yz","\\by\\b",[trim]))), + 2}]))), + <<"xy">> = iolist_to_binary(join(re:split("xy","\\by\\b",[]))), + <<"yz">> = iolist_to_binary(join(re:split("yz","\\by\\b",[trim]))), <<"yz">> = iolist_to_binary(join(re:split("yz","\\by\\b",[{parts, - 2}]))), - <<"yz">> = iolist_to_binary(join(re:split("yz","\\by\\b",[]))), - <<"xyz">> = iolist_to_binary(join(re:split("xyz","\\by\\b",[trim]))), + 2}]))), + <<"yz">> = iolist_to_binary(join(re:split("yz","\\by\\b",[]))), + <<"xyz">> = iolist_to_binary(join(re:split("xyz","\\by\\b",[trim]))), <<"xyz">> = iolist_to_binary(join(re:split("xyz","\\by\\b",[{parts, - 2}]))), - <<"xyz">> = iolist_to_binary(join(re:split("xyz","\\by\\b",[]))), - <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","\\Ba\\B",[trim]))), + 2}]))), + <<"xyz">> = iolist_to_binary(join(re:split("xyz","\\by\\b",[]))), + <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","\\Ba\\B",[trim]))), <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","\\Ba\\B",[{parts, - 2}]))), - <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","\\Ba\\B",[]))), - <<"a-">> = iolist_to_binary(join(re:split("a-","\\Ba\\B",[trim]))), + 2}]))), + <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","\\Ba\\B",[]))), + <<"a-">> = iolist_to_binary(join(re:split("a-","\\Ba\\B",[trim]))), <<"a-">> = iolist_to_binary(join(re:split("a-","\\Ba\\B",[{parts, - 2}]))), - <<"a-">> = iolist_to_binary(join(re:split("a-","\\Ba\\B",[]))), - <<"-a">> = iolist_to_binary(join(re:split("-a","\\Ba\\B",[trim]))), + 2}]))), + <<"a-">> = iolist_to_binary(join(re:split("a-","\\Ba\\B",[]))), + <<"-a">> = iolist_to_binary(join(re:split("-a","\\Ba\\B",[trim]))), <<"-a">> = iolist_to_binary(join(re:split("-a","\\Ba\\B",[{parts, - 2}]))), - <<"-a">> = iolist_to_binary(join(re:split("-a","\\Ba\\B",[]))), - <<"-a-">> = iolist_to_binary(join(re:split("-a-","\\Ba\\B",[trim]))), + 2}]))), + <<"-a">> = iolist_to_binary(join(re:split("-a","\\Ba\\B",[]))), + <<"-a-">> = iolist_to_binary(join(re:split("-a-","\\Ba\\B",[trim]))), <<"-a-">> = iolist_to_binary(join(re:split("-a-","\\Ba\\B",[{parts, - 2}]))), - <<"-a-">> = iolist_to_binary(join(re:split("-a-","\\Ba\\B",[]))), - <<"x">> = iolist_to_binary(join(re:split("xy","\\By\\b",[trim]))), + 2}]))), + <<"-a-">> = iolist_to_binary(join(re:split("-a-","\\Ba\\B",[]))), + <<"x">> = iolist_to_binary(join(re:split("xy","\\By\\b",[trim]))), <<"x:">> = iolist_to_binary(join(re:split("xy","\\By\\b",[{parts, - 2}]))), - <<"x:">> = iolist_to_binary(join(re:split("xy","\\By\\b",[]))), - <<":z">> = iolist_to_binary(join(re:split("yz","\\by\\B",[trim]))), + 2}]))), + <<"x:">> = iolist_to_binary(join(re:split("xy","\\By\\b",[]))), + <<":z">> = iolist_to_binary(join(re:split("yz","\\by\\B",[trim]))), <<":z">> = iolist_to_binary(join(re:split("yz","\\by\\B",[{parts, - 2}]))), - <<":z">> = iolist_to_binary(join(re:split("yz","\\by\\B",[]))), - <<"x:z">> = iolist_to_binary(join(re:split("xyz","\\By\\B",[trim]))), + 2}]))), + <<":z">> = iolist_to_binary(join(re:split("yz","\\by\\B",[]))), + <<"x:z">> = iolist_to_binary(join(re:split("xyz","\\By\\B",[trim]))), <<"x:z">> = iolist_to_binary(join(re:split("xyz","\\By\\B",[{parts, - 2}]))), - <<"x:z">> = iolist_to_binary(join(re:split("xyz","\\By\\B",[]))), - <<"">> = iolist_to_binary(join(re:split("a","\\w",[trim]))), + 2}]))), + <<"x:z">> = iolist_to_binary(join(re:split("xyz","\\By\\B",[]))), + <<"">> = iolist_to_binary(join(re:split("a","\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("a","\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a","\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("-","\\W",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a","\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("-","\\W",[trim]))), <<":">> = iolist_to_binary(join(re:split("-","\\W",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("-","\\W",[]))), - <<"::::Failers">> = iolist_to_binary(join(re:split("*** Failers","\\W",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("-","\\W",[]))), + <<"::::Failers">> = iolist_to_binary(join(re:split("*** Failers","\\W",[trim]))), <<":** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\W",[{parts, - 2}]))), - <<"::::Failers">> = iolist_to_binary(join(re:split("*** Failers","\\W",[]))), - <<"">> = iolist_to_binary(join(re:split("-","\\W",[trim]))), + 2}]))), + <<"::::Failers">> = iolist_to_binary(join(re:split("*** Failers","\\W",[]))), + <<"">> = iolist_to_binary(join(re:split("-","\\W",[trim]))), <<":">> = iolist_to_binary(join(re:split("-","\\W",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("-","\\W",[]))), - <<"a">> = iolist_to_binary(join(re:split("a","\\W",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("-","\\W",[]))), + <<"a">> = iolist_to_binary(join(re:split("a","\\W",[trim]))), <<"a">> = iolist_to_binary(join(re:split("a","\\W",[{parts, - 2}]))), - <<"a">> = iolist_to_binary(join(re:split("a","\\W",[]))), - <<"">> = iolist_to_binary(join(re:split("a b","a\\sb",[trim]))), + 2}]))), + <<"a">> = iolist_to_binary(join(re:split("a","\\W",[]))), + <<"">> = iolist_to_binary(join(re:split("a b","a\\sb",[trim]))), <<":">> = iolist_to_binary(join(re:split("a b","a\\sb",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a b","a\\sb",[]))), - <<"">> = iolist_to_binary(join(re:split("a-b","a\\Sb",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a b","a\\sb",[]))), + <<"">> = iolist_to_binary(join(re:split("a-b","a\\Sb",[trim]))), <<":">> = iolist_to_binary(join(re:split("a-b","a\\Sb",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a-b","a\\Sb",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a\\Sb",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a-b","a\\Sb",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a\\Sb",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a\\Sb",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a\\Sb",[]))), - <<"">> = iolist_to_binary(join(re:split("a-b","a\\Sb",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a\\Sb",[]))), + <<"">> = iolist_to_binary(join(re:split("a-b","a\\Sb",[trim]))), <<":">> = iolist_to_binary(join(re:split("a-b","a\\Sb",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a-b","a\\Sb",[]))), - <<"a b">> = iolist_to_binary(join(re:split("a b","a\\Sb",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a-b","a\\Sb",[]))), + <<"a b">> = iolist_to_binary(join(re:split("a b","a\\Sb",[trim]))), <<"a b">> = iolist_to_binary(join(re:split("a b","a\\Sb",[{parts, - 2}]))), - <<"a b">> = iolist_to_binary(join(re:split("a b","a\\Sb",[]))), - <<"">> = iolist_to_binary(join(re:split("1","\\d",[trim]))), + 2}]))), + <<"a b">> = iolist_to_binary(join(re:split("a b","a\\Sb",[]))), + <<"">> = iolist_to_binary(join(re:split("1","\\d",[trim]))), <<":">> = iolist_to_binary(join(re:split("1","\\d",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("1","\\d",[]))), - <<"">> = iolist_to_binary(join(re:split("-","\\D",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("1","\\d",[]))), + <<"">> = iolist_to_binary(join(re:split("-","\\D",[trim]))), <<":">> = iolist_to_binary(join(re:split("-","\\D",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("-","\\D",[]))), - <<"">> = iolist_to_binary(join(re:split("*** Failers","\\D",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("-","\\D",[]))), + <<"">> = iolist_to_binary(join(re:split("*** Failers","\\D",[trim]))), <<":** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\D",[{parts, - 2}]))), - <<":::::::::::">> = iolist_to_binary(join(re:split("*** Failers","\\D",[]))), - <<"">> = iolist_to_binary(join(re:split("-","\\D",[trim]))), + 2}]))), + <<":::::::::::">> = iolist_to_binary(join(re:split("*** Failers","\\D",[]))), + <<"">> = iolist_to_binary(join(re:split("-","\\D",[trim]))), <<":">> = iolist_to_binary(join(re:split("-","\\D",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("-","\\D",[]))), - <<"1">> = iolist_to_binary(join(re:split("1","\\D",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("-","\\D",[]))), + <<"1">> = iolist_to_binary(join(re:split("1","\\D",[trim]))), <<"1">> = iolist_to_binary(join(re:split("1","\\D",[{parts, - 2}]))), - <<"1">> = iolist_to_binary(join(re:split("1","\\D",[]))), + 2}]))), + <<"1">> = iolist_to_binary(join(re:split("1","\\D",[]))), ok. run17() -> - <<"">> = iolist_to_binary(join(re:split("a","[\\w]",[trim]))), + <<"">> = iolist_to_binary(join(re:split("a","[\\w]",[trim]))), <<":">> = iolist_to_binary(join(re:split("a","[\\w]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a","[\\w]",[]))), - <<"">> = iolist_to_binary(join(re:split("-","[\\W]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a","[\\w]",[]))), + <<"">> = iolist_to_binary(join(re:split("-","[\\W]",[trim]))), <<":">> = iolist_to_binary(join(re:split("-","[\\W]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("-","[\\W]",[]))), - <<"::::Failers">> = iolist_to_binary(join(re:split("*** Failers","[\\W]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("-","[\\W]",[]))), + <<"::::Failers">> = iolist_to_binary(join(re:split("*** Failers","[\\W]",[trim]))), <<":** Failers">> = iolist_to_binary(join(re:split("*** Failers","[\\W]",[{parts, - 2}]))), - <<"::::Failers">> = iolist_to_binary(join(re:split("*** Failers","[\\W]",[]))), - <<"">> = iolist_to_binary(join(re:split("-","[\\W]",[trim]))), + 2}]))), + <<"::::Failers">> = iolist_to_binary(join(re:split("*** Failers","[\\W]",[]))), + <<"">> = iolist_to_binary(join(re:split("-","[\\W]",[trim]))), <<":">> = iolist_to_binary(join(re:split("-","[\\W]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("-","[\\W]",[]))), - <<"a">> = iolist_to_binary(join(re:split("a","[\\W]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("-","[\\W]",[]))), + <<"a">> = iolist_to_binary(join(re:split("a","[\\W]",[trim]))), <<"a">> = iolist_to_binary(join(re:split("a","[\\W]",[{parts, - 2}]))), - <<"a">> = iolist_to_binary(join(re:split("a","[\\W]",[]))), - <<"">> = iolist_to_binary(join(re:split("a b","a[\\s]b",[trim]))), + 2}]))), + <<"a">> = iolist_to_binary(join(re:split("a","[\\W]",[]))), + <<"">> = iolist_to_binary(join(re:split("a b","a[\\s]b",[trim]))), <<":">> = iolist_to_binary(join(re:split("a b","a[\\s]b",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a b","a[\\s]b",[]))), - <<"">> = iolist_to_binary(join(re:split("a-b","a[\\S]b",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a b","a[\\s]b",[]))), + <<"">> = iolist_to_binary(join(re:split("a-b","a[\\S]b",[trim]))), <<":">> = iolist_to_binary(join(re:split("a-b","a[\\S]b",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a-b","a[\\S]b",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[\\S]b",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a-b","a[\\S]b",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[\\S]b",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[\\S]b",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[\\S]b",[]))), - <<"">> = iolist_to_binary(join(re:split("a-b","a[\\S]b",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[\\S]b",[]))), + <<"">> = iolist_to_binary(join(re:split("a-b","a[\\S]b",[trim]))), <<":">> = iolist_to_binary(join(re:split("a-b","a[\\S]b",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a-b","a[\\S]b",[]))), - <<"a b">> = iolist_to_binary(join(re:split("a b","a[\\S]b",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a-b","a[\\S]b",[]))), + <<"a b">> = iolist_to_binary(join(re:split("a b","a[\\S]b",[trim]))), <<"a b">> = iolist_to_binary(join(re:split("a b","a[\\S]b",[{parts, - 2}]))), - <<"a b">> = iolist_to_binary(join(re:split("a b","a[\\S]b",[]))), - <<"">> = iolist_to_binary(join(re:split("1","[\\d]",[trim]))), + 2}]))), + <<"a b">> = iolist_to_binary(join(re:split("a b","a[\\S]b",[]))), + <<"">> = iolist_to_binary(join(re:split("1","[\\d]",[trim]))), <<":">> = iolist_to_binary(join(re:split("1","[\\d]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("1","[\\d]",[]))), - <<"">> = iolist_to_binary(join(re:split("-","[\\D]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("1","[\\d]",[]))), + <<"">> = iolist_to_binary(join(re:split("-","[\\D]",[trim]))), <<":">> = iolist_to_binary(join(re:split("-","[\\D]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("-","[\\D]",[]))), - <<"">> = iolist_to_binary(join(re:split("*** Failers","[\\D]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("-","[\\D]",[]))), + <<"">> = iolist_to_binary(join(re:split("*** Failers","[\\D]",[trim]))), <<":** Failers">> = iolist_to_binary(join(re:split("*** Failers","[\\D]",[{parts, - 2}]))), - <<":::::::::::">> = iolist_to_binary(join(re:split("*** Failers","[\\D]",[]))), - <<"">> = iolist_to_binary(join(re:split("-","[\\D]",[trim]))), + 2}]))), + <<":::::::::::">> = iolist_to_binary(join(re:split("*** Failers","[\\D]",[]))), + <<"">> = iolist_to_binary(join(re:split("-","[\\D]",[trim]))), <<":">> = iolist_to_binary(join(re:split("-","[\\D]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("-","[\\D]",[]))), - <<"1">> = iolist_to_binary(join(re:split("1","[\\D]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("-","[\\D]",[]))), + <<"1">> = iolist_to_binary(join(re:split("1","[\\D]",[trim]))), <<"1">> = iolist_to_binary(join(re:split("1","[\\D]",[{parts, - 2}]))), - <<"1">> = iolist_to_binary(join(re:split("1","[\\D]",[]))), - <<":c">> = iolist_to_binary(join(re:split("abc","ab|cd",[trim]))), + 2}]))), + <<"1">> = iolist_to_binary(join(re:split("1","[\\D]",[]))), + <<":c">> = iolist_to_binary(join(re:split("abc","ab|cd",[trim]))), <<":c">> = iolist_to_binary(join(re:split("abc","ab|cd",[{parts, - 2}]))), - <<":c">> = iolist_to_binary(join(re:split("abc","ab|cd",[]))), - <<"">> = iolist_to_binary(join(re:split("abcd","ab|cd",[trim]))), + 2}]))), + <<":c">> = iolist_to_binary(join(re:split("abc","ab|cd",[]))), + <<"">> = iolist_to_binary(join(re:split("abcd","ab|cd",[trim]))), <<":cd">> = iolist_to_binary(join(re:split("abcd","ab|cd",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("abcd","ab|cd",[]))), - <<"d">> = iolist_to_binary(join(re:split("def","()ef",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("abcd","ab|cd",[]))), + <<"d">> = iolist_to_binary(join(re:split("def","()ef",[trim]))), <<"d::">> = iolist_to_binary(join(re:split("def","()ef",[{parts, - 2}]))), - <<"d::">> = iolist_to_binary(join(re:split("def","()ef",[]))), - <<"">> = iolist_to_binary(join(re:split("a(b","a\\(b",[trim]))), + 2}]))), + <<"d::">> = iolist_to_binary(join(re:split("def","()ef",[]))), + <<"">> = iolist_to_binary(join(re:split("a(b","a\\(b",[trim]))), <<":">> = iolist_to_binary(join(re:split("a(b","a\\(b",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a(b","a\\(b",[]))), - <<"">> = iolist_to_binary(join(re:split("ab","a\\(*b",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a(b","a\\(b",[]))), + <<"">> = iolist_to_binary(join(re:split("ab","a\\(*b",[trim]))), <<":">> = iolist_to_binary(join(re:split("ab","a\\(*b",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ab","a\\(*b",[]))), - <<"">> = iolist_to_binary(join(re:split("a((b","a\\(*b",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ab","a\\(*b",[]))), + <<"">> = iolist_to_binary(join(re:split("a((b","a\\(*b",[trim]))), <<":">> = iolist_to_binary(join(re:split("a((b","a\\(*b",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a((b","a\\(*b",[]))), - <<"a">> = iolist_to_binary(join(re:split("a","a\\\\b",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a((b","a\\(*b",[]))), + <<"a">> = iolist_to_binary(join(re:split("a","a\\\\b",[trim]))), <<"a">> = iolist_to_binary(join(re:split("a","a\\\\b",[{parts, - 2}]))), - <<"a">> = iolist_to_binary(join(re:split("a","a\\\\b",[]))), - <<":a:a:bc">> = iolist_to_binary(join(re:split("abc","((a))",[trim]))), + 2}]))), + <<"a">> = iolist_to_binary(join(re:split("a","a\\\\b",[]))), + <<":a:a:bc">> = iolist_to_binary(join(re:split("abc","((a))",[trim]))), <<":a:a:bc">> = iolist_to_binary(join(re:split("abc","((a))",[{parts, - 2}]))), - <<":a:a:bc">> = iolist_to_binary(join(re:split("abc","((a))",[]))), - <<":a:c">> = iolist_to_binary(join(re:split("abc","(a)b(c)",[trim]))), + 2}]))), + <<":a:a:bc">> = iolist_to_binary(join(re:split("abc","((a))",[]))), + <<":a:c">> = iolist_to_binary(join(re:split("abc","(a)b(c)",[trim]))), <<":a:c:">> = iolist_to_binary(join(re:split("abc","(a)b(c)",[{parts, - 2}]))), - <<":a:c:">> = iolist_to_binary(join(re:split("abc","(a)b(c)",[]))), - <<"aabb">> = iolist_to_binary(join(re:split("aabbabc","a+b+c",[trim]))), + 2}]))), + <<":a:c:">> = iolist_to_binary(join(re:split("abc","(a)b(c)",[]))), + <<"aabb">> = iolist_to_binary(join(re:split("aabbabc","a+b+c",[trim]))), <<"aabb:">> = iolist_to_binary(join(re:split("aabbabc","a+b+c",[{parts, - 2}]))), - <<"aabb:">> = iolist_to_binary(join(re:split("aabbabc","a+b+c",[]))), - <<"aabb">> = iolist_to_binary(join(re:split("aabbabc","a{1,}b{1,}c",[trim]))), + 2}]))), + <<"aabb:">> = iolist_to_binary(join(re:split("aabbabc","a+b+c",[]))), + <<"aabb">> = iolist_to_binary(join(re:split("aabbabc","a{1,}b{1,}c",[trim]))), <<"aabb:">> = iolist_to_binary(join(re:split("aabbabc","a{1,}b{1,}c",[{parts, - 2}]))), - <<"aabb:">> = iolist_to_binary(join(re:split("aabbabc","a{1,}b{1,}c",[]))), - <<"">> = iolist_to_binary(join(re:split("abcabc","a.+?c",[trim]))), + 2}]))), + <<"aabb:">> = iolist_to_binary(join(re:split("aabbabc","a{1,}b{1,}c",[]))), + <<"">> = iolist_to_binary(join(re:split("abcabc","a.+?c",[trim]))), <<":abc">> = iolist_to_binary(join(re:split("abcabc","a.+?c",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("abcabc","a.+?c",[]))), - <<":b">> = iolist_to_binary(join(re:split("ab","(a+|b)*",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("abcabc","a.+?c",[]))), + <<":b">> = iolist_to_binary(join(re:split("ab","(a+|b)*",[trim]))), <<":b:">> = iolist_to_binary(join(re:split("ab","(a+|b)*",[{parts, - 2}]))), - <<":b:">> = iolist_to_binary(join(re:split("ab","(a+|b)*",[]))), - <<":b">> = iolist_to_binary(join(re:split("ab","(a+|b){0,}",[trim]))), + 2}]))), + <<":b:">> = iolist_to_binary(join(re:split("ab","(a+|b)*",[]))), + <<":b">> = iolist_to_binary(join(re:split("ab","(a+|b){0,}",[trim]))), <<":b:">> = iolist_to_binary(join(re:split("ab","(a+|b){0,}",[{parts, - 2}]))), - <<":b:">> = iolist_to_binary(join(re:split("ab","(a+|b){0,}",[]))), - <<":b">> = iolist_to_binary(join(re:split("ab","(a+|b)+",[trim]))), + 2}]))), + <<":b:">> = iolist_to_binary(join(re:split("ab","(a+|b){0,}",[]))), + <<":b">> = iolist_to_binary(join(re:split("ab","(a+|b)+",[trim]))), <<":b:">> = iolist_to_binary(join(re:split("ab","(a+|b)+",[{parts, - 2}]))), - <<":b:">> = iolist_to_binary(join(re:split("ab","(a+|b)+",[]))), + 2}]))), + <<":b:">> = iolist_to_binary(join(re:split("ab","(a+|b)+",[]))), ok. run18() -> - <<":b">> = iolist_to_binary(join(re:split("ab","(a+|b){1,}",[trim]))), + <<":b">> = iolist_to_binary(join(re:split("ab","(a+|b){1,}",[trim]))), <<":b:">> = iolist_to_binary(join(re:split("ab","(a+|b){1,}",[{parts, - 2}]))), - <<":b:">> = iolist_to_binary(join(re:split("ab","(a+|b){1,}",[]))), - <<":a::b">> = iolist_to_binary(join(re:split("ab","(a+|b)?",[trim]))), + 2}]))), + <<":b:">> = iolist_to_binary(join(re:split("ab","(a+|b){1,}",[]))), + <<":a::b">> = iolist_to_binary(join(re:split("ab","(a+|b)?",[trim]))), <<":a:b">> = iolist_to_binary(join(re:split("ab","(a+|b)?",[{parts, - 2}]))), - <<":a::b:">> = iolist_to_binary(join(re:split("ab","(a+|b)?",[]))), - <<":a::b">> = iolist_to_binary(join(re:split("ab","(a+|b){0,1}",[trim]))), + 2}]))), + <<":a::b:">> = iolist_to_binary(join(re:split("ab","(a+|b)?",[]))), + <<":a::b">> = iolist_to_binary(join(re:split("ab","(a+|b){0,1}",[trim]))), <<":a:b">> = iolist_to_binary(join(re:split("ab","(a+|b){0,1}",[{parts, - 2}]))), - <<":a::b:">> = iolist_to_binary(join(re:split("ab","(a+|b){0,1}",[]))), - <<"">> = iolist_to_binary(join(re:split("cde","[^ab]*",[trim]))), + 2}]))), + <<":a::b:">> = iolist_to_binary(join(re:split("ab","(a+|b){0,1}",[]))), + <<"">> = iolist_to_binary(join(re:split("cde","[^ab]*",[trim]))), <<":">> = iolist_to_binary(join(re:split("cde","[^ab]*",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("cde","[^ab]*",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("cde","[^ab]*",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc",[]))), - <<"b">> = iolist_to_binary(join(re:split("b","abc",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc",[]))), + <<"b">> = iolist_to_binary(join(re:split("b","abc",[trim]))), <<"b">> = iolist_to_binary(join(re:split("b","abc",[{parts, - 2}]))), - <<"b">> = iolist_to_binary(join(re:split("b","abc",[]))), - <<":c">> = iolist_to_binary(join(re:split("abbbcd","([abc])*d",[trim]))), + 2}]))), + <<"b">> = iolist_to_binary(join(re:split("b","abc",[]))), + <<":c">> = iolist_to_binary(join(re:split("abbbcd","([abc])*d",[trim]))), <<":c:">> = iolist_to_binary(join(re:split("abbbcd","([abc])*d",[{parts, - 2}]))), - <<":c:">> = iolist_to_binary(join(re:split("abbbcd","([abc])*d",[]))), - <<":a">> = iolist_to_binary(join(re:split("abcd","([abc])*bcd",[trim]))), + 2}]))), + <<":c:">> = iolist_to_binary(join(re:split("abbbcd","([abc])*d",[]))), + <<":a">> = iolist_to_binary(join(re:split("abcd","([abc])*bcd",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("abcd","([abc])*bcd",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("abcd","([abc])*bcd",[]))), - <<"">> = iolist_to_binary(join(re:split("e","a|b|c|d|e",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("abcd","([abc])*bcd",[]))), + <<"">> = iolist_to_binary(join(re:split("e","a|b|c|d|e",[trim]))), <<":">> = iolist_to_binary(join(re:split("e","a|b|c|d|e",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("e","a|b|c|d|e",[]))), - <<":e">> = iolist_to_binary(join(re:split("ef","(a|b|c|d|e)f",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("e","a|b|c|d|e",[]))), + <<":e">> = iolist_to_binary(join(re:split("ef","(a|b|c|d|e)f",[trim]))), <<":e:">> = iolist_to_binary(join(re:split("ef","(a|b|c|d|e)f",[{parts, - 2}]))), - <<":e:">> = iolist_to_binary(join(re:split("ef","(a|b|c|d|e)f",[]))), - <<"">> = iolist_to_binary(join(re:split("abcdefg","abcd*efg",[trim]))), + 2}]))), + <<":e:">> = iolist_to_binary(join(re:split("ef","(a|b|c|d|e)f",[]))), + <<"">> = iolist_to_binary(join(re:split("abcdefg","abcd*efg",[trim]))), <<":">> = iolist_to_binary(join(re:split("abcdefg","abcd*efg",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abcdefg","abcd*efg",[]))), - <<"x:y:z">> = iolist_to_binary(join(re:split("xabyabbbz","ab*",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abcdefg","abcd*efg",[]))), + <<"x:y:z">> = iolist_to_binary(join(re:split("xabyabbbz","ab*",[trim]))), <<"x:yabbbz">> = iolist_to_binary(join(re:split("xabyabbbz","ab*",[{parts, - 2}]))), - <<"x:y:z">> = iolist_to_binary(join(re:split("xabyabbbz","ab*",[]))), - <<"x:y:z">> = iolist_to_binary(join(re:split("xayabbbz","ab*",[trim]))), + 2}]))), + <<"x:y:z">> = iolist_to_binary(join(re:split("xabyabbbz","ab*",[]))), + <<"x:y:z">> = iolist_to_binary(join(re:split("xayabbbz","ab*",[trim]))), <<"x:yabbbz">> = iolist_to_binary(join(re:split("xayabbbz","ab*",[{parts, - 2}]))), - <<"x:y:z">> = iolist_to_binary(join(re:split("xayabbbz","ab*",[]))), - <<"ab:cd">> = iolist_to_binary(join(re:split("abcde","(ab|cd)e",[trim]))), + 2}]))), + <<"x:y:z">> = iolist_to_binary(join(re:split("xayabbbz","ab*",[]))), + <<"ab:cd">> = iolist_to_binary(join(re:split("abcde","(ab|cd)e",[trim]))), <<"ab:cd:">> = iolist_to_binary(join(re:split("abcde","(ab|cd)e",[{parts, - 2}]))), - <<"ab:cd:">> = iolist_to_binary(join(re:split("abcde","(ab|cd)e",[]))), - <<"">> = iolist_to_binary(join(re:split("hij","[abhgefdc]ij",[trim]))), + 2}]))), + <<"ab:cd:">> = iolist_to_binary(join(re:split("abcde","(ab|cd)e",[]))), + <<"">> = iolist_to_binary(join(re:split("hij","[abhgefdc]ij",[trim]))), <<":">> = iolist_to_binary(join(re:split("hij","[abhgefdc]ij",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("hij","[abhgefdc]ij",[]))), - <<"abcd">> = iolist_to_binary(join(re:split("abcdef","(abc|)ef",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("hij","[abhgefdc]ij",[]))), + <<"abcd">> = iolist_to_binary(join(re:split("abcdef","(abc|)ef",[trim]))), <<"abcd::">> = iolist_to_binary(join(re:split("abcdef","(abc|)ef",[{parts, - 2}]))), - <<"abcd::">> = iolist_to_binary(join(re:split("abcdef","(abc|)ef",[]))), - <<"a:b">> = iolist_to_binary(join(re:split("abcd","(a|b)c*d",[trim]))), + 2}]))), + <<"abcd::">> = iolist_to_binary(join(re:split("abcdef","(abc|)ef",[]))), + <<"a:b">> = iolist_to_binary(join(re:split("abcd","(a|b)c*d",[trim]))), <<"a:b:">> = iolist_to_binary(join(re:split("abcd","(a|b)c*d",[{parts, - 2}]))), - <<"a:b:">> = iolist_to_binary(join(re:split("abcd","(a|b)c*d",[]))), - <<":a">> = iolist_to_binary(join(re:split("abc","(ab|ab*)bc",[trim]))), + 2}]))), + <<"a:b:">> = iolist_to_binary(join(re:split("abcd","(a|b)c*d",[]))), + <<":a">> = iolist_to_binary(join(re:split("abc","(ab|ab*)bc",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("abc","(ab|ab*)bc",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("abc","(ab|ab*)bc",[]))), - <<":bc">> = iolist_to_binary(join(re:split("abc","a([bc]*)c*",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("abc","(ab|ab*)bc",[]))), + <<":bc">> = iolist_to_binary(join(re:split("abc","a([bc]*)c*",[trim]))), <<":bc:">> = iolist_to_binary(join(re:split("abc","a([bc]*)c*",[{parts, - 2}]))), - <<":bc:">> = iolist_to_binary(join(re:split("abc","a([bc]*)c*",[]))), - <<":bc:d">> = iolist_to_binary(join(re:split("abcd","a([bc]*)(c*d)",[trim]))), + 2}]))), + <<":bc:">> = iolist_to_binary(join(re:split("abc","a([bc]*)c*",[]))), + <<":bc:d">> = iolist_to_binary(join(re:split("abcd","a([bc]*)(c*d)",[trim]))), <<":bc:d:">> = iolist_to_binary(join(re:split("abcd","a([bc]*)(c*d)",[{parts, - 2}]))), - <<":bc:d:">> = iolist_to_binary(join(re:split("abcd","a([bc]*)(c*d)",[]))), + 2}]))), + <<":bc:d:">> = iolist_to_binary(join(re:split("abcd","a([bc]*)(c*d)",[]))), ok. run19() -> - <<":bc:d">> = iolist_to_binary(join(re:split("abcd","a([bc]+)(c*d)",[trim]))), + <<":bc:d">> = iolist_to_binary(join(re:split("abcd","a([bc]+)(c*d)",[trim]))), <<":bc:d:">> = iolist_to_binary(join(re:split("abcd","a([bc]+)(c*d)",[{parts, - 2}]))), - <<":bc:d:">> = iolist_to_binary(join(re:split("abcd","a([bc]+)(c*d)",[]))), - <<":b:cd">> = iolist_to_binary(join(re:split("abcd","a([bc]*)(c+d)",[trim]))), + 2}]))), + <<":bc:d:">> = iolist_to_binary(join(re:split("abcd","a([bc]+)(c*d)",[]))), + <<":b:cd">> = iolist_to_binary(join(re:split("abcd","a([bc]*)(c+d)",[trim]))), <<":b:cd:">> = iolist_to_binary(join(re:split("abcd","a([bc]*)(c+d)",[{parts, - 2}]))), - <<":b:cd:">> = iolist_to_binary(join(re:split("abcd","a([bc]*)(c+d)",[]))), - <<"">> = iolist_to_binary(join(re:split("adcdcde","a[bcd]*dcdcde",[trim]))), + 2}]))), + <<":b:cd:">> = iolist_to_binary(join(re:split("abcd","a([bc]*)(c+d)",[]))), + <<"">> = iolist_to_binary(join(re:split("adcdcde","a[bcd]*dcdcde",[trim]))), <<":">> = iolist_to_binary(join(re:split("adcdcde","a[bcd]*dcdcde",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("adcdcde","a[bcd]*dcdcde",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[bcd]+dcdcde",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("adcdcde","a[bcd]*dcdcde",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[bcd]+dcdcde",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[bcd]+dcdcde",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[bcd]+dcdcde",[]))), - <<"abcde">> = iolist_to_binary(join(re:split("abcde","a[bcd]+dcdcde",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[bcd]+dcdcde",[]))), + <<"abcde">> = iolist_to_binary(join(re:split("abcde","a[bcd]+dcdcde",[trim]))), <<"abcde">> = iolist_to_binary(join(re:split("abcde","a[bcd]+dcdcde",[{parts, - 2}]))), - <<"abcde">> = iolist_to_binary(join(re:split("abcde","a[bcd]+dcdcde",[]))), - <<"adcdcde">> = iolist_to_binary(join(re:split("adcdcde","a[bcd]+dcdcde",[trim]))), + 2}]))), + <<"abcde">> = iolist_to_binary(join(re:split("abcde","a[bcd]+dcdcde",[]))), + <<"adcdcde">> = iolist_to_binary(join(re:split("adcdcde","a[bcd]+dcdcde",[trim]))), <<"adcdcde">> = iolist_to_binary(join(re:split("adcdcde","a[bcd]+dcdcde",[{parts, - 2}]))), - <<"adcdcde">> = iolist_to_binary(join(re:split("adcdcde","a[bcd]+dcdcde",[]))), - <<":ab">> = iolist_to_binary(join(re:split("abc","(ab|a)b*c",[trim]))), + 2}]))), + <<"adcdcde">> = iolist_to_binary(join(re:split("adcdcde","a[bcd]+dcdcde",[]))), + <<":ab">> = iolist_to_binary(join(re:split("abc","(ab|a)b*c",[trim]))), <<":ab:">> = iolist_to_binary(join(re:split("abc","(ab|a)b*c",[{parts, - 2}]))), - <<":ab:">> = iolist_to_binary(join(re:split("abc","(ab|a)b*c",[]))), - <<":abc:a:b:d">> = iolist_to_binary(join(re:split("abcd","((a)(b)c)(d)",[trim]))), + 2}]))), + <<":ab:">> = iolist_to_binary(join(re:split("abc","(ab|a)b*c",[]))), + <<":abc:a:b:d">> = iolist_to_binary(join(re:split("abcd","((a)(b)c)(d)",[trim]))), <<":abc:a:b:d:">> = iolist_to_binary(join(re:split("abcd","((a)(b)c)(d)",[{parts, - 2}]))), - <<":abc:a:b:d:">> = iolist_to_binary(join(re:split("abcd","((a)(b)c)(d)",[]))), - <<"">> = iolist_to_binary(join(re:split("alpha","[a-zA-Z_][a-zA-Z0-9_]*",[trim]))), + 2}]))), + <<":abc:a:b:d:">> = iolist_to_binary(join(re:split("abcd","((a)(b)c)(d)",[]))), + <<"">> = iolist_to_binary(join(re:split("alpha","[a-zA-Z_][a-zA-Z0-9_]*",[trim]))), <<":">> = iolist_to_binary(join(re:split("alpha","[a-zA-Z_][a-zA-Z0-9_]*",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("alpha","[a-zA-Z_][a-zA-Z0-9_]*",[]))), - <<"a">> = iolist_to_binary(join(re:split("abh","^a(bc+|b[eh])g|.h$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("alpha","[a-zA-Z_][a-zA-Z0-9_]*",[]))), + <<"a">> = iolist_to_binary(join(re:split("abh","^a(bc+|b[eh])g|.h$",[trim]))), <<"a::">> = iolist_to_binary(join(re:split("abh","^a(bc+|b[eh])g|.h$",[{parts, - 2}]))), - <<"a::">> = iolist_to_binary(join(re:split("abh","^a(bc+|b[eh])g|.h$",[]))), - <<":effgz">> = iolist_to_binary(join(re:split("effgz","(bc+d$|ef*g.|h?i(j|k))",[trim]))), + 2}]))), + <<"a::">> = iolist_to_binary(join(re:split("abh","^a(bc+|b[eh])g|.h$",[]))), + <<":effgz">> = iolist_to_binary(join(re:split("effgz","(bc+d$|ef*g.|h?i(j|k))",[trim]))), <<":effgz::">> = iolist_to_binary(join(re:split("effgz","(bc+d$|ef*g.|h?i(j|k))",[{parts, - 2}]))), - <<":effgz::">> = iolist_to_binary(join(re:split("effgz","(bc+d$|ef*g.|h?i(j|k))",[]))), - <<":ij:j">> = iolist_to_binary(join(re:split("ij","(bc+d$|ef*g.|h?i(j|k))",[trim]))), + 2}]))), + <<":effgz::">> = iolist_to_binary(join(re:split("effgz","(bc+d$|ef*g.|h?i(j|k))",[]))), + <<":ij:j">> = iolist_to_binary(join(re:split("ij","(bc+d$|ef*g.|h?i(j|k))",[trim]))), <<":ij:j:">> = iolist_to_binary(join(re:split("ij","(bc+d$|ef*g.|h?i(j|k))",[{parts, - 2}]))), - <<":ij:j:">> = iolist_to_binary(join(re:split("ij","(bc+d$|ef*g.|h?i(j|k))",[]))), - <<"r:effgz">> = iolist_to_binary(join(re:split("reffgz","(bc+d$|ef*g.|h?i(j|k))",[trim]))), + 2}]))), + <<":ij:j:">> = iolist_to_binary(join(re:split("ij","(bc+d$|ef*g.|h?i(j|k))",[]))), + <<"r:effgz">> = iolist_to_binary(join(re:split("reffgz","(bc+d$|ef*g.|h?i(j|k))",[trim]))), <<"r:effgz::">> = iolist_to_binary(join(re:split("reffgz","(bc+d$|ef*g.|h?i(j|k))",[{parts, - 2}]))), - <<"r:effgz::">> = iolist_to_binary(join(re:split("reffgz","(bc+d$|ef*g.|h?i(j|k))",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(bc+d$|ef*g.|h?i(j|k))",[trim]))), + 2}]))), + <<"r:effgz::">> = iolist_to_binary(join(re:split("reffgz","(bc+d$|ef*g.|h?i(j|k))",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(bc+d$|ef*g.|h?i(j|k))",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(bc+d$|ef*g.|h?i(j|k))",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(bc+d$|ef*g.|h?i(j|k))",[]))), - <<"effg">> = iolist_to_binary(join(re:split("effg","(bc+d$|ef*g.|h?i(j|k))",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(bc+d$|ef*g.|h?i(j|k))",[]))), + <<"effg">> = iolist_to_binary(join(re:split("effg","(bc+d$|ef*g.|h?i(j|k))",[trim]))), <<"effg">> = iolist_to_binary(join(re:split("effg","(bc+d$|ef*g.|h?i(j|k))",[{parts, - 2}]))), - <<"effg">> = iolist_to_binary(join(re:split("effg","(bc+d$|ef*g.|h?i(j|k))",[]))), - <<"bcdd">> = iolist_to_binary(join(re:split("bcdd","(bc+d$|ef*g.|h?i(j|k))",[trim]))), + 2}]))), + <<"effg">> = iolist_to_binary(join(re:split("effg","(bc+d$|ef*g.|h?i(j|k))",[]))), + <<"bcdd">> = iolist_to_binary(join(re:split("bcdd","(bc+d$|ef*g.|h?i(j|k))",[trim]))), <<"bcdd">> = iolist_to_binary(join(re:split("bcdd","(bc+d$|ef*g.|h?i(j|k))",[{parts, - 2}]))), - <<"bcdd">> = iolist_to_binary(join(re:split("bcdd","(bc+d$|ef*g.|h?i(j|k))",[]))), - <<":a:a:a:a:a:a:a:a:a:a">> = iolist_to_binary(join(re:split("a","((((((((((a))))))))))",[trim]))), + 2}]))), + <<"bcdd">> = iolist_to_binary(join(re:split("bcdd","(bc+d$|ef*g.|h?i(j|k))",[]))), + <<":a:a:a:a:a:a:a:a:a:a">> = iolist_to_binary(join(re:split("a","((((((((((a))))))))))",[trim]))), <<":a:a:a:a:a:a:a:a:a:a:">> = iolist_to_binary(join(re:split("a","((((((((((a))))))))))",[{parts, - 2}]))), - <<":a:a:a:a:a:a:a:a:a:a:">> = iolist_to_binary(join(re:split("a","((((((((((a))))))))))",[]))), - <<":a:a:a:a:a:a:a:a:a:a">> = iolist_to_binary(join(re:split("aa","((((((((((a))))))))))\\10",[trim]))), + 2}]))), + <<":a:a:a:a:a:a:a:a:a:a:">> = iolist_to_binary(join(re:split("a","((((((((((a))))))))))",[]))), + <<":a:a:a:a:a:a:a:a:a:a">> = iolist_to_binary(join(re:split("aa","((((((((((a))))))))))\\10",[trim]))), <<":a:a:a:a:a:a:a:a:a:a:">> = iolist_to_binary(join(re:split("aa","((((((((((a))))))))))\\10",[{parts, - 2}]))), - <<":a:a:a:a:a:a:a:a:a:a:">> = iolist_to_binary(join(re:split("aa","((((((((((a))))))))))\\10",[]))), - <<":a:a:a:a:a:a:a:a:a">> = iolist_to_binary(join(re:split("a","(((((((((a)))))))))",[trim]))), + 2}]))), + <<":a:a:a:a:a:a:a:a:a:a:">> = iolist_to_binary(join(re:split("aa","((((((((((a))))))))))\\10",[]))), + <<":a:a:a:a:a:a:a:a:a">> = iolist_to_binary(join(re:split("a","(((((((((a)))))))))",[trim]))), <<":a:a:a:a:a:a:a:a:a:">> = iolist_to_binary(join(re:split("a","(((((((((a)))))))))",[{parts, - 2}]))), - <<":a:a:a:a:a:a:a:a:a:">> = iolist_to_binary(join(re:split("a","(((((((((a)))))))))",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","multiple words of text",[trim]))), + 2}]))), + <<":a:a:a:a:a:a:a:a:a:">> = iolist_to_binary(join(re:split("a","(((((((((a)))))))))",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","multiple words of text",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","multiple words of text",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","multiple words of text",[]))), - <<"aa">> = iolist_to_binary(join(re:split("aa","multiple words of text",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","multiple words of text",[]))), + <<"aa">> = iolist_to_binary(join(re:split("aa","multiple words of text",[trim]))), <<"aa">> = iolist_to_binary(join(re:split("aa","multiple words of text",[{parts, - 2}]))), - <<"aa">> = iolist_to_binary(join(re:split("aa","multiple words of text",[]))), - <<"uh-uh">> = iolist_to_binary(join(re:split("uh-uh","multiple words of text",[trim]))), + 2}]))), + <<"aa">> = iolist_to_binary(join(re:split("aa","multiple words of text",[]))), + <<"uh-uh">> = iolist_to_binary(join(re:split("uh-uh","multiple words of text",[trim]))), <<"uh-uh">> = iolist_to_binary(join(re:split("uh-uh","multiple words of text",[{parts, - 2}]))), - <<"uh-uh">> = iolist_to_binary(join(re:split("uh-uh","multiple words of text",[]))), - <<":, yeah">> = iolist_to_binary(join(re:split("multiple words, yeah","multiple words",[trim]))), + 2}]))), + <<"uh-uh">> = iolist_to_binary(join(re:split("uh-uh","multiple words of text",[]))), + <<":, yeah">> = iolist_to_binary(join(re:split("multiple words, yeah","multiple words",[trim]))), <<":, yeah">> = iolist_to_binary(join(re:split("multiple words, yeah","multiple words",[{parts, - 2}]))), - <<":, yeah">> = iolist_to_binary(join(re:split("multiple words, yeah","multiple words",[]))), - <<":ab:de">> = iolist_to_binary(join(re:split("abcde","(.*)c(.*)",[trim]))), + 2}]))), + <<":, yeah">> = iolist_to_binary(join(re:split("multiple words, yeah","multiple words",[]))), + <<":ab:de">> = iolist_to_binary(join(re:split("abcde","(.*)c(.*)",[trim]))), <<":ab:de:">> = iolist_to_binary(join(re:split("abcde","(.*)c(.*)",[{parts, - 2}]))), - <<":ab:de:">> = iolist_to_binary(join(re:split("abcde","(.*)c(.*)",[]))), - <<":a:b">> = iolist_to_binary(join(re:split("(a, b)","\\((.*), (.*)\\)",[trim]))), + 2}]))), + <<":ab:de:">> = iolist_to_binary(join(re:split("abcde","(.*)c(.*)",[]))), + <<":a:b">> = iolist_to_binary(join(re:split("(a, b)","\\((.*), (.*)\\)",[trim]))), <<":a:b:">> = iolist_to_binary(join(re:split("(a, b)","\\((.*), (.*)\\)",[{parts, - 2}]))), - <<":a:b:">> = iolist_to_binary(join(re:split("(a, b)","\\((.*), (.*)\\)",[]))), - <<"">> = iolist_to_binary(join(re:split("abcd","abcd",[trim]))), + 2}]))), + <<":a:b:">> = iolist_to_binary(join(re:split("(a, b)","\\((.*), (.*)\\)",[]))), + <<"">> = iolist_to_binary(join(re:split("abcd","abcd",[trim]))), <<":">> = iolist_to_binary(join(re:split("abcd","abcd",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abcd","abcd",[]))), - <<":bc">> = iolist_to_binary(join(re:split("abcd","a(bc)d",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abcd","abcd",[]))), + <<":bc">> = iolist_to_binary(join(re:split("abcd","a(bc)d",[trim]))), <<":bc:">> = iolist_to_binary(join(re:split("abcd","a(bc)d",[{parts, - 2}]))), - <<":bc:">> = iolist_to_binary(join(re:split("abcd","a(bc)d",[]))), - <<"">> = iolist_to_binary(join(re:split("ac","a[-]?c",[trim]))), + 2}]))), + <<":bc:">> = iolist_to_binary(join(re:split("abcd","a(bc)d",[]))), + <<"">> = iolist_to_binary(join(re:split("ac","a[-]?c",[trim]))), <<":">> = iolist_to_binary(join(re:split("ac","a[-]?c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ac","a[-]?c",[]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ac","a[-]?c",[]))), ok. run20() -> - <<":abc">> = iolist_to_binary(join(re:split("abcabc","(abc)\\1",[trim]))), + <<":abc">> = iolist_to_binary(join(re:split("abcabc","(abc)\\1",[trim]))), <<":abc:">> = iolist_to_binary(join(re:split("abcabc","(abc)\\1",[{parts, - 2}]))), - <<":abc:">> = iolist_to_binary(join(re:split("abcabc","(abc)\\1",[]))), - <<":abc">> = iolist_to_binary(join(re:split("abcabc","([a-c]*)\\1",[trim]))), + 2}]))), + <<":abc:">> = iolist_to_binary(join(re:split("abcabc","(abc)\\1",[]))), + <<":abc">> = iolist_to_binary(join(re:split("abcabc","([a-c]*)\\1",[trim]))), <<":abc:">> = iolist_to_binary(join(re:split("abcabc","([a-c]*)\\1",[{parts, - 2}]))), - <<":abc:">> = iolist_to_binary(join(re:split("abcabc","([a-c]*)\\1",[]))), - <<":a">> = iolist_to_binary(join(re:split("a","(a)|\\1",[trim]))), + 2}]))), + <<":abc:">> = iolist_to_binary(join(re:split("abcabc","([a-c]*)\\1",[]))), + <<":a">> = iolist_to_binary(join(re:split("a","(a)|\\1",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("a","(a)|\\1",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("a","(a)|\\1",[]))), - <<"*** F:a:ilers">> = iolist_to_binary(join(re:split("*** Failers","(a)|\\1",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("a","(a)|\\1",[]))), + <<"*** F:a:ilers">> = iolist_to_binary(join(re:split("*** Failers","(a)|\\1",[trim]))), <<"*** F:a:ilers">> = iolist_to_binary(join(re:split("*** Failers","(a)|\\1",[{parts, - 2}]))), - <<"*** F:a:ilers">> = iolist_to_binary(join(re:split("*** Failers","(a)|\\1",[]))), - <<":a:b">> = iolist_to_binary(join(re:split("ab","(a)|\\1",[trim]))), + 2}]))), + <<"*** F:a:ilers">> = iolist_to_binary(join(re:split("*** Failers","(a)|\\1",[]))), + <<":a:b">> = iolist_to_binary(join(re:split("ab","(a)|\\1",[trim]))), <<":a:b">> = iolist_to_binary(join(re:split("ab","(a)|\\1",[{parts, - 2}]))), - <<":a:b">> = iolist_to_binary(join(re:split("ab","(a)|\\1",[]))), - <<"x">> = iolist_to_binary(join(re:split("x","(a)|\\1",[trim]))), + 2}]))), + <<":a:b">> = iolist_to_binary(join(re:split("ab","(a)|\\1",[]))), + <<"x">> = iolist_to_binary(join(re:split("x","(a)|\\1",[trim]))), <<"x">> = iolist_to_binary(join(re:split("x","(a)|\\1",[{parts, - 2}]))), - <<"x">> = iolist_to_binary(join(re:split("x","(a)|\\1",[]))), - <<":bb:b:b:cbc:c">> = iolist_to_binary(join(re:split("ababbbcbc","(([a-c])b*?\\2)*",[trim]))), + 2}]))), + <<"x">> = iolist_to_binary(join(re:split("x","(a)|\\1",[]))), + <<":bb:b:b:cbc:c">> = iolist_to_binary(join(re:split("ababbbcbc","(([a-c])b*?\\2)*",[trim]))), <<":bb:b:bcbc">> = iolist_to_binary(join(re:split("ababbbcbc","(([a-c])b*?\\2)*",[{parts, - 2}]))), - <<":bb:b:b:cbc:c:">> = iolist_to_binary(join(re:split("ababbbcbc","(([a-c])b*?\\2)*",[]))), - <<":cbc:c">> = iolist_to_binary(join(re:split("ababbbcbc","(([a-c])b*?\\2){3}",[trim]))), + 2}]))), + <<":bb:b:b:cbc:c:">> = iolist_to_binary(join(re:split("ababbbcbc","(([a-c])b*?\\2)*",[]))), + <<":cbc:c">> = iolist_to_binary(join(re:split("ababbbcbc","(([a-c])b*?\\2){3}",[trim]))), <<":cbc:c:">> = iolist_to_binary(join(re:split("ababbbcbc","(([a-c])b*?\\2){3}",[{parts, - 2}]))), - <<":cbc:c:">> = iolist_to_binary(join(re:split("ababbbcbc","(([a-c])b*?\\2){3}",[]))), - <<"aaaxabaxbaax:bbax:b:a">> = iolist_to_binary(join(re:split("aaaxabaxbaaxbbax","((\\3|b)\\2(a)x)+",[trim]))), + 2}]))), + <<":cbc:c:">> = iolist_to_binary(join(re:split("ababbbcbc","(([a-c])b*?\\2){3}",[]))), + <<"aaaxabaxbaax:bbax:b:a">> = iolist_to_binary(join(re:split("aaaxabaxbaaxbbax","((\\3|b)\\2(a)x)+",[trim]))), <<"aaaxabaxbaax:bbax:b:a:">> = iolist_to_binary(join(re:split("aaaxabaxbaaxbbax","((\\3|b)\\2(a)x)+",[{parts, - 2}]))), - <<"aaaxabaxbaax:bbax:b:a:">> = iolist_to_binary(join(re:split("aaaxabaxbaaxbbax","((\\3|b)\\2(a)x)+",[]))), - <<"bbaababbabaaaaa:bba:b:a">> = iolist_to_binary(join(re:split("bbaababbabaaaaabbaaaabba","((\\3|b)\\2(a)){2,}",[trim]))), + 2}]))), + <<"aaaxabaxbaax:bbax:b:a:">> = iolist_to_binary(join(re:split("aaaxabaxbaaxbbax","((\\3|b)\\2(a)x)+",[]))), + <<"bbaababbabaaaaa:bba:b:a">> = iolist_to_binary(join(re:split("bbaababbabaaaaabbaaaabba","((\\3|b)\\2(a)){2,}",[trim]))), <<"bbaababbabaaaaa:bba:b:a:">> = iolist_to_binary(join(re:split("bbaababbabaaaaabbaaaabba","((\\3|b)\\2(a)){2,}",[{parts, - 2}]))), - <<"bbaababbabaaaaa:bba:b:a:">> = iolist_to_binary(join(re:split("bbaababbabaaaaabbaaaabba","((\\3|b)\\2(a)){2,}",[]))), + 2}]))), + <<"bbaababbabaaaaa:bba:b:a:">> = iolist_to_binary(join(re:split("bbaababbabaaaaabbaaaabba","((\\3|b)\\2(a)){2,}",[]))), <<"">> = iolist_to_binary(join(re:split("ABC","abc",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ABC","abc",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ABC","abc",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ABC","abc",[caseless]))), <<"X:Y">> = iolist_to_binary(join(re:split("XABCY","abc",[caseless, - trim]))), + trim]))), <<"X:Y">> = iolist_to_binary(join(re:split("XABCY","abc",[caseless, {parts, - 2}]))), - <<"X:Y">> = iolist_to_binary(join(re:split("XABCY","abc",[caseless]))), + 2}]))), + <<"X:Y">> = iolist_to_binary(join(re:split("XABCY","abc",[caseless]))), <<"AB">> = iolist_to_binary(join(re:split("ABABC","abc",[caseless, - trim]))), + trim]))), <<"AB:">> = iolist_to_binary(join(re:split("ABABC","abc",[caseless, {parts, - 2}]))), - <<"AB:">> = iolist_to_binary(join(re:split("ABABC","abc",[caseless]))), + 2}]))), + <<"AB:">> = iolist_to_binary(join(re:split("ABABC","abc",[caseless]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc",[caseless, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc",[caseless, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc",[caseless]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc",[caseless]))), <<"aaxabxbaxbbx">> = iolist_to_binary(join(re:split("aaxabxbaxbbx","abc",[caseless, - trim]))), + trim]))), <<"aaxabxbaxbbx">> = iolist_to_binary(join(re:split("aaxabxbaxbbx","abc",[caseless, {parts, - 2}]))), - <<"aaxabxbaxbbx">> = iolist_to_binary(join(re:split("aaxabxbaxbbx","abc",[caseless]))), + 2}]))), + <<"aaxabxbaxbbx">> = iolist_to_binary(join(re:split("aaxabxbaxbbx","abc",[caseless]))), <<"XBC">> = iolist_to_binary(join(re:split("XBC","abc",[caseless, - trim]))), + trim]))), <<"XBC">> = iolist_to_binary(join(re:split("XBC","abc",[caseless, {parts, - 2}]))), - <<"XBC">> = iolist_to_binary(join(re:split("XBC","abc",[caseless]))), + 2}]))), + <<"XBC">> = iolist_to_binary(join(re:split("XBC","abc",[caseless]))), <<"AXC">> = iolist_to_binary(join(re:split("AXC","abc",[caseless, - trim]))), + trim]))), <<"AXC">> = iolist_to_binary(join(re:split("AXC","abc",[caseless, {parts, - 2}]))), - <<"AXC">> = iolist_to_binary(join(re:split("AXC","abc",[caseless]))), + 2}]))), + <<"AXC">> = iolist_to_binary(join(re:split("AXC","abc",[caseless]))), <<"ABX">> = iolist_to_binary(join(re:split("ABX","abc",[caseless, - trim]))), + trim]))), <<"ABX">> = iolist_to_binary(join(re:split("ABX","abc",[caseless, {parts, - 2}]))), - <<"ABX">> = iolist_to_binary(join(re:split("ABX","abc",[caseless]))), + 2}]))), + <<"ABX">> = iolist_to_binary(join(re:split("ABX","abc",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ABC","ab*c",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ABC","ab*c",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ABC","ab*c",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ABC","ab*c",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ABC","ab*bc",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ABC","ab*bc",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ABC","ab*bc",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ABC","ab*bc",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ABBC","ab*bc",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ABBC","ab*bc",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ABBC","ab*bc",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ABBC","ab*bc",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ABBBBC","ab*?bc",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ABBBBC","ab*?bc",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ABBBBC","ab*?bc",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ABBBBC","ab*?bc",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ABBBBC","ab{0,}?bc",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ABBBBC","ab{0,}?bc",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ABBBBC","ab{0,}?bc",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ABBBBC","ab{0,}?bc",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ABBC","ab+?bc",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ABBC","ab+?bc",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ABBC","ab+?bc",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ABBC","ab+?bc",[caseless]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab+bc",[caseless, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab+bc",[caseless, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab+bc",[caseless]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab+bc",[caseless]))), <<"ABC">> = iolist_to_binary(join(re:split("ABC","ab+bc",[caseless, - trim]))), + trim]))), <<"ABC">> = iolist_to_binary(join(re:split("ABC","ab+bc",[caseless, {parts, - 2}]))), - <<"ABC">> = iolist_to_binary(join(re:split("ABC","ab+bc",[caseless]))), + 2}]))), + <<"ABC">> = iolist_to_binary(join(re:split("ABC","ab+bc",[caseless]))), <<"ABQ">> = iolist_to_binary(join(re:split("ABQ","ab+bc",[caseless, - trim]))), + trim]))), <<"ABQ">> = iolist_to_binary(join(re:split("ABQ","ab+bc",[caseless, {parts, - 2}]))), - <<"ABQ">> = iolist_to_binary(join(re:split("ABQ","ab+bc",[caseless]))), + 2}]))), + <<"ABQ">> = iolist_to_binary(join(re:split("ABQ","ab+bc",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ABBBBC","ab+bc",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ABBBBC","ab+bc",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ABBBBC","ab+bc",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ABBBBC","ab+bc",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ABBBBC","ab{1,}?bc",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ABBBBC","ab{1,}?bc",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ABBBBC","ab{1,}?bc",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ABBBBC","ab{1,}?bc",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ABBBBC","ab{1,3}?bc",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ABBBBC","ab{1,3}?bc",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ABBBBC","ab{1,3}?bc",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ABBBBC","ab{1,3}?bc",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ABBBBC","ab{3,4}?bc",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ABBBBC","ab{3,4}?bc",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ABBBBC","ab{3,4}?bc",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ABBBBC","ab{3,4}?bc",[caseless]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab{4,5}?bc",[caseless, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab{4,5}?bc",[caseless, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab{4,5}?bc",[caseless]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab{4,5}?bc",[caseless]))), <<"ABQ">> = iolist_to_binary(join(re:split("ABQ","ab{4,5}?bc",[caseless, - trim]))), + trim]))), <<"ABQ">> = iolist_to_binary(join(re:split("ABQ","ab{4,5}?bc",[caseless, {parts, - 2}]))), - <<"ABQ">> = iolist_to_binary(join(re:split("ABQ","ab{4,5}?bc",[caseless]))), + 2}]))), + <<"ABQ">> = iolist_to_binary(join(re:split("ABQ","ab{4,5}?bc",[caseless]))), <<"ABBBBC">> = iolist_to_binary(join(re:split("ABBBBC","ab{4,5}?bc",[caseless, - trim]))), + trim]))), <<"ABBBBC">> = iolist_to_binary(join(re:split("ABBBBC","ab{4,5}?bc",[caseless, {parts, - 2}]))), - <<"ABBBBC">> = iolist_to_binary(join(re:split("ABBBBC","ab{4,5}?bc",[caseless]))), + 2}]))), + <<"ABBBBC">> = iolist_to_binary(join(re:split("ABBBBC","ab{4,5}?bc",[caseless]))), ok. run21() -> <<"">> = iolist_to_binary(join(re:split("ABBC","ab??bc",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ABBC","ab??bc",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ABBC","ab??bc",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ABBC","ab??bc",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ABC","ab??bc",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ABC","ab??bc",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ABC","ab??bc",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ABC","ab??bc",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ABC","ab{0,1}?bc",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ABC","ab{0,1}?bc",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ABC","ab{0,1}?bc",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ABC","ab{0,1}?bc",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ABC","ab??c",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ABC","ab??c",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ABC","ab??c",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ABC","ab??c",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ABC","ab{0,1}?c",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ABC","ab{0,1}?c",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ABC","ab{0,1}?c",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ABC","ab{0,1}?c",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ABC","^abc$",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ABC","^abc$",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ABC","^abc$",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ABC","^abc$",[caseless]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^abc$",[caseless, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^abc$",[caseless, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^abc$",[caseless]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^abc$",[caseless]))), <<"ABBBBC">> = iolist_to_binary(join(re:split("ABBBBC","^abc$",[caseless, - trim]))), + trim]))), <<"ABBBBC">> = iolist_to_binary(join(re:split("ABBBBC","^abc$",[caseless, {parts, - 2}]))), - <<"ABBBBC">> = iolist_to_binary(join(re:split("ABBBBC","^abc$",[caseless]))), + 2}]))), + <<"ABBBBC">> = iolist_to_binary(join(re:split("ABBBBC","^abc$",[caseless]))), <<"ABCC">> = iolist_to_binary(join(re:split("ABCC","^abc$",[caseless, - trim]))), + trim]))), <<"ABCC">> = iolist_to_binary(join(re:split("ABCC","^abc$",[caseless, {parts, - 2}]))), - <<"ABCC">> = iolist_to_binary(join(re:split("ABCC","^abc$",[caseless]))), + 2}]))), + <<"ABCC">> = iolist_to_binary(join(re:split("ABCC","^abc$",[caseless]))), <<":C">> = iolist_to_binary(join(re:split("ABCC","^abc",[caseless, - trim]))), + trim]))), <<":C">> = iolist_to_binary(join(re:split("ABCC","^abc",[caseless, {parts, - 2}]))), - <<":C">> = iolist_to_binary(join(re:split("ABCC","^abc",[caseless]))), + 2}]))), + <<":C">> = iolist_to_binary(join(re:split("ABCC","^abc",[caseless]))), <<"A">> = iolist_to_binary(join(re:split("AABC","abc$",[caseless, - trim]))), + trim]))), <<"A:">> = iolist_to_binary(join(re:split("AABC","abc$",[caseless, {parts, - 2}]))), - <<"A:">> = iolist_to_binary(join(re:split("AABC","abc$",[caseless]))), + 2}]))), + <<"A:">> = iolist_to_binary(join(re:split("AABC","abc$",[caseless]))), <<"ABC">> = iolist_to_binary(join(re:split("ABC","^",[caseless, - trim]))), + trim]))), <<"ABC">> = iolist_to_binary(join(re:split("ABC","^",[caseless, {parts, - 2}]))), - <<"ABC">> = iolist_to_binary(join(re:split("ABC","^",[caseless]))), + 2}]))), + <<"ABC">> = iolist_to_binary(join(re:split("ABC","^",[caseless]))), <<"ABC">> = iolist_to_binary(join(re:split("ABC","$",[caseless, - trim]))), + trim]))), <<"ABC:">> = iolist_to_binary(join(re:split("ABC","$",[caseless, {parts, - 2}]))), - <<"ABC:">> = iolist_to_binary(join(re:split("ABC","$",[caseless]))), + 2}]))), + <<"ABC:">> = iolist_to_binary(join(re:split("ABC","$",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ABC","a.c",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ABC","a.c",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ABC","a.c",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ABC","a.c",[caseless]))), <<"">> = iolist_to_binary(join(re:split("AXC","a.c",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("AXC","a.c",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("AXC","a.c",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("AXC","a.c",[caseless]))), <<"">> = iolist_to_binary(join(re:split("AXYZC","a.*?c",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("AXYZC","a.*?c",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("AXYZC","a.*?c",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("AXYZC","a.*?c",[caseless]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a.*c",[caseless, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a.*c",[caseless, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a.*c",[caseless]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a.*c",[caseless]))), <<"">> = iolist_to_binary(join(re:split("AABC","a.*c",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("AABC","a.*c",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("AABC","a.*c",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("AABC","a.*c",[caseless]))), <<"AXYZD">> = iolist_to_binary(join(re:split("AXYZD","a.*c",[caseless, - trim]))), + trim]))), <<"AXYZD">> = iolist_to_binary(join(re:split("AXYZD","a.*c",[caseless, {parts, - 2}]))), - <<"AXYZD">> = iolist_to_binary(join(re:split("AXYZD","a.*c",[caseless]))), + 2}]))), + <<"AXYZD">> = iolist_to_binary(join(re:split("AXYZD","a.*c",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ABD","a[bc]d",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ABD","a[bc]d",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ABD","a[bc]d",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ABD","a[bc]d",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ACE","a[b-d]e",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ACE","a[b-d]e",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ACE","a[b-d]e",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ACE","a[b-d]e",[caseless]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[b-d]e",[caseless, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[b-d]e",[caseless, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[b-d]e",[caseless]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[b-d]e",[caseless]))), <<"ABC">> = iolist_to_binary(join(re:split("ABC","a[b-d]e",[caseless, - trim]))), + trim]))), <<"ABC">> = iolist_to_binary(join(re:split("ABC","a[b-d]e",[caseless, {parts, - 2}]))), - <<"ABC">> = iolist_to_binary(join(re:split("ABC","a[b-d]e",[caseless]))), + 2}]))), + <<"ABC">> = iolist_to_binary(join(re:split("ABC","a[b-d]e",[caseless]))), <<"ABD">> = iolist_to_binary(join(re:split("ABD","a[b-d]e",[caseless, - trim]))), + trim]))), <<"ABD">> = iolist_to_binary(join(re:split("ABD","a[b-d]e",[caseless, {parts, - 2}]))), - <<"ABD">> = iolist_to_binary(join(re:split("ABD","a[b-d]e",[caseless]))), + 2}]))), + <<"ABD">> = iolist_to_binary(join(re:split("ABD","a[b-d]e",[caseless]))), <<"A">> = iolist_to_binary(join(re:split("AAC","a[b-d]",[caseless, - trim]))), + trim]))), <<"A:">> = iolist_to_binary(join(re:split("AAC","a[b-d]",[caseless, {parts, - 2}]))), - <<"A:">> = iolist_to_binary(join(re:split("AAC","a[b-d]",[caseless]))), + 2}]))), + <<"A:">> = iolist_to_binary(join(re:split("AAC","a[b-d]",[caseless]))), <<"">> = iolist_to_binary(join(re:split("A-","a[-b]",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("A-","a[-b]",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("A-","a[-b]",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("A-","a[-b]",[caseless]))), <<"">> = iolist_to_binary(join(re:split("A-","a[b-]",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("A-","a[b-]",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("A-","a[b-]",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("A-","a[b-]",[caseless]))), <<"">> = iolist_to_binary(join(re:split("A]","a]",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("A]","a]",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("A]","a]",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("A]","a]",[caseless]))), ok. run22() -> <<"">> = iolist_to_binary(join(re:split("A]B","a[]]b",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("A]B","a[]]b",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("A]B","a[]]b",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("A]B","a[]]b",[caseless]))), <<"">> = iolist_to_binary(join(re:split("AED","a[^bc]d",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("AED","a[^bc]d",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("AED","a[^bc]d",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("AED","a[^bc]d",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ADC","a[^-b]c",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ADC","a[^-b]c",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ADC","a[^-b]c",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ADC","a[^-b]c",[caseless]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[^-b]c",[caseless, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[^-b]c",[caseless, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[^-b]c",[caseless]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[^-b]c",[caseless]))), <<"ABD">> = iolist_to_binary(join(re:split("ABD","a[^-b]c",[caseless, - trim]))), + trim]))), <<"ABD">> = iolist_to_binary(join(re:split("ABD","a[^-b]c",[caseless, {parts, - 2}]))), - <<"ABD">> = iolist_to_binary(join(re:split("ABD","a[^-b]c",[caseless]))), + 2}]))), + <<"ABD">> = iolist_to_binary(join(re:split("ABD","a[^-b]c",[caseless]))), <<"A-C">> = iolist_to_binary(join(re:split("A-C","a[^-b]c",[caseless, - trim]))), + trim]))), <<"A-C">> = iolist_to_binary(join(re:split("A-C","a[^-b]c",[caseless, {parts, - 2}]))), - <<"A-C">> = iolist_to_binary(join(re:split("A-C","a[^-b]c",[caseless]))), + 2}]))), + <<"A-C">> = iolist_to_binary(join(re:split("A-C","a[^-b]c",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ADC","a[^]b]c",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ADC","a[^]b]c",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ADC","a[^]b]c",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ADC","a[^]b]c",[caseless]))), <<":C">> = iolist_to_binary(join(re:split("ABC","ab|cd",[caseless, - trim]))), + trim]))), <<":C">> = iolist_to_binary(join(re:split("ABC","ab|cd",[caseless, {parts, - 2}]))), - <<":C">> = iolist_to_binary(join(re:split("ABC","ab|cd",[caseless]))), + 2}]))), + <<":C">> = iolist_to_binary(join(re:split("ABC","ab|cd",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ABCD","ab|cd",[caseless, - trim]))), + trim]))), <<":CD">> = iolist_to_binary(join(re:split("ABCD","ab|cd",[caseless, {parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("ABCD","ab|cd",[caseless]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("ABCD","ab|cd",[caseless]))), <<"D">> = iolist_to_binary(join(re:split("DEF","()ef",[caseless, - trim]))), + trim]))), <<"D::">> = iolist_to_binary(join(re:split("DEF","()ef",[caseless, {parts, - 2}]))), - <<"D::">> = iolist_to_binary(join(re:split("DEF","()ef",[caseless]))), + 2}]))), + <<"D::">> = iolist_to_binary(join(re:split("DEF","()ef",[caseless]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","$b",[caseless, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","$b",[caseless, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","$b",[caseless]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","$b",[caseless]))), <<"A]C">> = iolist_to_binary(join(re:split("A]C","$b",[caseless, - trim]))), + trim]))), <<"A]C">> = iolist_to_binary(join(re:split("A]C","$b",[caseless, {parts, - 2}]))), - <<"A]C">> = iolist_to_binary(join(re:split("A]C","$b",[caseless]))), + 2}]))), + <<"A]C">> = iolist_to_binary(join(re:split("A]C","$b",[caseless]))), <<"B">> = iolist_to_binary(join(re:split("B","$b",[caseless, - trim]))), + trim]))), <<"B">> = iolist_to_binary(join(re:split("B","$b",[caseless, {parts, - 2}]))), - <<"B">> = iolist_to_binary(join(re:split("B","$b",[caseless]))), + 2}]))), + <<"B">> = iolist_to_binary(join(re:split("B","$b",[caseless]))), <<"">> = iolist_to_binary(join(re:split("A(B","a\\(b",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("A(B","a\\(b",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("A(B","a\\(b",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("A(B","a\\(b",[caseless]))), <<"">> = iolist_to_binary(join(re:split("AB","a\\(*b",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("AB","a\\(*b",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("AB","a\\(*b",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("AB","a\\(*b",[caseless]))), <<"">> = iolist_to_binary(join(re:split("A((B","a\\(*b",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("A((B","a\\(*b",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("A((B","a\\(*b",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("A((B","a\\(*b",[caseless]))), <<"A">> = iolist_to_binary(join(re:split("A","a\\\\b",[caseless, notbol, - trim]))), + trim]))), <<"A">> = iolist_to_binary(join(re:split("A","a\\\\b",[caseless, notbol, {parts, - 2}]))), + 2}]))), <<"A">> = iolist_to_binary(join(re:split("A","a\\\\b",[caseless, - notbol]))), + notbol]))), <<":A:A:BC">> = iolist_to_binary(join(re:split("ABC","((a))",[caseless, - trim]))), + trim]))), <<":A:A:BC">> = iolist_to_binary(join(re:split("ABC","((a))",[caseless, {parts, - 2}]))), - <<":A:A:BC">> = iolist_to_binary(join(re:split("ABC","((a))",[caseless]))), + 2}]))), + <<":A:A:BC">> = iolist_to_binary(join(re:split("ABC","((a))",[caseless]))), <<":A:C">> = iolist_to_binary(join(re:split("ABC","(a)b(c)",[caseless, - trim]))), + trim]))), <<":A:C:">> = iolist_to_binary(join(re:split("ABC","(a)b(c)",[caseless, {parts, - 2}]))), - <<":A:C:">> = iolist_to_binary(join(re:split("ABC","(a)b(c)",[caseless]))), + 2}]))), + <<":A:C:">> = iolist_to_binary(join(re:split("ABC","(a)b(c)",[caseless]))), <<"AABB">> = iolist_to_binary(join(re:split("AABBABC","a+b+c",[caseless, - trim]))), + trim]))), <<"AABB:">> = iolist_to_binary(join(re:split("AABBABC","a+b+c",[caseless, {parts, - 2}]))), - <<"AABB:">> = iolist_to_binary(join(re:split("AABBABC","a+b+c",[caseless]))), + 2}]))), + <<"AABB:">> = iolist_to_binary(join(re:split("AABBABC","a+b+c",[caseless]))), <<"AABB">> = iolist_to_binary(join(re:split("AABBABC","a{1,}b{1,}c",[caseless, - trim]))), + trim]))), <<"AABB:">> = iolist_to_binary(join(re:split("AABBABC","a{1,}b{1,}c",[caseless, {parts, - 2}]))), - <<"AABB:">> = iolist_to_binary(join(re:split("AABBABC","a{1,}b{1,}c",[caseless]))), + 2}]))), + <<"AABB:">> = iolist_to_binary(join(re:split("AABBABC","a{1,}b{1,}c",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ABCABC","a.+?c",[caseless, - trim]))), + trim]))), <<":ABC">> = iolist_to_binary(join(re:split("ABCABC","a.+?c",[caseless, {parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("ABCABC","a.+?c",[caseless]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("ABCABC","a.+?c",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ABCABC","a.*?c",[caseless, - trim]))), + trim]))), <<":ABC">> = iolist_to_binary(join(re:split("ABCABC","a.*?c",[caseless, {parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("ABCABC","a.*?c",[caseless]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("ABCABC","a.*?c",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ABCABC","a.{0,5}?c",[caseless, - trim]))), + trim]))), <<":ABC">> = iolist_to_binary(join(re:split("ABCABC","a.{0,5}?c",[caseless, {parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("ABCABC","a.{0,5}?c",[caseless]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("ABCABC","a.{0,5}?c",[caseless]))), <<":B">> = iolist_to_binary(join(re:split("AB","(a+|b)*",[caseless, - trim]))), + trim]))), <<":B:">> = iolist_to_binary(join(re:split("AB","(a+|b)*",[caseless, {parts, - 2}]))), - <<":B:">> = iolist_to_binary(join(re:split("AB","(a+|b)*",[caseless]))), + 2}]))), + <<":B:">> = iolist_to_binary(join(re:split("AB","(a+|b)*",[caseless]))), <<":B">> = iolist_to_binary(join(re:split("AB","(a+|b){0,}",[caseless, - trim]))), + trim]))), <<":B:">> = iolist_to_binary(join(re:split("AB","(a+|b){0,}",[caseless, {parts, - 2}]))), - <<":B:">> = iolist_to_binary(join(re:split("AB","(a+|b){0,}",[caseless]))), + 2}]))), + <<":B:">> = iolist_to_binary(join(re:split("AB","(a+|b){0,}",[caseless]))), <<":B">> = iolist_to_binary(join(re:split("AB","(a+|b)+",[caseless, - trim]))), + trim]))), <<":B:">> = iolist_to_binary(join(re:split("AB","(a+|b)+",[caseless, {parts, - 2}]))), - <<":B:">> = iolist_to_binary(join(re:split("AB","(a+|b)+",[caseless]))), + 2}]))), + <<":B:">> = iolist_to_binary(join(re:split("AB","(a+|b)+",[caseless]))), ok. run23() -> <<":B">> = iolist_to_binary(join(re:split("AB","(a+|b){1,}",[caseless, - trim]))), + trim]))), <<":B:">> = iolist_to_binary(join(re:split("AB","(a+|b){1,}",[caseless, {parts, - 2}]))), - <<":B:">> = iolist_to_binary(join(re:split("AB","(a+|b){1,}",[caseless]))), + 2}]))), + <<":B:">> = iolist_to_binary(join(re:split("AB","(a+|b){1,}",[caseless]))), <<":A::B">> = iolist_to_binary(join(re:split("AB","(a+|b)?",[caseless, - trim]))), + trim]))), <<":A:B">> = iolist_to_binary(join(re:split("AB","(a+|b)?",[caseless, {parts, - 2}]))), - <<":A::B:">> = iolist_to_binary(join(re:split("AB","(a+|b)?",[caseless]))), + 2}]))), + <<":A::B:">> = iolist_to_binary(join(re:split("AB","(a+|b)?",[caseless]))), <<":A::B">> = iolist_to_binary(join(re:split("AB","(a+|b){0,1}",[caseless, - trim]))), + trim]))), <<":A:B">> = iolist_to_binary(join(re:split("AB","(a+|b){0,1}",[caseless, {parts, - 2}]))), - <<":A::B:">> = iolist_to_binary(join(re:split("AB","(a+|b){0,1}",[caseless]))), + 2}]))), + <<":A::B:">> = iolist_to_binary(join(re:split("AB","(a+|b){0,1}",[caseless]))), <<":A::B">> = iolist_to_binary(join(re:split("AB","(a+|b){0,1}?",[caseless, - trim]))), + trim]))), <<":A:B">> = iolist_to_binary(join(re:split("AB","(a+|b){0,1}?",[caseless, {parts, - 2}]))), - <<":A::B:">> = iolist_to_binary(join(re:split("AB","(a+|b){0,1}?",[caseless]))), + 2}]))), + <<":A::B:">> = iolist_to_binary(join(re:split("AB","(a+|b){0,1}?",[caseless]))), <<"">> = iolist_to_binary(join(re:split("CDE","[^ab]*",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("CDE","[^ab]*",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("CDE","[^ab]*",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("CDE","[^ab]*",[caseless]))), <<":C">> = iolist_to_binary(join(re:split("ABBBCD","([abc])*d",[caseless, - trim]))), + trim]))), <<":C:">> = iolist_to_binary(join(re:split("ABBBCD","([abc])*d",[caseless, {parts, - 2}]))), - <<":C:">> = iolist_to_binary(join(re:split("ABBBCD","([abc])*d",[caseless]))), + 2}]))), + <<":C:">> = iolist_to_binary(join(re:split("ABBBCD","([abc])*d",[caseless]))), <<":A">> = iolist_to_binary(join(re:split("ABCD","([abc])*bcd",[caseless, - trim]))), + trim]))), <<":A:">> = iolist_to_binary(join(re:split("ABCD","([abc])*bcd",[caseless, {parts, - 2}]))), - <<":A:">> = iolist_to_binary(join(re:split("ABCD","([abc])*bcd",[caseless]))), + 2}]))), + <<":A:">> = iolist_to_binary(join(re:split("ABCD","([abc])*bcd",[caseless]))), <<"">> = iolist_to_binary(join(re:split("E","a|b|c|d|e",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("E","a|b|c|d|e",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("E","a|b|c|d|e",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("E","a|b|c|d|e",[caseless]))), <<":E">> = iolist_to_binary(join(re:split("EF","(a|b|c|d|e)f",[caseless, - trim]))), + trim]))), <<":E:">> = iolist_to_binary(join(re:split("EF","(a|b|c|d|e)f",[caseless, {parts, - 2}]))), - <<":E:">> = iolist_to_binary(join(re:split("EF","(a|b|c|d|e)f",[caseless]))), + 2}]))), + <<":E:">> = iolist_to_binary(join(re:split("EF","(a|b|c|d|e)f",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ABCDEFG","abcd*efg",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ABCDEFG","abcd*efg",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ABCDEFG","abcd*efg",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ABCDEFG","abcd*efg",[caseless]))), <<"X:Y:Z">> = iolist_to_binary(join(re:split("XABYABBBZ","ab*",[caseless, - trim]))), + trim]))), <<"X:YABBBZ">> = iolist_to_binary(join(re:split("XABYABBBZ","ab*",[caseless, {parts, - 2}]))), - <<"X:Y:Z">> = iolist_to_binary(join(re:split("XABYABBBZ","ab*",[caseless]))), + 2}]))), + <<"X:Y:Z">> = iolist_to_binary(join(re:split("XABYABBBZ","ab*",[caseless]))), <<"X:Y:Z">> = iolist_to_binary(join(re:split("XAYABBBZ","ab*",[caseless, - trim]))), + trim]))), <<"X:YABBBZ">> = iolist_to_binary(join(re:split("XAYABBBZ","ab*",[caseless, {parts, - 2}]))), - <<"X:Y:Z">> = iolist_to_binary(join(re:split("XAYABBBZ","ab*",[caseless]))), + 2}]))), + <<"X:Y:Z">> = iolist_to_binary(join(re:split("XAYABBBZ","ab*",[caseless]))), <<"AB:CD">> = iolist_to_binary(join(re:split("ABCDE","(ab|cd)e",[caseless, - trim]))), + trim]))), <<"AB:CD:">> = iolist_to_binary(join(re:split("ABCDE","(ab|cd)e",[caseless, {parts, - 2}]))), - <<"AB:CD:">> = iolist_to_binary(join(re:split("ABCDE","(ab|cd)e",[caseless]))), + 2}]))), + <<"AB:CD:">> = iolist_to_binary(join(re:split("ABCDE","(ab|cd)e",[caseless]))), <<"">> = iolist_to_binary(join(re:split("HIJ","[abhgefdc]ij",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("HIJ","[abhgefdc]ij",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("HIJ","[abhgefdc]ij",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("HIJ","[abhgefdc]ij",[caseless]))), <<"ABCDE">> = iolist_to_binary(join(re:split("ABCDE","^(ab|cd)e",[caseless, - trim]))), + trim]))), <<"ABCDE">> = iolist_to_binary(join(re:split("ABCDE","^(ab|cd)e",[caseless, {parts, - 2}]))), - <<"ABCDE">> = iolist_to_binary(join(re:split("ABCDE","^(ab|cd)e",[caseless]))), + 2}]))), + <<"ABCDE">> = iolist_to_binary(join(re:split("ABCDE","^(ab|cd)e",[caseless]))), <<"ABCD">> = iolist_to_binary(join(re:split("ABCDEF","(abc|)ef",[caseless, - trim]))), + trim]))), <<"ABCD::">> = iolist_to_binary(join(re:split("ABCDEF","(abc|)ef",[caseless, {parts, - 2}]))), - <<"ABCD::">> = iolist_to_binary(join(re:split("ABCDEF","(abc|)ef",[caseless]))), + 2}]))), + <<"ABCD::">> = iolist_to_binary(join(re:split("ABCDEF","(abc|)ef",[caseless]))), <<"A:B">> = iolist_to_binary(join(re:split("ABCD","(a|b)c*d",[caseless, - trim]))), + trim]))), <<"A:B:">> = iolist_to_binary(join(re:split("ABCD","(a|b)c*d",[caseless, {parts, - 2}]))), - <<"A:B:">> = iolist_to_binary(join(re:split("ABCD","(a|b)c*d",[caseless]))), + 2}]))), + <<"A:B:">> = iolist_to_binary(join(re:split("ABCD","(a|b)c*d",[caseless]))), <<":A">> = iolist_to_binary(join(re:split("ABC","(ab|ab*)bc",[caseless, - trim]))), + trim]))), <<":A:">> = iolist_to_binary(join(re:split("ABC","(ab|ab*)bc",[caseless, {parts, - 2}]))), - <<":A:">> = iolist_to_binary(join(re:split("ABC","(ab|ab*)bc",[caseless]))), + 2}]))), + <<":A:">> = iolist_to_binary(join(re:split("ABC","(ab|ab*)bc",[caseless]))), <<":BC">> = iolist_to_binary(join(re:split("ABC","a([bc]*)c*",[caseless, - trim]))), + trim]))), <<":BC:">> = iolist_to_binary(join(re:split("ABC","a([bc]*)c*",[caseless, {parts, - 2}]))), - <<":BC:">> = iolist_to_binary(join(re:split("ABC","a([bc]*)c*",[caseless]))), + 2}]))), + <<":BC:">> = iolist_to_binary(join(re:split("ABC","a([bc]*)c*",[caseless]))), ok. run24() -> <<":BC:D">> = iolist_to_binary(join(re:split("ABCD","a([bc]*)(c*d)",[caseless, - trim]))), + trim]))), <<":BC:D:">> = iolist_to_binary(join(re:split("ABCD","a([bc]*)(c*d)",[caseless, {parts, - 2}]))), - <<":BC:D:">> = iolist_to_binary(join(re:split("ABCD","a([bc]*)(c*d)",[caseless]))), + 2}]))), + <<":BC:D:">> = iolist_to_binary(join(re:split("ABCD","a([bc]*)(c*d)",[caseless]))), <<":BC:D">> = iolist_to_binary(join(re:split("ABCD","a([bc]+)(c*d)",[caseless, - trim]))), + trim]))), <<":BC:D:">> = iolist_to_binary(join(re:split("ABCD","a([bc]+)(c*d)",[caseless, {parts, - 2}]))), - <<":BC:D:">> = iolist_to_binary(join(re:split("ABCD","a([bc]+)(c*d)",[caseless]))), + 2}]))), + <<":BC:D:">> = iolist_to_binary(join(re:split("ABCD","a([bc]+)(c*d)",[caseless]))), <<":B:CD">> = iolist_to_binary(join(re:split("ABCD","a([bc]*)(c+d)",[caseless, - trim]))), + trim]))), <<":B:CD:">> = iolist_to_binary(join(re:split("ABCD","a([bc]*)(c+d)",[caseless, {parts, - 2}]))), - <<":B:CD:">> = iolist_to_binary(join(re:split("ABCD","a([bc]*)(c+d)",[caseless]))), + 2}]))), + <<":B:CD:">> = iolist_to_binary(join(re:split("ABCD","a([bc]*)(c+d)",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ADCDCDE","a[bcd]*dcdcde",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ADCDCDE","a[bcd]*dcdcde",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ADCDCDE","a[bcd]*dcdcde",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ADCDCDE","a[bcd]*dcdcde",[caseless]))), <<":AB">> = iolist_to_binary(join(re:split("ABC","(ab|a)b*c",[caseless, - trim]))), + trim]))), <<":AB:">> = iolist_to_binary(join(re:split("ABC","(ab|a)b*c",[caseless, {parts, - 2}]))), - <<":AB:">> = iolist_to_binary(join(re:split("ABC","(ab|a)b*c",[caseless]))), + 2}]))), + <<":AB:">> = iolist_to_binary(join(re:split("ABC","(ab|a)b*c",[caseless]))), <<":ABC:A:B:D">> = iolist_to_binary(join(re:split("ABCD","((a)(b)c)(d)",[caseless, - trim]))), + trim]))), <<":ABC:A:B:D:">> = iolist_to_binary(join(re:split("ABCD","((a)(b)c)(d)",[caseless, {parts, - 2}]))), - <<":ABC:A:B:D:">> = iolist_to_binary(join(re:split("ABCD","((a)(b)c)(d)",[caseless]))), + 2}]))), + <<":ABC:A:B:D:">> = iolist_to_binary(join(re:split("ABCD","((a)(b)c)(d)",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ALPHA","[a-zA-Z_][a-zA-Z0-9_]*",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ALPHA","[a-zA-Z_][a-zA-Z0-9_]*",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ALPHA","[a-zA-Z_][a-zA-Z0-9_]*",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ALPHA","[a-zA-Z_][a-zA-Z0-9_]*",[caseless]))), <<"A">> = iolist_to_binary(join(re:split("ABH","^a(bc+|b[eh])g|.h$",[caseless, - trim]))), + trim]))), <<"A::">> = iolist_to_binary(join(re:split("ABH","^a(bc+|b[eh])g|.h$",[caseless, {parts, - 2}]))), - <<"A::">> = iolist_to_binary(join(re:split("ABH","^a(bc+|b[eh])g|.h$",[caseless]))), + 2}]))), + <<"A::">> = iolist_to_binary(join(re:split("ABH","^a(bc+|b[eh])g|.h$",[caseless]))), <<":EFFGZ">> = iolist_to_binary(join(re:split("EFFGZ","(bc+d$|ef*g.|h?i(j|k))",[caseless, - trim]))), + trim]))), <<":EFFGZ::">> = iolist_to_binary(join(re:split("EFFGZ","(bc+d$|ef*g.|h?i(j|k))",[caseless, {parts, - 2}]))), - <<":EFFGZ::">> = iolist_to_binary(join(re:split("EFFGZ","(bc+d$|ef*g.|h?i(j|k))",[caseless]))), + 2}]))), + <<":EFFGZ::">> = iolist_to_binary(join(re:split("EFFGZ","(bc+d$|ef*g.|h?i(j|k))",[caseless]))), <<":IJ:J">> = iolist_to_binary(join(re:split("IJ","(bc+d$|ef*g.|h?i(j|k))",[caseless, - trim]))), + trim]))), <<":IJ:J:">> = iolist_to_binary(join(re:split("IJ","(bc+d$|ef*g.|h?i(j|k))",[caseless, {parts, - 2}]))), - <<":IJ:J:">> = iolist_to_binary(join(re:split("IJ","(bc+d$|ef*g.|h?i(j|k))",[caseless]))), + 2}]))), + <<":IJ:J:">> = iolist_to_binary(join(re:split("IJ","(bc+d$|ef*g.|h?i(j|k))",[caseless]))), <<"R:EFFGZ">> = iolist_to_binary(join(re:split("REFFGZ","(bc+d$|ef*g.|h?i(j|k))",[caseless, - trim]))), + trim]))), <<"R:EFFGZ::">> = iolist_to_binary(join(re:split("REFFGZ","(bc+d$|ef*g.|h?i(j|k))",[caseless, {parts, - 2}]))), - <<"R:EFFGZ::">> = iolist_to_binary(join(re:split("REFFGZ","(bc+d$|ef*g.|h?i(j|k))",[caseless]))), + 2}]))), + <<"R:EFFGZ::">> = iolist_to_binary(join(re:split("REFFGZ","(bc+d$|ef*g.|h?i(j|k))",[caseless]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(bc+d$|ef*g.|h?i(j|k))",[caseless, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(bc+d$|ef*g.|h?i(j|k))",[caseless, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(bc+d$|ef*g.|h?i(j|k))",[caseless]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(bc+d$|ef*g.|h?i(j|k))",[caseless]))), <<"ADCDCDE">> = iolist_to_binary(join(re:split("ADCDCDE","(bc+d$|ef*g.|h?i(j|k))",[caseless, - trim]))), + trim]))), <<"ADCDCDE">> = iolist_to_binary(join(re:split("ADCDCDE","(bc+d$|ef*g.|h?i(j|k))",[caseless, {parts, - 2}]))), - <<"ADCDCDE">> = iolist_to_binary(join(re:split("ADCDCDE","(bc+d$|ef*g.|h?i(j|k))",[caseless]))), + 2}]))), + <<"ADCDCDE">> = iolist_to_binary(join(re:split("ADCDCDE","(bc+d$|ef*g.|h?i(j|k))",[caseless]))), <<"EFFG">> = iolist_to_binary(join(re:split("EFFG","(bc+d$|ef*g.|h?i(j|k))",[caseless, - trim]))), + trim]))), <<"EFFG">> = iolist_to_binary(join(re:split("EFFG","(bc+d$|ef*g.|h?i(j|k))",[caseless, {parts, - 2}]))), - <<"EFFG">> = iolist_to_binary(join(re:split("EFFG","(bc+d$|ef*g.|h?i(j|k))",[caseless]))), + 2}]))), + <<"EFFG">> = iolist_to_binary(join(re:split("EFFG","(bc+d$|ef*g.|h?i(j|k))",[caseless]))), <<"BCDD">> = iolist_to_binary(join(re:split("BCDD","(bc+d$|ef*g.|h?i(j|k))",[caseless, - trim]))), + trim]))), <<"BCDD">> = iolist_to_binary(join(re:split("BCDD","(bc+d$|ef*g.|h?i(j|k))",[caseless, {parts, - 2}]))), - <<"BCDD">> = iolist_to_binary(join(re:split("BCDD","(bc+d$|ef*g.|h?i(j|k))",[caseless]))), + 2}]))), + <<"BCDD">> = iolist_to_binary(join(re:split("BCDD","(bc+d$|ef*g.|h?i(j|k))",[caseless]))), <<":A:A:A:A:A:A:A:A:A:A">> = iolist_to_binary(join(re:split("A","((((((((((a))))))))))",[caseless, - trim]))), + trim]))), <<":A:A:A:A:A:A:A:A:A:A:">> = iolist_to_binary(join(re:split("A","((((((((((a))))))))))",[caseless, {parts, - 2}]))), - <<":A:A:A:A:A:A:A:A:A:A:">> = iolist_to_binary(join(re:split("A","((((((((((a))))))))))",[caseless]))), + 2}]))), + <<":A:A:A:A:A:A:A:A:A:A:">> = iolist_to_binary(join(re:split("A","((((((((((a))))))))))",[caseless]))), <<":A:A:A:A:A:A:A:A:A:A">> = iolist_to_binary(join(re:split("AA","((((((((((a))))))))))\\10",[caseless, - trim]))), + trim]))), <<":A:A:A:A:A:A:A:A:A:A:">> = iolist_to_binary(join(re:split("AA","((((((((((a))))))))))\\10",[caseless, {parts, - 2}]))), - <<":A:A:A:A:A:A:A:A:A:A:">> = iolist_to_binary(join(re:split("AA","((((((((((a))))))))))\\10",[caseless]))), + 2}]))), + <<":A:A:A:A:A:A:A:A:A:A:">> = iolist_to_binary(join(re:split("AA","((((((((((a))))))))))\\10",[caseless]))), <<":A:A:A:A:A:A:A:A:A">> = iolist_to_binary(join(re:split("A","(((((((((a)))))))))",[caseless, - trim]))), + trim]))), <<":A:A:A:A:A:A:A:A:A:">> = iolist_to_binary(join(re:split("A","(((((((((a)))))))))",[caseless, {parts, - 2}]))), - <<":A:A:A:A:A:A:A:A:A:">> = iolist_to_binary(join(re:split("A","(((((((((a)))))))))",[caseless]))), + 2}]))), + <<":A:A:A:A:A:A:A:A:A:">> = iolist_to_binary(join(re:split("A","(((((((((a)))))))))",[caseless]))), <<":A">> = iolist_to_binary(join(re:split("A","(?:(?:(?:(?:(?:(?:(?:(?:(?:(a))))))))))",[caseless, - trim]))), + trim]))), <<":A:">> = iolist_to_binary(join(re:split("A","(?:(?:(?:(?:(?:(?:(?:(?:(?:(a))))))))))",[caseless, {parts, - 2}]))), - <<":A:">> = iolist_to_binary(join(re:split("A","(?:(?:(?:(?:(?:(?:(?:(?:(?:(a))))))))))",[caseless]))), + 2}]))), + <<":A:">> = iolist_to_binary(join(re:split("A","(?:(?:(?:(?:(?:(?:(?:(?:(?:(a))))))))))",[caseless]))), <<":C">> = iolist_to_binary(join(re:split("C","(?:(?:(?:(?:(?:(?:(?:(?:(?:(a|b|c))))))))))",[caseless, - trim]))), + trim]))), <<":C:">> = iolist_to_binary(join(re:split("C","(?:(?:(?:(?:(?:(?:(?:(?:(?:(a|b|c))))))))))",[caseless, {parts, - 2}]))), - <<":C:">> = iolist_to_binary(join(re:split("C","(?:(?:(?:(?:(?:(?:(?:(?:(?:(a|b|c))))))))))",[caseless]))), + 2}]))), + <<":C:">> = iolist_to_binary(join(re:split("C","(?:(?:(?:(?:(?:(?:(?:(?:(?:(a|b|c))))))))))",[caseless]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","multiple words of text",[caseless, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","multiple words of text",[caseless, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","multiple words of text",[caseless]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","multiple words of text",[caseless]))), <<"AA">> = iolist_to_binary(join(re:split("AA","multiple words of text",[caseless, - trim]))), + trim]))), <<"AA">> = iolist_to_binary(join(re:split("AA","multiple words of text",[caseless, {parts, - 2}]))), - <<"AA">> = iolist_to_binary(join(re:split("AA","multiple words of text",[caseless]))), + 2}]))), + <<"AA">> = iolist_to_binary(join(re:split("AA","multiple words of text",[caseless]))), <<"UH-UH">> = iolist_to_binary(join(re:split("UH-UH","multiple words of text",[caseless, - trim]))), + trim]))), <<"UH-UH">> = iolist_to_binary(join(re:split("UH-UH","multiple words of text",[caseless, {parts, - 2}]))), - <<"UH-UH">> = iolist_to_binary(join(re:split("UH-UH","multiple words of text",[caseless]))), + 2}]))), + <<"UH-UH">> = iolist_to_binary(join(re:split("UH-UH","multiple words of text",[caseless]))), <<":, YEAH">> = iolist_to_binary(join(re:split("MULTIPLE WORDS, YEAH","multiple words",[caseless, - trim]))), + trim]))), <<":, YEAH">> = iolist_to_binary(join(re:split("MULTIPLE WORDS, YEAH","multiple words",[caseless, {parts, - 2}]))), - <<":, YEAH">> = iolist_to_binary(join(re:split("MULTIPLE WORDS, YEAH","multiple words",[caseless]))), + 2}]))), + <<":, YEAH">> = iolist_to_binary(join(re:split("MULTIPLE WORDS, YEAH","multiple words",[caseless]))), <<":AB:DE">> = iolist_to_binary(join(re:split("ABCDE","(.*)c(.*)",[caseless, - trim]))), + trim]))), <<":AB:DE:">> = iolist_to_binary(join(re:split("ABCDE","(.*)c(.*)",[caseless, {parts, - 2}]))), - <<":AB:DE:">> = iolist_to_binary(join(re:split("ABCDE","(.*)c(.*)",[caseless]))), + 2}]))), + <<":AB:DE:">> = iolist_to_binary(join(re:split("ABCDE","(.*)c(.*)",[caseless]))), <<":A:B">> = iolist_to_binary(join(re:split("(A, B)","\\((.*), (.*)\\)",[caseless, - trim]))), + trim]))), <<":A:B:">> = iolist_to_binary(join(re:split("(A, B)","\\((.*), (.*)\\)",[caseless, {parts, - 2}]))), - <<":A:B:">> = iolist_to_binary(join(re:split("(A, B)","\\((.*), (.*)\\)",[caseless]))), + 2}]))), + <<":A:B:">> = iolist_to_binary(join(re:split("(A, B)","\\((.*), (.*)\\)",[caseless]))), ok. run25() -> <<"">> = iolist_to_binary(join(re:split("ABCD","abcd",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ABCD","abcd",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ABCD","abcd",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ABCD","abcd",[caseless]))), <<":BC">> = iolist_to_binary(join(re:split("ABCD","a(bc)d",[caseless, - trim]))), + trim]))), <<":BC:">> = iolist_to_binary(join(re:split("ABCD","a(bc)d",[caseless, {parts, - 2}]))), - <<":BC:">> = iolist_to_binary(join(re:split("ABCD","a(bc)d",[caseless]))), + 2}]))), + <<":BC:">> = iolist_to_binary(join(re:split("ABCD","a(bc)d",[caseless]))), <<"">> = iolist_to_binary(join(re:split("AC","a[-]?c",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("AC","a[-]?c",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("AC","a[-]?c",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("AC","a[-]?c",[caseless]))), <<":ABC">> = iolist_to_binary(join(re:split("ABCABC","(abc)\\1",[caseless, - trim]))), + trim]))), <<":ABC:">> = iolist_to_binary(join(re:split("ABCABC","(abc)\\1",[caseless, {parts, - 2}]))), - <<":ABC:">> = iolist_to_binary(join(re:split("ABCABC","(abc)\\1",[caseless]))), + 2}]))), + <<":ABC:">> = iolist_to_binary(join(re:split("ABCABC","(abc)\\1",[caseless]))), <<":ABC">> = iolist_to_binary(join(re:split("ABCABC","([a-c]*)\\1",[caseless, - trim]))), + trim]))), <<":ABC:">> = iolist_to_binary(join(re:split("ABCABC","([a-c]*)\\1",[caseless, {parts, - 2}]))), - <<":ABC:">> = iolist_to_binary(join(re:split("ABCABC","([a-c]*)\\1",[caseless]))), - <<"ab">> = iolist_to_binary(join(re:split("abad","a(?!b).",[trim]))), + 2}]))), + <<":ABC:">> = iolist_to_binary(join(re:split("ABCABC","([a-c]*)\\1",[caseless]))), + <<"ab">> = iolist_to_binary(join(re:split("abad","a(?!b).",[trim]))), <<"ab:">> = iolist_to_binary(join(re:split("abad","a(?!b).",[{parts, - 2}]))), - <<"ab:">> = iolist_to_binary(join(re:split("abad","a(?!b).",[]))), - <<"ab">> = iolist_to_binary(join(re:split("abad","a(?=d).",[trim]))), + 2}]))), + <<"ab:">> = iolist_to_binary(join(re:split("abad","a(?!b).",[]))), + <<"ab">> = iolist_to_binary(join(re:split("abad","a(?=d).",[trim]))), <<"ab:">> = iolist_to_binary(join(re:split("abad","a(?=d).",[{parts, - 2}]))), - <<"ab:">> = iolist_to_binary(join(re:split("abad","a(?=d).",[]))), - <<"ab">> = iolist_to_binary(join(re:split("abad","a(?=c|d).",[trim]))), + 2}]))), + <<"ab:">> = iolist_to_binary(join(re:split("abad","a(?=d).",[]))), + <<"ab">> = iolist_to_binary(join(re:split("abad","a(?=c|d).",[trim]))), <<"ab:">> = iolist_to_binary(join(re:split("abad","a(?=c|d).",[{parts, - 2}]))), - <<"ab:">> = iolist_to_binary(join(re:split("abad","a(?=c|d).",[]))), - <<":e">> = iolist_to_binary(join(re:split("ace","a(?:b|c|d)(.)",[trim]))), + 2}]))), + <<"ab:">> = iolist_to_binary(join(re:split("abad","a(?=c|d).",[]))), + <<":e">> = iolist_to_binary(join(re:split("ace","a(?:b|c|d)(.)",[trim]))), <<":e:">> = iolist_to_binary(join(re:split("ace","a(?:b|c|d)(.)",[{parts, - 2}]))), - <<":e:">> = iolist_to_binary(join(re:split("ace","a(?:b|c|d)(.)",[]))), - <<":e">> = iolist_to_binary(join(re:split("ace","a(?:b|c|d)*(.)",[trim]))), + 2}]))), + <<":e:">> = iolist_to_binary(join(re:split("ace","a(?:b|c|d)(.)",[]))), + <<":e">> = iolist_to_binary(join(re:split("ace","a(?:b|c|d)*(.)",[trim]))), <<":e:">> = iolist_to_binary(join(re:split("ace","a(?:b|c|d)*(.)",[{parts, - 2}]))), - <<":e:">> = iolist_to_binary(join(re:split("ace","a(?:b|c|d)*(.)",[]))), - <<":e">> = iolist_to_binary(join(re:split("ace","a(?:b|c|d)+?(.)",[trim]))), + 2}]))), + <<":e:">> = iolist_to_binary(join(re:split("ace","a(?:b|c|d)*(.)",[]))), + <<":e">> = iolist_to_binary(join(re:split("ace","a(?:b|c|d)+?(.)",[trim]))), <<":e:">> = iolist_to_binary(join(re:split("ace","a(?:b|c|d)+?(.)",[{parts, - 2}]))), - <<":e:">> = iolist_to_binary(join(re:split("ace","a(?:b|c|d)+?(.)",[]))), - <<":d:bcdbe">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d)+?(.)",[trim]))), + 2}]))), + <<":e:">> = iolist_to_binary(join(re:split("ace","a(?:b|c|d)+?(.)",[]))), + <<":d:bcdbe">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d)+?(.)",[trim]))), <<":d:bcdbe">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d)+?(.)",[{parts, - 2}]))), - <<":d:bcdbe">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d)+?(.)",[]))), - <<":e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d)+(.)",[trim]))), + 2}]))), + <<":d:bcdbe">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d)+?(.)",[]))), + <<":e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d)+(.)",[trim]))), <<":e:">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d)+(.)",[{parts, - 2}]))), - <<":e:">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d)+(.)",[]))), - <<":b:cdbe">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){2}(.)",[trim]))), + 2}]))), + <<":e:">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d)+(.)",[]))), + <<":b:cdbe">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){2}(.)",[trim]))), <<":b:cdbe">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){2}(.)",[{parts, - 2}]))), - <<":b:cdbe">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){2}(.)",[]))), - <<":b:e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){4,5}(.)",[trim]))), + 2}]))), + <<":b:cdbe">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){2}(.)",[]))), + <<":b:e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){4,5}(.)",[trim]))), <<":b:e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){4,5}(.)",[{parts, - 2}]))), - <<":b:e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){4,5}(.)",[]))), - <<":d:be">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){4,5}?(.)",[trim]))), + 2}]))), + <<":b:e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){4,5}(.)",[]))), + <<":d:be">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){4,5}?(.)",[trim]))), <<":d:be">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){4,5}?(.)",[{parts, - 2}]))), - <<":d:be">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){4,5}?(.)",[]))), - <<":bar:foo:bar">> = iolist_to_binary(join(re:split("foobar","((foo)|(bar))*",[trim]))), + 2}]))), + <<":d:be">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){4,5}?(.)",[]))), + <<":bar:foo:bar">> = iolist_to_binary(join(re:split("foobar","((foo)|(bar))*",[trim]))), <<":bar:foo:bar:">> = iolist_to_binary(join(re:split("foobar","((foo)|(bar))*",[{parts, - 2}]))), - <<":bar:foo:bar:">> = iolist_to_binary(join(re:split("foobar","((foo)|(bar))*",[]))), - <<":e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){6,7}(.)",[trim]))), + 2}]))), + <<":bar:foo:bar:">> = iolist_to_binary(join(re:split("foobar","((foo)|(bar))*",[]))), + <<":e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){6,7}(.)",[trim]))), <<":e:">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){6,7}(.)",[{parts, - 2}]))), - <<":e:">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){6,7}(.)",[]))), - <<":e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){6,7}?(.)",[trim]))), + 2}]))), + <<":e:">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){6,7}(.)",[]))), + <<":e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){6,7}?(.)",[trim]))), <<":e:">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){6,7}?(.)",[{parts, - 2}]))), - <<":e:">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){6,7}?(.)",[]))), - <<":e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){5,6}(.)",[trim]))), + 2}]))), + <<":e:">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){6,7}?(.)",[]))), + <<":e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){5,6}(.)",[trim]))), <<":e:">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){5,6}(.)",[{parts, - 2}]))), - <<":e:">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){5,6}(.)",[]))), - <<":b:e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){5,6}?(.)",[trim]))), + 2}]))), + <<":e:">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){5,6}(.)",[]))), + <<":b:e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){5,6}?(.)",[trim]))), <<":b:e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){5,6}?(.)",[{parts, - 2}]))), - <<":b:e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){5,6}?(.)",[]))), + 2}]))), + <<":b:e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){5,6}?(.)",[]))), ok. run26() -> - <<":e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){5,7}(.)",[trim]))), + <<":e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){5,7}(.)",[trim]))), <<":e:">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){5,7}(.)",[{parts, - 2}]))), - <<":e:">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){5,7}(.)",[]))), - <<":b:e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){5,7}?(.)",[trim]))), + 2}]))), + <<":e:">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){5,7}(.)",[]))), + <<":b:e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){5,7}?(.)",[trim]))), <<":b:e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){5,7}?(.)",[{parts, - 2}]))), - <<":b:e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){5,7}?(.)",[]))), - <<":c:e">> = iolist_to_binary(join(re:split("ace","a(?:b|(c|e){1,2}?|d)+?(.)",[trim]))), + 2}]))), + <<":b:e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){5,7}?(.)",[]))), + <<":c:e">> = iolist_to_binary(join(re:split("ace","a(?:b|(c|e){1,2}?|d)+?(.)",[trim]))), <<":c:e:">> = iolist_to_binary(join(re:split("ace","a(?:b|(c|e){1,2}?|d)+?(.)",[{parts, - 2}]))), - <<":c:e:">> = iolist_to_binary(join(re:split("ace","a(?:b|(c|e){1,2}?|d)+?(.)",[]))), - <<":A">> = iolist_to_binary(join(re:split("AB","^(.+)?B",[trim]))), + 2}]))), + <<":c:e:">> = iolist_to_binary(join(re:split("ace","a(?:b|(c|e){1,2}?|d)+?(.)",[]))), + <<":A">> = iolist_to_binary(join(re:split("AB","^(.+)?B",[trim]))), <<":A:">> = iolist_to_binary(join(re:split("AB","^(.+)?B",[{parts, - 2}]))), - <<":A:">> = iolist_to_binary(join(re:split("AB","^(.+)?B",[]))), - <<":.">> = iolist_to_binary(join(re:split(".","^([^a-z])|(\\^)$",[trim]))), + 2}]))), + <<":A:">> = iolist_to_binary(join(re:split("AB","^(.+)?B",[]))), + <<":.">> = iolist_to_binary(join(re:split(".","^([^a-z])|(\\^)$",[trim]))), <<":.::">> = iolist_to_binary(join(re:split(".","^([^a-z])|(\\^)$",[{parts, - 2}]))), - <<":.::">> = iolist_to_binary(join(re:split(".","^([^a-z])|(\\^)$",[]))), - <<":OUT">> = iolist_to_binary(join(re:split("<&OUT","^[<>]&",[trim]))), + 2}]))), + <<":.::">> = iolist_to_binary(join(re:split(".","^([^a-z])|(\\^)$",[]))), + <<":OUT">> = iolist_to_binary(join(re:split("<&OUT","^[<>]&",[trim]))), <<":OUT">> = iolist_to_binary(join(re:split("<&OUT","^[<>]&",[{parts, - 2}]))), - <<":OUT">> = iolist_to_binary(join(re:split("<&OUT","^[<>]&",[]))), - <<":aaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaa","^(a\\1?){4}$",[trim]))), + 2}]))), + <<":OUT">> = iolist_to_binary(join(re:split("<&OUT","^[<>]&",[]))), + <<":aaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaa","^(a\\1?){4}$",[trim]))), <<":aaaa:">> = iolist_to_binary(join(re:split("aaaaaaaaaa","^(a\\1?){4}$",[{parts, - 2}]))), - <<":aaaa:">> = iolist_to_binary(join(re:split("aaaaaaaaaa","^(a\\1?){4}$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a\\1?){4}$",[trim]))), + 2}]))), + <<":aaaa:">> = iolist_to_binary(join(re:split("aaaaaaaaaa","^(a\\1?){4}$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a\\1?){4}$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a\\1?){4}$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a\\1?){4}$",[]))), - <<"AB">> = iolist_to_binary(join(re:split("AB","^(a\\1?){4}$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a\\1?){4}$",[]))), + <<"AB">> = iolist_to_binary(join(re:split("AB","^(a\\1?){4}$",[trim]))), <<"AB">> = iolist_to_binary(join(re:split("AB","^(a\\1?){4}$",[{parts, - 2}]))), - <<"AB">> = iolist_to_binary(join(re:split("AB","^(a\\1?){4}$",[]))), - <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a\\1?){4}$",[trim]))), + 2}]))), + <<"AB">> = iolist_to_binary(join(re:split("AB","^(a\\1?){4}$",[]))), + <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a\\1?){4}$",[trim]))), <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a\\1?){4}$",[{parts, - 2}]))), - <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a\\1?){4}$",[]))), - <<"aaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaa","^(a\\1?){4}$",[trim]))), + 2}]))), + <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a\\1?){4}$",[]))), + <<"aaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaa","^(a\\1?){4}$",[trim]))), <<"aaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaa","^(a\\1?){4}$",[{parts, - 2}]))), - <<"aaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaa","^(a\\1?){4}$",[]))), - <<":aaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaa","^(a(?(1)\\1)){4}$",[trim]))), + 2}]))), + <<"aaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaa","^(a\\1?){4}$",[]))), + <<":aaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaa","^(a(?(1)\\1)){4}$",[trim]))), <<":aaaa:">> = iolist_to_binary(join(re:split("aaaaaaaaaa","^(a(?(1)\\1)){4}$",[{parts, - 2}]))), - <<":aaaa:">> = iolist_to_binary(join(re:split("aaaaaaaaaa","^(a(?(1)\\1)){4}$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a(?(1)\\1)){4}$",[trim]))), + 2}]))), + <<":aaaa:">> = iolist_to_binary(join(re:split("aaaaaaaaaa","^(a(?(1)\\1)){4}$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a(?(1)\\1)){4}$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a(?(1)\\1)){4}$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a(?(1)\\1)){4}$",[]))), - <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a(?(1)\\1)){4}$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a(?(1)\\1)){4}$",[]))), + <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a(?(1)\\1)){4}$",[trim]))), <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a(?(1)\\1)){4}$",[{parts, - 2}]))), - <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a(?(1)\\1)){4}$",[]))), - <<"aaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaa","^(a(?(1)\\1)){4}$",[trim]))), + 2}]))), + <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a(?(1)\\1)){4}$",[]))), + <<"aaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaa","^(a(?(1)\\1)){4}$",[trim]))), <<"aaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaa","^(a(?(1)\\1)){4}$",[{parts, - 2}]))), - <<"aaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaa","^(a(?(1)\\1)){4}$",[]))), - <<":f:o:o:b:a:r">> = iolist_to_binary(join(re:split("foobar","(?:(f)(o)(o)|(b)(a)(r))*",[trim]))), + 2}]))), + <<"aaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaa","^(a(?(1)\\1)){4}$",[]))), + <<":f:o:o:b:a:r">> = iolist_to_binary(join(re:split("foobar","(?:(f)(o)(o)|(b)(a)(r))*",[trim]))), <<":f:o:o:b:a:r:">> = iolist_to_binary(join(re:split("foobar","(?:(f)(o)(o)|(b)(a)(r))*",[{parts, - 2}]))), - <<":f:o:o:b:a:r:">> = iolist_to_binary(join(re:split("foobar","(?:(f)(o)(o)|(b)(a)(r))*",[]))), - <<"a">> = iolist_to_binary(join(re:split("ab","(?<=a)b",[trim]))), + 2}]))), + <<":f:o:o:b:a:r:">> = iolist_to_binary(join(re:split("foobar","(?:(f)(o)(o)|(b)(a)(r))*",[]))), + <<"a">> = iolist_to_binary(join(re:split("ab","(?<=a)b",[trim]))), <<"a:">> = iolist_to_binary(join(re:split("ab","(?<=a)b",[{parts, - 2}]))), - <<"a:">> = iolist_to_binary(join(re:split("ab","(?<=a)b",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=a)b",[trim]))), + 2}]))), + <<"a:">> = iolist_to_binary(join(re:split("ab","(?<=a)b",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=a)b",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=a)b",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=a)b",[]))), - <<"cb">> = iolist_to_binary(join(re:split("cb","(?<=a)b",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=a)b",[]))), + <<"cb">> = iolist_to_binary(join(re:split("cb","(?<=a)b",[trim]))), <<"cb">> = iolist_to_binary(join(re:split("cb","(?<=a)b",[{parts, - 2}]))), - <<"cb">> = iolist_to_binary(join(re:split("cb","(?<=a)b",[]))), - <<"b">> = iolist_to_binary(join(re:split("b","(?<=a)b",[trim]))), + 2}]))), + <<"cb">> = iolist_to_binary(join(re:split("cb","(?<=a)b",[]))), + <<"b">> = iolist_to_binary(join(re:split("b","(?<=a)b",[trim]))), <<"b">> = iolist_to_binary(join(re:split("b","(?<=a)b",[{parts, - 2}]))), - <<"b">> = iolist_to_binary(join(re:split("b","(?<=a)b",[]))), - <<"a">> = iolist_to_binary(join(re:split("ab","(?<!c)b",[trim]))), + 2}]))), + <<"b">> = iolist_to_binary(join(re:split("b","(?<=a)b",[]))), + <<"a">> = iolist_to_binary(join(re:split("ab","(?<!c)b",[trim]))), <<"a:">> = iolist_to_binary(join(re:split("ab","(?<!c)b",[{parts, - 2}]))), - <<"a:">> = iolist_to_binary(join(re:split("ab","(?<!c)b",[]))), - <<"">> = iolist_to_binary(join(re:split("b","(?<!c)b",[trim]))), + 2}]))), + <<"a:">> = iolist_to_binary(join(re:split("ab","(?<!c)b",[]))), + <<"">> = iolist_to_binary(join(re:split("b","(?<!c)b",[trim]))), <<":">> = iolist_to_binary(join(re:split("b","(?<!c)b",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("b","(?<!c)b",[]))), - <<"">> = iolist_to_binary(join(re:split("b","(?<!c)b",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("b","(?<!c)b",[]))), + <<"">> = iolist_to_binary(join(re:split("b","(?<!c)b",[trim]))), <<":">> = iolist_to_binary(join(re:split("b","(?<!c)b",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("b","(?<!c)b",[]))), - <<"">> = iolist_to_binary(join(re:split("aba","(?:..)*a",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("b","(?<!c)b",[]))), + <<"">> = iolist_to_binary(join(re:split("aba","(?:..)*a",[trim]))), <<":">> = iolist_to_binary(join(re:split("aba","(?:..)*a",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aba","(?:..)*a",[]))), - <<":b">> = iolist_to_binary(join(re:split("aba","(?:..)*?a",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aba","(?:..)*a",[]))), + <<":b">> = iolist_to_binary(join(re:split("aba","(?:..)*?a",[trim]))), <<":ba">> = iolist_to_binary(join(re:split("aba","(?:..)*?a",[{parts, - 2}]))), - <<":b:">> = iolist_to_binary(join(re:split("aba","(?:..)*?a",[]))), - <<":b:c">> = iolist_to_binary(join(re:split("abc","^(?:b|a(?=(.)))*\\1",[trim]))), + 2}]))), + <<":b:">> = iolist_to_binary(join(re:split("aba","(?:..)*?a",[]))), + <<":b:c">> = iolist_to_binary(join(re:split("abc","^(?:b|a(?=(.)))*\\1",[trim]))), <<":b:c">> = iolist_to_binary(join(re:split("abc","^(?:b|a(?=(.)))*\\1",[{parts, - 2}]))), - <<":b:c">> = iolist_to_binary(join(re:split("abc","^(?:b|a(?=(.)))*\\1",[]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","^(){3,5}",[trim]))), + 2}]))), + <<":b:c">> = iolist_to_binary(join(re:split("abc","^(?:b|a(?=(.)))*\\1",[]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","^(){3,5}",[trim]))), <<"abc">> = iolist_to_binary(join(re:split("abc","^(){3,5}",[{parts, - 2}]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","^(){3,5}",[]))), - <<":a">> = iolist_to_binary(join(re:split("aax","^(a+)*ax",[trim]))), + 2}]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","^(){3,5}",[]))), + <<":a">> = iolist_to_binary(join(re:split("aax","^(a+)*ax",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aax","^(a+)*ax",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aax","^(a+)*ax",[]))), - <<":a:a">> = iolist_to_binary(join(re:split("aax","^((a|b)+)*ax",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aax","^(a+)*ax",[]))), + <<":a:a">> = iolist_to_binary(join(re:split("aax","^((a|b)+)*ax",[trim]))), <<":a:a:">> = iolist_to_binary(join(re:split("aax","^((a|b)+)*ax",[{parts, - 2}]))), - <<":a:a:">> = iolist_to_binary(join(re:split("aax","^((a|b)+)*ax",[]))), - <<":a:a">> = iolist_to_binary(join(re:split("aax","^((a|bc)+)*ax",[trim]))), + 2}]))), + <<":a:a:">> = iolist_to_binary(join(re:split("aax","^((a|b)+)*ax",[]))), + <<":a:a">> = iolist_to_binary(join(re:split("aax","^((a|bc)+)*ax",[trim]))), <<":a:a:">> = iolist_to_binary(join(re:split("aax","^((a|bc)+)*ax",[{parts, - 2}]))), - <<":a:a:">> = iolist_to_binary(join(re:split("aax","^((a|bc)+)*ax",[]))), - <<"c">> = iolist_to_binary(join(re:split("cab","(a|x)*ab",[trim]))), + 2}]))), + <<":a:a:">> = iolist_to_binary(join(re:split("aax","^((a|bc)+)*ax",[]))), + <<"c">> = iolist_to_binary(join(re:split("cab","(a|x)*ab",[trim]))), <<"c::">> = iolist_to_binary(join(re:split("cab","(a|x)*ab",[{parts, - 2}]))), - <<"c::">> = iolist_to_binary(join(re:split("cab","(a|x)*ab",[]))), - <<"c">> = iolist_to_binary(join(re:split("cab","(a)*ab",[trim]))), + 2}]))), + <<"c::">> = iolist_to_binary(join(re:split("cab","(a|x)*ab",[]))), + <<"c">> = iolist_to_binary(join(re:split("cab","(a)*ab",[trim]))), <<"c::">> = iolist_to_binary(join(re:split("cab","(a)*ab",[{parts, - 2}]))), - <<"c::">> = iolist_to_binary(join(re:split("cab","(a)*ab",[]))), + 2}]))), + <<"c::">> = iolist_to_binary(join(re:split("cab","(a)*ab",[]))), ok. run27() -> - <<"">> = iolist_to_binary(join(re:split("ab","(?:(?i)a)b",[trim]))), + <<"">> = iolist_to_binary(join(re:split("ab","(?:(?i)a)b",[trim]))), <<":">> = iolist_to_binary(join(re:split("ab","(?:(?i)a)b",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ab","(?:(?i)a)b",[]))), - <<":a">> = iolist_to_binary(join(re:split("ab","((?i)a)b",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ab","(?:(?i)a)b",[]))), + <<":a">> = iolist_to_binary(join(re:split("ab","((?i)a)b",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("ab","((?i)a)b",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("ab","((?i)a)b",[]))), - <<"">> = iolist_to_binary(join(re:split("Ab","(?:(?i)a)b",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("ab","((?i)a)b",[]))), + <<"">> = iolist_to_binary(join(re:split("Ab","(?:(?i)a)b",[trim]))), <<":">> = iolist_to_binary(join(re:split("Ab","(?:(?i)a)b",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("Ab","(?:(?i)a)b",[]))), - <<":A">> = iolist_to_binary(join(re:split("Ab","((?i)a)b",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("Ab","(?:(?i)a)b",[]))), + <<":A">> = iolist_to_binary(join(re:split("Ab","((?i)a)b",[trim]))), <<":A:">> = iolist_to_binary(join(re:split("Ab","((?i)a)b",[{parts, - 2}]))), - <<":A:">> = iolist_to_binary(join(re:split("Ab","((?i)a)b",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?:(?i)a)b",[trim]))), + 2}]))), + <<":A:">> = iolist_to_binary(join(re:split("Ab","((?i)a)b",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?:(?i)a)b",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?:(?i)a)b",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?:(?i)a)b",[]))), - <<"cb">> = iolist_to_binary(join(re:split("cb","(?:(?i)a)b",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?:(?i)a)b",[]))), + <<"cb">> = iolist_to_binary(join(re:split("cb","(?:(?i)a)b",[trim]))), <<"cb">> = iolist_to_binary(join(re:split("cb","(?:(?i)a)b",[{parts, - 2}]))), - <<"cb">> = iolist_to_binary(join(re:split("cb","(?:(?i)a)b",[]))), - <<"aB">> = iolist_to_binary(join(re:split("aB","(?:(?i)a)b",[trim]))), + 2}]))), + <<"cb">> = iolist_to_binary(join(re:split("cb","(?:(?i)a)b",[]))), + <<"aB">> = iolist_to_binary(join(re:split("aB","(?:(?i)a)b",[trim]))), <<"aB">> = iolist_to_binary(join(re:split("aB","(?:(?i)a)b",[{parts, - 2}]))), - <<"aB">> = iolist_to_binary(join(re:split("aB","(?:(?i)a)b",[]))), - <<"">> = iolist_to_binary(join(re:split("ab","(?i:a)b",[trim]))), + 2}]))), + <<"aB">> = iolist_to_binary(join(re:split("aB","(?:(?i)a)b",[]))), + <<"">> = iolist_to_binary(join(re:split("ab","(?i:a)b",[trim]))), <<":">> = iolist_to_binary(join(re:split("ab","(?i:a)b",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ab","(?i:a)b",[]))), - <<":a">> = iolist_to_binary(join(re:split("ab","((?i:a))b",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ab","(?i:a)b",[]))), + <<":a">> = iolist_to_binary(join(re:split("ab","((?i:a))b",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("ab","((?i:a))b",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("ab","((?i:a))b",[]))), - <<"">> = iolist_to_binary(join(re:split("Ab","(?i:a)b",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("ab","((?i:a))b",[]))), + <<"">> = iolist_to_binary(join(re:split("Ab","(?i:a)b",[trim]))), <<":">> = iolist_to_binary(join(re:split("Ab","(?i:a)b",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("Ab","(?i:a)b",[]))), - <<":A">> = iolist_to_binary(join(re:split("Ab","((?i:a))b",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("Ab","(?i:a)b",[]))), + <<":A">> = iolist_to_binary(join(re:split("Ab","((?i:a))b",[trim]))), <<":A:">> = iolist_to_binary(join(re:split("Ab","((?i:a))b",[{parts, - 2}]))), - <<":A:">> = iolist_to_binary(join(re:split("Ab","((?i:a))b",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?i:a)b",[trim]))), + 2}]))), + <<":A:">> = iolist_to_binary(join(re:split("Ab","((?i:a))b",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?i:a)b",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?i:a)b",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?i:a)b",[]))), - <<"aB">> = iolist_to_binary(join(re:split("aB","(?i:a)b",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?i:a)b",[]))), + <<"aB">> = iolist_to_binary(join(re:split("aB","(?i:a)b",[trim]))), <<"aB">> = iolist_to_binary(join(re:split("aB","(?i:a)b",[{parts, - 2}]))), - <<"aB">> = iolist_to_binary(join(re:split("aB","(?i:a)b",[]))), - <<"aB">> = iolist_to_binary(join(re:split("aB","(?i:a)b",[trim]))), + 2}]))), + <<"aB">> = iolist_to_binary(join(re:split("aB","(?i:a)b",[]))), + <<"aB">> = iolist_to_binary(join(re:split("aB","(?i:a)b",[trim]))), <<"aB">> = iolist_to_binary(join(re:split("aB","(?i:a)b",[{parts, - 2}]))), - <<"aB">> = iolist_to_binary(join(re:split("aB","(?i:a)b",[]))), + 2}]))), + <<"aB">> = iolist_to_binary(join(re:split("aB","(?i:a)b",[]))), <<"">> = iolist_to_binary(join(re:split("ab","(?:(?-i)a)b",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ab","(?:(?-i)a)b",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ab","(?:(?-i)a)b",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ab","(?:(?-i)a)b",[caseless]))), <<":a">> = iolist_to_binary(join(re:split("ab","((?-i)a)b",[caseless, - trim]))), + trim]))), <<":a:">> = iolist_to_binary(join(re:split("ab","((?-i)a)b",[caseless, {parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("ab","((?-i)a)b",[caseless]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("ab","((?-i)a)b",[caseless]))), <<"">> = iolist_to_binary(join(re:split("aB","(?:(?-i)a)b",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("aB","(?:(?-i)a)b",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aB","(?:(?-i)a)b",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aB","(?:(?-i)a)b",[caseless]))), <<":a">> = iolist_to_binary(join(re:split("aB","((?-i)a)b",[caseless, - trim]))), + trim]))), <<":a:">> = iolist_to_binary(join(re:split("aB","((?-i)a)b",[caseless, {parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aB","((?-i)a)b",[caseless]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aB","((?-i)a)b",[caseless]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?:(?-i)a)b",[caseless, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?:(?-i)a)b",[caseless, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?:(?-i)a)b",[caseless]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?:(?-i)a)b",[caseless]))), <<"">> = iolist_to_binary(join(re:split("aB","(?:(?-i)a)b",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("aB","(?:(?-i)a)b",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aB","(?:(?-i)a)b",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aB","(?:(?-i)a)b",[caseless]))), <<"Ab">> = iolist_to_binary(join(re:split("Ab","(?:(?-i)a)b",[caseless, - trim]))), + trim]))), <<"Ab">> = iolist_to_binary(join(re:split("Ab","(?:(?-i)a)b",[caseless, {parts, - 2}]))), - <<"Ab">> = iolist_to_binary(join(re:split("Ab","(?:(?-i)a)b",[caseless]))), + 2}]))), + <<"Ab">> = iolist_to_binary(join(re:split("Ab","(?:(?-i)a)b",[caseless]))), <<"">> = iolist_to_binary(join(re:split("aB","(?:(?-i)a)b",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("aB","(?:(?-i)a)b",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aB","(?:(?-i)a)b",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aB","(?:(?-i)a)b",[caseless]))), <<":a">> = iolist_to_binary(join(re:split("aB","((?-i)a)b",[caseless, - trim]))), + trim]))), <<":a:">> = iolist_to_binary(join(re:split("aB","((?-i)a)b",[caseless, {parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aB","((?-i)a)b",[caseless]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aB","((?-i)a)b",[caseless]))), ok. run28() -> <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?:(?-i)a)b",[caseless, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?:(?-i)a)b",[caseless, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?:(?-i)a)b",[caseless]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?:(?-i)a)b",[caseless]))), <<"Ab">> = iolist_to_binary(join(re:split("Ab","(?:(?-i)a)b",[caseless, - trim]))), + trim]))), <<"Ab">> = iolist_to_binary(join(re:split("Ab","(?:(?-i)a)b",[caseless, {parts, - 2}]))), - <<"Ab">> = iolist_to_binary(join(re:split("Ab","(?:(?-i)a)b",[caseless]))), + 2}]))), + <<"Ab">> = iolist_to_binary(join(re:split("Ab","(?:(?-i)a)b",[caseless]))), <<"AB">> = iolist_to_binary(join(re:split("AB","(?:(?-i)a)b",[caseless, - trim]))), + trim]))), <<"AB">> = iolist_to_binary(join(re:split("AB","(?:(?-i)a)b",[caseless, {parts, - 2}]))), - <<"AB">> = iolist_to_binary(join(re:split("AB","(?:(?-i)a)b",[caseless]))), + 2}]))), + <<"AB">> = iolist_to_binary(join(re:split("AB","(?:(?-i)a)b",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ab","(?-i:a)b",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ab","(?-i:a)b",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ab","(?-i:a)b",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ab","(?-i:a)b",[caseless]))), <<":a">> = iolist_to_binary(join(re:split("ab","((?-i:a))b",[caseless, - trim]))), + trim]))), <<":a:">> = iolist_to_binary(join(re:split("ab","((?-i:a))b",[caseless, {parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("ab","((?-i:a))b",[caseless]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("ab","((?-i:a))b",[caseless]))), <<"">> = iolist_to_binary(join(re:split("aB","(?-i:a)b",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("aB","(?-i:a)b",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aB","(?-i:a)b",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aB","(?-i:a)b",[caseless]))), <<":a">> = iolist_to_binary(join(re:split("aB","((?-i:a))b",[caseless, - trim]))), + trim]))), <<":a:">> = iolist_to_binary(join(re:split("aB","((?-i:a))b",[caseless, {parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aB","((?-i:a))b",[caseless]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aB","((?-i:a))b",[caseless]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?-i:a)b",[caseless, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?-i:a)b",[caseless, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?-i:a)b",[caseless]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?-i:a)b",[caseless]))), <<"AB">> = iolist_to_binary(join(re:split("AB","(?-i:a)b",[caseless, - trim]))), + trim]))), <<"AB">> = iolist_to_binary(join(re:split("AB","(?-i:a)b",[caseless, {parts, - 2}]))), - <<"AB">> = iolist_to_binary(join(re:split("AB","(?-i:a)b",[caseless]))), + 2}]))), + <<"AB">> = iolist_to_binary(join(re:split("AB","(?-i:a)b",[caseless]))), <<"Ab">> = iolist_to_binary(join(re:split("Ab","(?-i:a)b",[caseless, - trim]))), + trim]))), <<"Ab">> = iolist_to_binary(join(re:split("Ab","(?-i:a)b",[caseless, {parts, - 2}]))), - <<"Ab">> = iolist_to_binary(join(re:split("Ab","(?-i:a)b",[caseless]))), + 2}]))), + <<"Ab">> = iolist_to_binary(join(re:split("Ab","(?-i:a)b",[caseless]))), <<"">> = iolist_to_binary(join(re:split("aB","(?-i:a)b",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("aB","(?-i:a)b",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aB","(?-i:a)b",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aB","(?-i:a)b",[caseless]))), <<":a">> = iolist_to_binary(join(re:split("aB","((?-i:a))b",[caseless, - trim]))), + trim]))), <<":a:">> = iolist_to_binary(join(re:split("aB","((?-i:a))b",[caseless, {parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aB","((?-i:a))b",[caseless]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aB","((?-i:a))b",[caseless]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?-i:a)b",[caseless, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?-i:a)b",[caseless, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?-i:a)b",[caseless]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?-i:a)b",[caseless]))), <<"Ab">> = iolist_to_binary(join(re:split("Ab","(?-i:a)b",[caseless, - trim]))), + trim]))), <<"Ab">> = iolist_to_binary(join(re:split("Ab","(?-i:a)b",[caseless, {parts, - 2}]))), - <<"Ab">> = iolist_to_binary(join(re:split("Ab","(?-i:a)b",[caseless]))), + 2}]))), + <<"Ab">> = iolist_to_binary(join(re:split("Ab","(?-i:a)b",[caseless]))), <<"AB">> = iolist_to_binary(join(re:split("AB","(?-i:a)b",[caseless, - trim]))), + trim]))), <<"AB">> = iolist_to_binary(join(re:split("AB","(?-i:a)b",[caseless, {parts, - 2}]))), - <<"AB">> = iolist_to_binary(join(re:split("AB","(?-i:a)b",[caseless]))), + 2}]))), + <<"AB">> = iolist_to_binary(join(re:split("AB","(?-i:a)b",[caseless]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","((?-i:a.))b",[caseless, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","((?-i:a.))b",[caseless, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","((?-i:a.))b",[caseless]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","((?-i:a.))b",[caseless]))), <<"AB">> = iolist_to_binary(join(re:split("AB","((?-i:a.))b",[caseless, - trim]))), + trim]))), <<"AB">> = iolist_to_binary(join(re:split("AB","((?-i:a.))b",[caseless, {parts, - 2}]))), - <<"AB">> = iolist_to_binary(join(re:split("AB","((?-i:a.))b",[caseless]))), + 2}]))), + <<"AB">> = iolist_to_binary(join(re:split("AB","((?-i:a.))b",[caseless]))), <<"a B">> = iolist_to_binary(join(re:split("a -B","((?-i:a.))b",[caseless,trim]))), +B","((?-i:a.))b",[caseless,trim]))), <<"a B">> = iolist_to_binary(join(re:split("a -B","((?-i:a.))b",[caseless,{parts,2}]))), +B","((?-i:a.))b",[caseless,{parts,2}]))), <<"a B">> = iolist_to_binary(join(re:split("a -B","((?-i:a.))b",[caseless]))), +B","((?-i:a.))b",[caseless]))), <<":a ">> = iolist_to_binary(join(re:split("a -B","((?s-i:a.))b",[caseless,trim]))), +B","((?s-i:a.))b",[caseless,trim]))), <<":a :">> = iolist_to_binary(join(re:split("a -B","((?s-i:a.))b",[caseless,{parts,2}]))), +B","((?s-i:a.))b",[caseless,{parts,2}]))), <<":a :">> = iolist_to_binary(join(re:split("a -B","((?s-i:a.))b",[caseless]))), - <<"">> = iolist_to_binary(join(re:split("cabbbb","(?:c|d)(?:)(?:a(?:)(?:b)(?:b(?:))(?:b(?:)(?:b)))",[trim]))), +B","((?s-i:a.))b",[caseless]))), + <<"">> = iolist_to_binary(join(re:split("cabbbb","(?:c|d)(?:)(?:a(?:)(?:b)(?:b(?:))(?:b(?:)(?:b)))",[trim]))), <<":">> = iolist_to_binary(join(re:split("cabbbb","(?:c|d)(?:)(?:a(?:)(?:b)(?:b(?:))(?:b(?:)(?:b)))",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("cabbbb","(?:c|d)(?:)(?:a(?:)(?:b)(?:b(?:))(?:b(?:)(?:b)))",[]))), - <<"">> = iolist_to_binary(join(re:split("caaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","(?:c|d)(?:)(?:aaaaaaaa(?:)(?:bbbbbbbb)(?:bbbbbbbb(?:))(?:bbbbbbbb(?:)(?:bbbbbbbb)))",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("cabbbb","(?:c|d)(?:)(?:a(?:)(?:b)(?:b(?:))(?:b(?:)(?:b)))",[]))), + <<"">> = iolist_to_binary(join(re:split("caaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","(?:c|d)(?:)(?:aaaaaaaa(?:)(?:bbbbbbbb)(?:bbbbbbbb(?:))(?:bbbbbbbb(?:)(?:bbbbbbbb)))",[trim]))), <<":">> = iolist_to_binary(join(re:split("caaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","(?:c|d)(?:)(?:aaaaaaaa(?:)(?:bbbbbbbb)(?:bbbbbbbb(?:))(?:bbbbbbbb(?:)(?:bbbbbbbb)))",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("caaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","(?:c|d)(?:)(?:aaaaaaaa(?:)(?:bbbbbbbb)(?:bbbbbbbb(?:))(?:bbbbbbbb(?:)(?:bbbbbbbb)))",[]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("caaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","(?:c|d)(?:)(?:aaaaaaaa(?:)(?:bbbbbbbb)(?:bbbbbbbb(?:))(?:bbbbbbbb(?:)(?:bbbbbbbb)))",[]))), <<":Ab">> = iolist_to_binary(join(re:split("Ab4ab","(ab)\\d\\1",[caseless, - trim]))), + trim]))), <<":Ab:">> = iolist_to_binary(join(re:split("Ab4ab","(ab)\\d\\1",[caseless, {parts, - 2}]))), - <<":Ab:">> = iolist_to_binary(join(re:split("Ab4ab","(ab)\\d\\1",[caseless]))), + 2}]))), + <<":Ab:">> = iolist_to_binary(join(re:split("Ab4ab","(ab)\\d\\1",[caseless]))), <<":ab">> = iolist_to_binary(join(re:split("ab4Ab","(ab)\\d\\1",[caseless, - trim]))), + trim]))), <<":ab:">> = iolist_to_binary(join(re:split("ab4Ab","(ab)\\d\\1",[caseless, {parts, - 2}]))), - <<":ab:">> = iolist_to_binary(join(re:split("ab4Ab","(ab)\\d\\1",[caseless]))), - <<"">> = iolist_to_binary(join(re:split("foobar1234baz","foo\\w*\\d{4}baz",[trim]))), + 2}]))), + <<":ab:">> = iolist_to_binary(join(re:split("ab4Ab","(ab)\\d\\1",[caseless]))), + <<"">> = iolist_to_binary(join(re:split("foobar1234baz","foo\\w*\\d{4}baz",[trim]))), <<":">> = iolist_to_binary(join(re:split("foobar1234baz","foo\\w*\\d{4}baz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("foobar1234baz","foo\\w*\\d{4}baz",[]))), - <<":~~">> = iolist_to_binary(join(re:split("x~~","x(~~)*(?:(?:F)?)?",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("foobar1234baz","foo\\w*\\d{4}baz",[]))), + <<":~~">> = iolist_to_binary(join(re:split("x~~","x(~~)*(?:(?:F)?)?",[trim]))), <<":~~:">> = iolist_to_binary(join(re:split("x~~","x(~~)*(?:(?:F)?)?",[{parts, - 2}]))), - <<":~~:">> = iolist_to_binary(join(re:split("x~~","x(~~)*(?:(?:F)?)?",[]))), - <<"">> = iolist_to_binary(join(re:split("aaac","^a(?#xxx){3}c",[trim]))), + 2}]))), + <<":~~:">> = iolist_to_binary(join(re:split("x~~","x(~~)*(?:(?:F)?)?",[]))), + <<"">> = iolist_to_binary(join(re:split("aaac","^a(?#xxx){3}c",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaac","^a(?#xxx){3}c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaac","^a(?#xxx){3}c",[]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaac","^a(?#xxx){3}c",[]))), ok. run29() -> <<"">> = iolist_to_binary(join(re:split("aaac","^a (?#xxx) (?#yyy) {3}c",[extended, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("aaac","^a (?#xxx) (?#yyy) {3}c",[extended, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaac","^a (?#xxx) (?#yyy) {3}c",[extended]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<![cd])b",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaac","^a (?#xxx) (?#yyy) {3}c",[extended]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<![cd])b",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<![cd])b",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<![cd])b",[]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<![cd])b",[]))), <<"B B">> = iolist_to_binary(join(re:split("B -B","(?<![cd])b",[trim]))), +B","(?<![cd])b",[trim]))), <<"B B">> = iolist_to_binary(join(re:split("B -B","(?<![cd])b",[{parts,2}]))), +B","(?<![cd])b",[{parts,2}]))), <<"B B">> = iolist_to_binary(join(re:split("B -B","(?<![cd])b",[]))), - <<"dbcb">> = iolist_to_binary(join(re:split("dbcb","(?<![cd])b",[trim]))), +B","(?<![cd])b",[]))), + <<"dbcb">> = iolist_to_binary(join(re:split("dbcb","(?<![cd])b",[trim]))), <<"dbcb">> = iolist_to_binary(join(re:split("dbcb","(?<![cd])b",[{parts, - 2}]))), - <<"dbcb">> = iolist_to_binary(join(re:split("dbcb","(?<![cd])b",[]))), - <<"db::cb">> = iolist_to_binary(join(re:split("dbaacb","(?<![cd])[ab]",[trim]))), + 2}]))), + <<"dbcb">> = iolist_to_binary(join(re:split("dbcb","(?<![cd])b",[]))), + <<"db::cb">> = iolist_to_binary(join(re:split("dbaacb","(?<![cd])[ab]",[trim]))), <<"db:acb">> = iolist_to_binary(join(re:split("dbaacb","(?<![cd])[ab]",[{parts, - 2}]))), - <<"db::cb">> = iolist_to_binary(join(re:split("dbaacb","(?<![cd])[ab]",[]))), - <<"db::::cb">> = iolist_to_binary(join(re:split("dbaacb","(?<!(c|d))[ab]",[trim]))), + 2}]))), + <<"db::cb">> = iolist_to_binary(join(re:split("dbaacb","(?<![cd])[ab]",[]))), + <<"db::::cb">> = iolist_to_binary(join(re:split("dbaacb","(?<!(c|d))[ab]",[trim]))), <<"db::acb">> = iolist_to_binary(join(re:split("dbaacb","(?<!(c|d))[ab]",[{parts, - 2}]))), - <<"db::::cb">> = iolist_to_binary(join(re:split("dbaacb","(?<!(c|d))[ab]",[]))), - <<"cdacc">> = iolist_to_binary(join(re:split("cdaccb","(?<!cd)[ab]",[trim]))), + 2}]))), + <<"db::::cb">> = iolist_to_binary(join(re:split("dbaacb","(?<!(c|d))[ab]",[]))), + <<"cdacc">> = iolist_to_binary(join(re:split("cdaccb","(?<!cd)[ab]",[trim]))), <<"cdacc:">> = iolist_to_binary(join(re:split("cdaccb","(?<!cd)[ab]",[{parts, - 2}]))), - <<"cdacc:">> = iolist_to_binary(join(re:split("cdaccb","(?<!cd)[ab]",[]))), - <<"">> = iolist_to_binary(join(re:split("","^(?:a?b?)*$",[trim]))), + 2}]))), + <<"cdacc:">> = iolist_to_binary(join(re:split("cdaccb","(?<!cd)[ab]",[]))), + <<"">> = iolist_to_binary(join(re:split("","^(?:a?b?)*$",[trim]))), <<"">> = iolist_to_binary(join(re:split("","^(?:a?b?)*$",[{parts, - 2}]))), - <<"">> = iolist_to_binary(join(re:split("","^(?:a?b?)*$",[]))), - <<"">> = iolist_to_binary(join(re:split("a","^(?:a?b?)*$",[trim]))), + 2}]))), + <<"">> = iolist_to_binary(join(re:split("","^(?:a?b?)*$",[]))), + <<"">> = iolist_to_binary(join(re:split("a","^(?:a?b?)*$",[trim]))), <<":">> = iolist_to_binary(join(re:split("a","^(?:a?b?)*$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a","^(?:a?b?)*$",[]))), - <<"">> = iolist_to_binary(join(re:split("ab","^(?:a?b?)*$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a","^(?:a?b?)*$",[]))), + <<"">> = iolist_to_binary(join(re:split("ab","^(?:a?b?)*$",[trim]))), <<":">> = iolist_to_binary(join(re:split("ab","^(?:a?b?)*$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ab","^(?:a?b?)*$",[]))), - <<"">> = iolist_to_binary(join(re:split("aaa","^(?:a?b?)*$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ab","^(?:a?b?)*$",[]))), + <<"">> = iolist_to_binary(join(re:split("aaa","^(?:a?b?)*$",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaa","^(?:a?b?)*$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaa","^(?:a?b?)*$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?:a?b?)*$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaa","^(?:a?b?)*$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?:a?b?)*$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?:a?b?)*$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?:a?b?)*$",[]))), - <<"dbcb">> = iolist_to_binary(join(re:split("dbcb","^(?:a?b?)*$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?:a?b?)*$",[]))), + <<"dbcb">> = iolist_to_binary(join(re:split("dbcb","^(?:a?b?)*$",[trim]))), <<"dbcb">> = iolist_to_binary(join(re:split("dbcb","^(?:a?b?)*$",[{parts, - 2}]))), - <<"dbcb">> = iolist_to_binary(join(re:split("dbcb","^(?:a?b?)*$",[]))), - <<"a--">> = iolist_to_binary(join(re:split("a--","^(?:a?b?)*$",[trim]))), + 2}]))), + <<"dbcb">> = iolist_to_binary(join(re:split("dbcb","^(?:a?b?)*$",[]))), + <<"a--">> = iolist_to_binary(join(re:split("a--","^(?:a?b?)*$",[trim]))), <<"a--">> = iolist_to_binary(join(re:split("a--","^(?:a?b?)*$",[{parts, - 2}]))), - <<"a--">> = iolist_to_binary(join(re:split("a--","^(?:a?b?)*$",[]))), - <<"aa--">> = iolist_to_binary(join(re:split("aa--","^(?:a?b?)*$",[trim]))), + 2}]))), + <<"a--">> = iolist_to_binary(join(re:split("a--","^(?:a?b?)*$",[]))), + <<"aa--">> = iolist_to_binary(join(re:split("aa--","^(?:a?b?)*$",[trim]))), <<"aa--">> = iolist_to_binary(join(re:split("aa--","^(?:a?b?)*$",[{parts, - 2}]))), - <<"aa--">> = iolist_to_binary(join(re:split("aa--","^(?:a?b?)*$",[]))), + 2}]))), + <<"aa--">> = iolist_to_binary(join(re:split("aa--","^(?:a?b?)*$",[]))), <<":a : :b: c">> = iolist_to_binary(join(re:split("a b -c","((?s)^a(.))((?m)^b$)",[trim]))), +c","((?s)^a(.))((?m)^b$)",[trim]))), <<":a : :b: c">> = iolist_to_binary(join(re:split("a b -c","((?s)^a(.))((?m)^b$)",[{parts,2}]))), +c","((?s)^a(.))((?m)^b$)",[{parts,2}]))), <<":a : :b: c">> = iolist_to_binary(join(re:split("a b -c","((?s)^a(.))((?m)^b$)",[]))), +c","((?s)^a(.))((?m)^b$)",[]))), <<"a :b: c">> = iolist_to_binary(join(re:split("a b -c","((?m)^b$)",[trim]))), +c","((?m)^b$)",[trim]))), <<"a :b: c">> = iolist_to_binary(join(re:split("a b -c","((?m)^b$)",[{parts,2}]))), +c","((?m)^b$)",[{parts,2}]))), <<"a :b: c">> = iolist_to_binary(join(re:split("a b -c","((?m)^b$)",[]))), +c","((?m)^b$)",[]))), <<"a ">> = iolist_to_binary(join(re:split("a -b","(?m)^b",[trim]))), +b","(?m)^b",[trim]))), <<"a :">> = iolist_to_binary(join(re:split("a -b","(?m)^b",[{parts,2}]))), +b","(?m)^b",[{parts,2}]))), <<"a :">> = iolist_to_binary(join(re:split("a -b","(?m)^b",[]))), +b","(?m)^b",[]))), <<"a :b">> = iolist_to_binary(join(re:split("a -b","(?m)^(b)",[trim]))), +b","(?m)^(b)",[trim]))), <<"a :b:">> = iolist_to_binary(join(re:split("a -b","(?m)^(b)",[{parts,2}]))), +b","(?m)^(b)",[{parts,2}]))), <<"a :b:">> = iolist_to_binary(join(re:split("a -b","(?m)^(b)",[]))), +b","(?m)^(b)",[]))), <<"a :b">> = iolist_to_binary(join(re:split("a -b","((?m)^b)",[trim]))), +b","((?m)^b)",[trim]))), <<"a :b:">> = iolist_to_binary(join(re:split("a -b","((?m)^b)",[{parts,2}]))), +b","((?m)^b)",[{parts,2}]))), <<"a :b:">> = iolist_to_binary(join(re:split("a -b","((?m)^b)",[]))), +b","((?m)^b)",[]))), <<"a:b">> = iolist_to_binary(join(re:split("a -b","\\n((?m)^b)",[trim]))), +b","\\n((?m)^b)",[trim]))), <<"a:b:">> = iolist_to_binary(join(re:split("a -b","\\n((?m)^b)",[{parts,2}]))), +b","\\n((?m)^b)",[{parts,2}]))), <<"a:b:">> = iolist_to_binary(join(re:split("a -b","\\n((?m)^b)",[]))), +b","\\n((?m)^b)",[]))), <<"a b: ">> = iolist_to_binary(join(re:split("a b -c","((?s).)c(?!.)",[trim]))), +c","((?s).)c(?!.)",[trim]))), <<"a b: :">> = iolist_to_binary(join(re:split("a b -c","((?s).)c(?!.)",[{parts,2}]))), +c","((?s).)c(?!.)",[{parts,2}]))), <<"a b: :">> = iolist_to_binary(join(re:split("a b -c","((?s).)c(?!.)",[]))), +c","((?s).)c(?!.)",[]))), <<"a b: ">> = iolist_to_binary(join(re:split("a b -c","((?s).)c(?!.)",[trim]))), +c","((?s).)c(?!.)",[trim]))), <<"a b: :">> = iolist_to_binary(join(re:split("a b -c","((?s).)c(?!.)",[{parts,2}]))), +c","((?s).)c(?!.)",[{parts,2}]))), <<"a b: :">> = iolist_to_binary(join(re:split("a b -c","((?s).)c(?!.)",[]))), +c","((?s).)c(?!.)",[]))), <<"a :b ">> = iolist_to_binary(join(re:split("a b -c","((?s)b.)c(?!.)",[trim]))), +c","((?s)b.)c(?!.)",[trim]))), <<"a :b :">> = iolist_to_binary(join(re:split("a b -c","((?s)b.)c(?!.)",[{parts,2}]))), +c","((?s)b.)c(?!.)",[{parts,2}]))), <<"a :b :">> = iolist_to_binary(join(re:split("a b -c","((?s)b.)c(?!.)",[]))), +c","((?s)b.)c(?!.)",[]))), <<"a :b ">> = iolist_to_binary(join(re:split("a b -c","((?s)b.)c(?!.)",[trim]))), +c","((?s)b.)c(?!.)",[trim]))), <<"a :b :">> = iolist_to_binary(join(re:split("a b -c","((?s)b.)c(?!.)",[{parts,2}]))), +c","((?s)b.)c(?!.)",[{parts,2}]))), <<"a :b :">> = iolist_to_binary(join(re:split("a b -c","((?s)b.)c(?!.)",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","()^b",[trim]))), +c","((?s)b.)c(?!.)",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","()^b",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","()^b",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","()^b",[]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","()^b",[]))), <<"a b c">> = iolist_to_binary(join(re:split("a b -c","()^b",[trim]))), +c","()^b",[trim]))), <<"a b c">> = iolist_to_binary(join(re:split("a b -c","()^b",[{parts,2}]))), +c","()^b",[{parts,2}]))), <<"a b c">> = iolist_to_binary(join(re:split("a b -c","()^b",[]))), +c","()^b",[]))), <<"a b c">> = iolist_to_binary(join(re:split("a b -c","()^b",[trim]))), +c","()^b",[trim]))), <<"a b c">> = iolist_to_binary(join(re:split("a b -c","()^b",[{parts,2}]))), +c","()^b",[{parts,2}]))), <<"a b c">> = iolist_to_binary(join(re:split("a b -c","()^b",[]))), +c","()^b",[]))), <<"a :b: c">> = iolist_to_binary(join(re:split("a b -c","((?m)^b)",[trim]))), +c","((?m)^b)",[trim]))), <<"a :b: c">> = iolist_to_binary(join(re:split("a b -c","((?m)^b)",[{parts,2}]))), +c","((?m)^b)",[{parts,2}]))), <<"a :b: c">> = iolist_to_binary(join(re:split("a b -c","((?m)^b)",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(x)?(?(1)a|b)",[trim]))), +c","((?m)^b)",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(x)?(?(1)a|b)",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(x)?(?(1)a|b)",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(x)?(?(1)a|b)",[]))), - <<"a">> = iolist_to_binary(join(re:split("a","(x)?(?(1)a|b)",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(x)?(?(1)a|b)",[]))), + <<"a">> = iolist_to_binary(join(re:split("a","(x)?(?(1)a|b)",[trim]))), <<"a">> = iolist_to_binary(join(re:split("a","(x)?(?(1)a|b)",[{parts, - 2}]))), - <<"a">> = iolist_to_binary(join(re:split("a","(x)?(?(1)a|b)",[]))), - <<"a">> = iolist_to_binary(join(re:split("a","(x)?(?(1)a|b)",[trim]))), + 2}]))), + <<"a">> = iolist_to_binary(join(re:split("a","(x)?(?(1)a|b)",[]))), + <<"a">> = iolist_to_binary(join(re:split("a","(x)?(?(1)a|b)",[trim]))), <<"a">> = iolist_to_binary(join(re:split("a","(x)?(?(1)a|b)",[{parts, - 2}]))), - <<"a">> = iolist_to_binary(join(re:split("a","(x)?(?(1)a|b)",[]))), - <<"">> = iolist_to_binary(join(re:split("a","(x)?(?(1)b|a)",[trim]))), + 2}]))), + <<"a">> = iolist_to_binary(join(re:split("a","(x)?(?(1)a|b)",[]))), + <<"">> = iolist_to_binary(join(re:split("a","(x)?(?(1)b|a)",[trim]))), <<"::">> = iolist_to_binary(join(re:split("a","(x)?(?(1)b|a)",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("a","(x)?(?(1)b|a)",[]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("a","(x)?(?(1)b|a)",[]))), ok. run30() -> - <<"">> = iolist_to_binary(join(re:split("a","()?(?(1)b|a)",[trim]))), + <<"">> = iolist_to_binary(join(re:split("a","()?(?(1)b|a)",[trim]))), <<"::">> = iolist_to_binary(join(re:split("a","()?(?(1)b|a)",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("a","()?(?(1)b|a)",[]))), - <<"">> = iolist_to_binary(join(re:split("a","()?(?(1)a|b)",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("a","()?(?(1)b|a)",[]))), + <<"">> = iolist_to_binary(join(re:split("a","()?(?(1)a|b)",[trim]))), <<"::">> = iolist_to_binary(join(re:split("a","()?(?(1)a|b)",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("a","()?(?(1)a|b)",[]))), - <<":(:)">> = iolist_to_binary(join(re:split("(blah)","^(\\()?blah(?(1)(\\)))$",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("a","()?(?(1)a|b)",[]))), + <<":(:)">> = iolist_to_binary(join(re:split("(blah)","^(\\()?blah(?(1)(\\)))$",[trim]))), <<":(:):">> = iolist_to_binary(join(re:split("(blah)","^(\\()?blah(?(1)(\\)))$",[{parts, - 2}]))), - <<":(:):">> = iolist_to_binary(join(re:split("(blah)","^(\\()?blah(?(1)(\\)))$",[]))), - <<"">> = iolist_to_binary(join(re:split("blah","^(\\()?blah(?(1)(\\)))$",[trim]))), + 2}]))), + <<":(:):">> = iolist_to_binary(join(re:split("(blah)","^(\\()?blah(?(1)(\\)))$",[]))), + <<"">> = iolist_to_binary(join(re:split("blah","^(\\()?blah(?(1)(\\)))$",[trim]))), <<":::">> = iolist_to_binary(join(re:split("blah","^(\\()?blah(?(1)(\\)))$",[{parts, - 2}]))), - <<":::">> = iolist_to_binary(join(re:split("blah","^(\\()?blah(?(1)(\\)))$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\()?blah(?(1)(\\)))$",[trim]))), + 2}]))), + <<":::">> = iolist_to_binary(join(re:split("blah","^(\\()?blah(?(1)(\\)))$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\()?blah(?(1)(\\)))$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\()?blah(?(1)(\\)))$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\()?blah(?(1)(\\)))$",[]))), - <<"a">> = iolist_to_binary(join(re:split("a","^(\\()?blah(?(1)(\\)))$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\()?blah(?(1)(\\)))$",[]))), + <<"a">> = iolist_to_binary(join(re:split("a","^(\\()?blah(?(1)(\\)))$",[trim]))), <<"a">> = iolist_to_binary(join(re:split("a","^(\\()?blah(?(1)(\\)))$",[{parts, - 2}]))), - <<"a">> = iolist_to_binary(join(re:split("a","^(\\()?blah(?(1)(\\)))$",[]))), - <<"blah)">> = iolist_to_binary(join(re:split("blah)","^(\\()?blah(?(1)(\\)))$",[trim]))), + 2}]))), + <<"a">> = iolist_to_binary(join(re:split("a","^(\\()?blah(?(1)(\\)))$",[]))), + <<"blah)">> = iolist_to_binary(join(re:split("blah)","^(\\()?blah(?(1)(\\)))$",[trim]))), <<"blah)">> = iolist_to_binary(join(re:split("blah)","^(\\()?blah(?(1)(\\)))$",[{parts, - 2}]))), - <<"blah)">> = iolist_to_binary(join(re:split("blah)","^(\\()?blah(?(1)(\\)))$",[]))), - <<"(blah">> = iolist_to_binary(join(re:split("(blah","^(\\()?blah(?(1)(\\)))$",[trim]))), + 2}]))), + <<"blah)">> = iolist_to_binary(join(re:split("blah)","^(\\()?blah(?(1)(\\)))$",[]))), + <<"(blah">> = iolist_to_binary(join(re:split("(blah","^(\\()?blah(?(1)(\\)))$",[trim]))), <<"(blah">> = iolist_to_binary(join(re:split("(blah","^(\\()?blah(?(1)(\\)))$",[{parts, - 2}]))), - <<"(blah">> = iolist_to_binary(join(re:split("(blah","^(\\()?blah(?(1)(\\)))$",[]))), - <<":(:)">> = iolist_to_binary(join(re:split("(blah)","^(\\(+)?blah(?(1)(\\)))$",[trim]))), + 2}]))), + <<"(blah">> = iolist_to_binary(join(re:split("(blah","^(\\()?blah(?(1)(\\)))$",[]))), + <<":(:)">> = iolist_to_binary(join(re:split("(blah)","^(\\(+)?blah(?(1)(\\)))$",[trim]))), <<":(:):">> = iolist_to_binary(join(re:split("(blah)","^(\\(+)?blah(?(1)(\\)))$",[{parts, - 2}]))), - <<":(:):">> = iolist_to_binary(join(re:split("(blah)","^(\\(+)?blah(?(1)(\\)))$",[]))), - <<"">> = iolist_to_binary(join(re:split("blah","^(\\(+)?blah(?(1)(\\)))$",[trim]))), + 2}]))), + <<":(:):">> = iolist_to_binary(join(re:split("(blah)","^(\\(+)?blah(?(1)(\\)))$",[]))), + <<"">> = iolist_to_binary(join(re:split("blah","^(\\(+)?blah(?(1)(\\)))$",[trim]))), <<":::">> = iolist_to_binary(join(re:split("blah","^(\\(+)?blah(?(1)(\\)))$",[{parts, - 2}]))), - <<":::">> = iolist_to_binary(join(re:split("blah","^(\\(+)?blah(?(1)(\\)))$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\(+)?blah(?(1)(\\)))$",[trim]))), + 2}]))), + <<":::">> = iolist_to_binary(join(re:split("blah","^(\\(+)?blah(?(1)(\\)))$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\(+)?blah(?(1)(\\)))$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\(+)?blah(?(1)(\\)))$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\(+)?blah(?(1)(\\)))$",[]))), - <<"blah)">> = iolist_to_binary(join(re:split("blah)","^(\\(+)?blah(?(1)(\\)))$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\(+)?blah(?(1)(\\)))$",[]))), + <<"blah)">> = iolist_to_binary(join(re:split("blah)","^(\\(+)?blah(?(1)(\\)))$",[trim]))), <<"blah)">> = iolist_to_binary(join(re:split("blah)","^(\\(+)?blah(?(1)(\\)))$",[{parts, - 2}]))), - <<"blah)">> = iolist_to_binary(join(re:split("blah)","^(\\(+)?blah(?(1)(\\)))$",[]))), - <<"(blah">> = iolist_to_binary(join(re:split("(blah","^(\\(+)?blah(?(1)(\\)))$",[trim]))), + 2}]))), + <<"blah)">> = iolist_to_binary(join(re:split("blah)","^(\\(+)?blah(?(1)(\\)))$",[]))), + <<"(blah">> = iolist_to_binary(join(re:split("(blah","^(\\(+)?blah(?(1)(\\)))$",[trim]))), <<"(blah">> = iolist_to_binary(join(re:split("(blah","^(\\(+)?blah(?(1)(\\)))$",[{parts, - 2}]))), - <<"(blah">> = iolist_to_binary(join(re:split("(blah","^(\\(+)?blah(?(1)(\\)))$",[]))), - <<"">> = iolist_to_binary(join(re:split("a","(?(?!a)b|a)",[trim]))), + 2}]))), + <<"(blah">> = iolist_to_binary(join(re:split("(blah","^(\\(+)?blah(?(1)(\\)))$",[]))), + <<"">> = iolist_to_binary(join(re:split("a","(?(?!a)b|a)",[trim]))), <<":">> = iolist_to_binary(join(re:split("a","(?(?!a)b|a)",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a","(?(?!a)b|a)",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?(?=a)b|a)",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a","(?(?!a)b|a)",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?(?=a)b|a)",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?(?=a)b|a)",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?(?=a)b|a)",[]))), - <<"a">> = iolist_to_binary(join(re:split("a","(?(?=a)b|a)",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?(?=a)b|a)",[]))), + <<"a">> = iolist_to_binary(join(re:split("a","(?(?=a)b|a)",[trim]))), <<"a">> = iolist_to_binary(join(re:split("a","(?(?=a)b|a)",[{parts, - 2}]))), - <<"a">> = iolist_to_binary(join(re:split("a","(?(?=a)b|a)",[]))), - <<"a">> = iolist_to_binary(join(re:split("a","(?(?=a)b|a)",[trim]))), + 2}]))), + <<"a">> = iolist_to_binary(join(re:split("a","(?(?=a)b|a)",[]))), + <<"a">> = iolist_to_binary(join(re:split("a","(?(?=a)b|a)",[trim]))), <<"a">> = iolist_to_binary(join(re:split("a","(?(?=a)b|a)",[{parts, - 2}]))), - <<"a">> = iolist_to_binary(join(re:split("a","(?(?=a)b|a)",[]))), - <<"">> = iolist_to_binary(join(re:split("a","(?(?=a)a|b)",[trim]))), + 2}]))), + <<"a">> = iolist_to_binary(join(re:split("a","(?(?=a)b|a)",[]))), + <<"">> = iolist_to_binary(join(re:split("a","(?(?=a)a|b)",[trim]))), <<":">> = iolist_to_binary(join(re:split("a","(?(?=a)a|b)",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a","(?(?=a)a|b)",[]))), - <<"a:a:aab">> = iolist_to_binary(join(re:split("aaab","(?=(a+?))(\\1ab)",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a","(?(?=a)a|b)",[]))), + <<"a:a:aab">> = iolist_to_binary(join(re:split("aaab","(?=(a+?))(\\1ab)",[trim]))), <<"a:a:aab:">> = iolist_to_binary(join(re:split("aaab","(?=(a+?))(\\1ab)",[{parts, - 2}]))), - <<"a:a:aab:">> = iolist_to_binary(join(re:split("aaab","(?=(a+?))(\\1ab)",[]))), - <<":one:">> = iolist_to_binary(join(re:split("one:","(\\w+:)+",[trim]))), + 2}]))), + <<"a:a:aab:">> = iolist_to_binary(join(re:split("aaab","(?=(a+?))(\\1ab)",[]))), + <<":one:">> = iolist_to_binary(join(re:split("one:","(\\w+:)+",[trim]))), <<":one::">> = iolist_to_binary(join(re:split("one:","(\\w+:)+",[{parts, - 2}]))), - <<":one::">> = iolist_to_binary(join(re:split("one:","(\\w+:)+",[]))), - <<"a:a">> = iolist_to_binary(join(re:split("a","$(?<=^(a))",[trim]))), + 2}]))), + <<":one::">> = iolist_to_binary(join(re:split("one:","(\\w+:)+",[]))), + <<"a:a">> = iolist_to_binary(join(re:split("a","$(?<=^(a))",[trim]))), <<"a:a:">> = iolist_to_binary(join(re:split("a","$(?<=^(a))",[{parts, - 2}]))), - <<"a:a:">> = iolist_to_binary(join(re:split("a","$(?<=^(a))",[]))), - <<"a:a:aab">> = iolist_to_binary(join(re:split("aaab","(?=(a+?))(\\1ab)",[trim]))), + 2}]))), + <<"a:a:">> = iolist_to_binary(join(re:split("a","$(?<=^(a))",[]))), + <<"a:a:aab">> = iolist_to_binary(join(re:split("aaab","(?=(a+?))(\\1ab)",[trim]))), <<"a:a:aab:">> = iolist_to_binary(join(re:split("aaab","(?=(a+?))(\\1ab)",[{parts, - 2}]))), - <<"a:a:aab:">> = iolist_to_binary(join(re:split("aaab","(?=(a+?))(\\1ab)",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?=(a+?))\\1ab",[trim]))), + 2}]))), + <<"a:a:aab:">> = iolist_to_binary(join(re:split("aaab","(?=(a+?))(\\1ab)",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?=(a+?))\\1ab",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?=(a+?))\\1ab",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?=(a+?))\\1ab",[]))), - <<"aaab">> = iolist_to_binary(join(re:split("aaab","^(?=(a+?))\\1ab",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?=(a+?))\\1ab",[]))), + <<"aaab">> = iolist_to_binary(join(re:split("aaab","^(?=(a+?))\\1ab",[trim]))), <<"aaab">> = iolist_to_binary(join(re:split("aaab","^(?=(a+?))\\1ab",[{parts, - 2}]))), - <<"aaab">> = iolist_to_binary(join(re:split("aaab","^(?=(a+?))\\1ab",[]))), - <<"aaab">> = iolist_to_binary(join(re:split("aaab","^(?=(a+?))\\1ab",[trim]))), + 2}]))), + <<"aaab">> = iolist_to_binary(join(re:split("aaab","^(?=(a+?))\\1ab",[]))), + <<"aaab">> = iolist_to_binary(join(re:split("aaab","^(?=(a+?))\\1ab",[trim]))), <<"aaab">> = iolist_to_binary(join(re:split("aaab","^(?=(a+?))\\1ab",[{parts, - 2}]))), - <<"aaab">> = iolist_to_binary(join(re:split("aaab","^(?=(a+?))\\1ab",[]))), - <<"::abcd">> = iolist_to_binary(join(re:split("abcd","([\\w:]+::)?(\\w+)$",[trim]))), + 2}]))), + <<"aaab">> = iolist_to_binary(join(re:split("aaab","^(?=(a+?))\\1ab",[]))), + <<"::abcd">> = iolist_to_binary(join(re:split("abcd","([\\w:]+::)?(\\w+)$",[trim]))), <<"::abcd:">> = iolist_to_binary(join(re:split("abcd","([\\w:]+::)?(\\w+)$",[{parts, - 2}]))), - <<"::abcd:">> = iolist_to_binary(join(re:split("abcd","([\\w:]+::)?(\\w+)$",[]))), - <<":xy:z::::abcd">> = iolist_to_binary(join(re:split("xy:z:::abcd","([\\w:]+::)?(\\w+)$",[trim]))), + 2}]))), + <<"::abcd:">> = iolist_to_binary(join(re:split("abcd","([\\w:]+::)?(\\w+)$",[]))), + <<":xy:z::::abcd">> = iolist_to_binary(join(re:split("xy:z:::abcd","([\\w:]+::)?(\\w+)$",[trim]))), <<":xy:z::::abcd:">> = iolist_to_binary(join(re:split("xy:z:::abcd","([\\w:]+::)?(\\w+)$",[{parts, - 2}]))), - <<":xy:z::::abcd:">> = iolist_to_binary(join(re:split("xy:z:::abcd","([\\w:]+::)?(\\w+)$",[]))), - <<":c:d">> = iolist_to_binary(join(re:split("aexycd","^[^bcd]*(c+)",[trim]))), + 2}]))), + <<":xy:z::::abcd:">> = iolist_to_binary(join(re:split("xy:z:::abcd","([\\w:]+::)?(\\w+)$",[]))), + <<":c:d">> = iolist_to_binary(join(re:split("aexycd","^[^bcd]*(c+)",[trim]))), <<":c:d">> = iolist_to_binary(join(re:split("aexycd","^[^bcd]*(c+)",[{parts, - 2}]))), - <<":c:d">> = iolist_to_binary(join(re:split("aexycd","^[^bcd]*(c+)",[]))), - <<"c:aa">> = iolist_to_binary(join(re:split("caab","(a*)b+",[trim]))), + 2}]))), + <<":c:d">> = iolist_to_binary(join(re:split("aexycd","^[^bcd]*(c+)",[]))), + <<"c:aa">> = iolist_to_binary(join(re:split("caab","(a*)b+",[trim]))), <<"c:aa:">> = iolist_to_binary(join(re:split("caab","(a*)b+",[{parts, - 2}]))), - <<"c:aa:">> = iolist_to_binary(join(re:split("caab","(a*)b+",[]))), - <<"::abcd">> = iolist_to_binary(join(re:split("abcd","([\\w:]+::)?(\\w+)$",[trim]))), + 2}]))), + <<"c:aa:">> = iolist_to_binary(join(re:split("caab","(a*)b+",[]))), + <<"::abcd">> = iolist_to_binary(join(re:split("abcd","([\\w:]+::)?(\\w+)$",[trim]))), <<"::abcd:">> = iolist_to_binary(join(re:split("abcd","([\\w:]+::)?(\\w+)$",[{parts, - 2}]))), - <<"::abcd:">> = iolist_to_binary(join(re:split("abcd","([\\w:]+::)?(\\w+)$",[]))), - <<":xy:z::::abcd">> = iolist_to_binary(join(re:split("xy:z:::abcd","([\\w:]+::)?(\\w+)$",[trim]))), + 2}]))), + <<"::abcd:">> = iolist_to_binary(join(re:split("abcd","([\\w:]+::)?(\\w+)$",[]))), + <<":xy:z::::abcd">> = iolist_to_binary(join(re:split("xy:z:::abcd","([\\w:]+::)?(\\w+)$",[trim]))), <<":xy:z::::abcd:">> = iolist_to_binary(join(re:split("xy:z:::abcd","([\\w:]+::)?(\\w+)$",[{parts, - 2}]))), - <<":xy:z::::abcd:">> = iolist_to_binary(join(re:split("xy:z:::abcd","([\\w:]+::)?(\\w+)$",[]))), - <<"*** ::Failers">> = iolist_to_binary(join(re:split("*** Failers","([\\w:]+::)?(\\w+)$",[trim]))), + 2}]))), + <<":xy:z::::abcd:">> = iolist_to_binary(join(re:split("xy:z:::abcd","([\\w:]+::)?(\\w+)$",[]))), + <<"*** ::Failers">> = iolist_to_binary(join(re:split("*** Failers","([\\w:]+::)?(\\w+)$",[trim]))), <<"*** ::Failers:">> = iolist_to_binary(join(re:split("*** Failers","([\\w:]+::)?(\\w+)$",[{parts, - 2}]))), - <<"*** ::Failers:">> = iolist_to_binary(join(re:split("*** Failers","([\\w:]+::)?(\\w+)$",[]))), - <<"abcd:">> = iolist_to_binary(join(re:split("abcd:","([\\w:]+::)?(\\w+)$",[trim]))), + 2}]))), + <<"*** ::Failers:">> = iolist_to_binary(join(re:split("*** Failers","([\\w:]+::)?(\\w+)$",[]))), + <<"abcd:">> = iolist_to_binary(join(re:split("abcd:","([\\w:]+::)?(\\w+)$",[trim]))), <<"abcd:">> = iolist_to_binary(join(re:split("abcd:","([\\w:]+::)?(\\w+)$",[{parts, - 2}]))), - <<"abcd:">> = iolist_to_binary(join(re:split("abcd:","([\\w:]+::)?(\\w+)$",[]))), - <<"abcd:">> = iolist_to_binary(join(re:split("abcd:","([\\w:]+::)?(\\w+)$",[trim]))), + 2}]))), + <<"abcd:">> = iolist_to_binary(join(re:split("abcd:","([\\w:]+::)?(\\w+)$",[]))), + <<"abcd:">> = iolist_to_binary(join(re:split("abcd:","([\\w:]+::)?(\\w+)$",[trim]))), <<"abcd:">> = iolist_to_binary(join(re:split("abcd:","([\\w:]+::)?(\\w+)$",[{parts, - 2}]))), - <<"abcd:">> = iolist_to_binary(join(re:split("abcd:","([\\w:]+::)?(\\w+)$",[]))), - <<":c:d">> = iolist_to_binary(join(re:split("aexycd","^[^bcd]*(c+)",[trim]))), + 2}]))), + <<"abcd:">> = iolist_to_binary(join(re:split("abcd:","([\\w:]+::)?(\\w+)$",[]))), + <<":c:d">> = iolist_to_binary(join(re:split("aexycd","^[^bcd]*(c+)",[trim]))), <<":c:d">> = iolist_to_binary(join(re:split("aexycd","^[^bcd]*(c+)",[{parts, - 2}]))), - <<":c:d">> = iolist_to_binary(join(re:split("aexycd","^[^bcd]*(c+)",[]))), + 2}]))), + <<":c:d">> = iolist_to_binary(join(re:split("aexycd","^[^bcd]*(c+)",[]))), ok. run31() -> - <<"">> = iolist_to_binary(join(re:split("aaab","(?>a+)b",[trim]))), + <<"">> = iolist_to_binary(join(re:split("aaab","(?>a+)b",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaab","(?>a+)b",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaab","(?>a+)b",[]))), - <<"a::[:b]::">> = iolist_to_binary(join(re:split("a:[b]:","([[:]+)",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaab","(?>a+)b",[]))), + <<"a::[:b]::">> = iolist_to_binary(join(re:split("a:[b]:","([[:]+)",[trim]))), <<"a::[:b]:">> = iolist_to_binary(join(re:split("a:[b]:","([[:]+)",[{parts, - 2}]))), - <<"a::[:b]:::">> = iolist_to_binary(join(re:split("a:[b]:","([[:]+)",[]))), - <<"a:=[:b]:=">> = iolist_to_binary(join(re:split("a=[b]=","([[=]+)",[trim]))), + 2}]))), + <<"a::[:b]:::">> = iolist_to_binary(join(re:split("a:[b]:","([[:]+)",[]))), + <<"a:=[:b]:=">> = iolist_to_binary(join(re:split("a=[b]=","([[=]+)",[trim]))), <<"a:=[:b]=">> = iolist_to_binary(join(re:split("a=[b]=","([[=]+)",[{parts, - 2}]))), - <<"a:=[:b]:=:">> = iolist_to_binary(join(re:split("a=[b]=","([[=]+)",[]))), - <<"a:.[:b]:.">> = iolist_to_binary(join(re:split("a.[b].","([[.]+)",[trim]))), + 2}]))), + <<"a:=[:b]:=:">> = iolist_to_binary(join(re:split("a=[b]=","([[=]+)",[]))), + <<"a:.[:b]:.">> = iolist_to_binary(join(re:split("a.[b].","([[.]+)",[trim]))), <<"a:.[:b].">> = iolist_to_binary(join(re:split("a.[b].","([[.]+)",[{parts, - 2}]))), - <<"a:.[:b]:.:">> = iolist_to_binary(join(re:split("a.[b].","([[.]+)",[]))), - <<":aaab">> = iolist_to_binary(join(re:split("aaab","((?>a+)b)",[trim]))), + 2}]))), + <<"a:.[:b]:.:">> = iolist_to_binary(join(re:split("a.[b].","([[.]+)",[]))), + <<":aaab">> = iolist_to_binary(join(re:split("aaab","((?>a+)b)",[trim]))), <<":aaab:">> = iolist_to_binary(join(re:split("aaab","((?>a+)b)",[{parts, - 2}]))), - <<":aaab:">> = iolist_to_binary(join(re:split("aaab","((?>a+)b)",[]))), - <<":aaa">> = iolist_to_binary(join(re:split("aaab","(?>(a+))b",[trim]))), + 2}]))), + <<":aaab:">> = iolist_to_binary(join(re:split("aaab","((?>a+)b)",[]))), + <<":aaa">> = iolist_to_binary(join(re:split("aaab","(?>(a+))b",[trim]))), <<":aaa:">> = iolist_to_binary(join(re:split("aaab","(?>(a+))b",[{parts, - 2}]))), - <<":aaa:">> = iolist_to_binary(join(re:split("aaab","(?>(a+))b",[]))), - <<"((:x">> = iolist_to_binary(join(re:split("((abc(ade)ufh()()x","((?>[^()]+)|\\([^()]*\\))+",[trim]))), + 2}]))), + <<":aaa:">> = iolist_to_binary(join(re:split("aaab","(?>(a+))b",[]))), + <<"((:x">> = iolist_to_binary(join(re:split("((abc(ade)ufh()()x","((?>[^()]+)|\\([^()]*\\))+",[trim]))), <<"((:x:">> = iolist_to_binary(join(re:split("((abc(ade)ufh()()x","((?>[^()]+)|\\([^()]*\\))+",[{parts, - 2}]))), - <<"((:x:">> = iolist_to_binary(join(re:split("((abc(ade)ufh()()x","((?>[^()]+)|\\([^()]*\\))+",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a\\Z",[trim]))), + 2}]))), + <<"((:x:">> = iolist_to_binary(join(re:split("((abc(ade)ufh()()x","((?>[^()]+)|\\([^()]*\\))+",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a\\Z",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a\\Z",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a\\Z",[]))), - <<"aaab">> = iolist_to_binary(join(re:split("aaab","a\\Z",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a\\Z",[]))), + <<"aaab">> = iolist_to_binary(join(re:split("aaab","a\\Z",[trim]))), <<"aaab">> = iolist_to_binary(join(re:split("aaab","a\\Z",[{parts, - 2}]))), - <<"aaab">> = iolist_to_binary(join(re:split("aaab","a\\Z",[]))), + 2}]))), + <<"aaab">> = iolist_to_binary(join(re:split("aaab","a\\Z",[]))), <<"a b">> = iolist_to_binary(join(re:split("a -b","a\\Z",[trim]))), +b","a\\Z",[trim]))), <<"a b">> = iolist_to_binary(join(re:split("a -b","a\\Z",[{parts,2}]))), +b","a\\Z",[{parts,2}]))), <<"a b">> = iolist_to_binary(join(re:split("a -b","a\\Z",[]))), +b","a\\Z",[]))), <<"a ">> = iolist_to_binary(join(re:split("a -b","b\\Z",[trim]))), +b","b\\Z",[trim]))), <<"a :">> = iolist_to_binary(join(re:split("a -b","b\\Z",[{parts,2}]))), +b","b\\Z",[{parts,2}]))), <<"a :">> = iolist_to_binary(join(re:split("a -b","b\\Z",[]))), +b","b\\Z",[]))), <<"a ">> = iolist_to_binary(join(re:split("a -b","b\\Z",[trim]))), +b","b\\Z",[trim]))), <<"a :">> = iolist_to_binary(join(re:split("a -b","b\\Z",[{parts,2}]))), +b","b\\Z",[{parts,2}]))), <<"a :">> = iolist_to_binary(join(re:split("a -b","b\\Z",[]))), +b","b\\Z",[]))), <<"a ">> = iolist_to_binary(join(re:split("a -b","b\\z",[trim]))), +b","b\\z",[trim]))), <<"a :">> = iolist_to_binary(join(re:split("a -b","b\\z",[{parts,2}]))), +b","b\\z",[{parts,2}]))), <<"a :">> = iolist_to_binary(join(re:split("a -b","b\\z",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","b\\z",[trim]))), +b","b\\z",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","b\\z",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","b\\z",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","b\\z",[]))), - <<"">> = iolist_to_binary(join(re:split("a","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","b\\z",[]))), + <<"">> = iolist_to_binary(join(re:split("a","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), <<"::">> = iolist_to_binary(join(re:split("a","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("a","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("a","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), <<"::">> = iolist_to_binary(join(re:split("abc","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("abc","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), - <<"">> = iolist_to_binary(join(re:split("a-b","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("abc","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), + <<"">> = iolist_to_binary(join(re:split("a-b","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), <<"::">> = iolist_to_binary(join(re:split("a-b","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("a-b","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), - <<"">> = iolist_to_binary(join(re:split("0-9","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("a-b","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), + <<"">> = iolist_to_binary(join(re:split("0-9","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), <<"::">> = iolist_to_binary(join(re:split("0-9","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("0-9","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), - <<"">> = iolist_to_binary(join(re:split("a.b","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("0-9","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), + <<"">> = iolist_to_binary(join(re:split("a.b","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), <<"::">> = iolist_to_binary(join(re:split("a.b","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("a.b","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), - <<"">> = iolist_to_binary(join(re:split("5.6.7","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("a.b","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), + <<"">> = iolist_to_binary(join(re:split("5.6.7","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), <<"::">> = iolist_to_binary(join(re:split("5.6.7","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("5.6.7","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), - <<"">> = iolist_to_binary(join(re:split("the.quick.brown.fox","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("5.6.7","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), + <<"">> = iolist_to_binary(join(re:split("the.quick.brown.fox","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), <<"::">> = iolist_to_binary(join(re:split("the.quick.brown.fox","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("the.quick.brown.fox","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), - <<"">> = iolist_to_binary(join(re:split("a100.b200.300c","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("the.quick.brown.fox","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), + <<"">> = iolist_to_binary(join(re:split("a100.b200.300c","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), <<"::">> = iolist_to_binary(join(re:split("a100.b200.300c","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("a100.b200.300c","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), - <<"">> = iolist_to_binary(join(re:split("12-ab.1245","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("a100.b200.300c","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), + <<"">> = iolist_to_binary(join(re:split("12-ab.1245","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), <<"::">> = iolist_to_binary(join(re:split("12-ab.1245","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("12-ab.1245","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("12-ab.1245","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), - <<"">> = iolist_to_binary(join(re:split("","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), + <<"">> = iolist_to_binary(join(re:split("","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), <<"">> = iolist_to_binary(join(re:split("","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[{parts, - 2}]))), - <<"">> = iolist_to_binary(join(re:split("","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), - <<".a">> = iolist_to_binary(join(re:split(".a","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), + 2}]))), + <<"">> = iolist_to_binary(join(re:split("","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), + <<".a">> = iolist_to_binary(join(re:split(".a","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), <<".a">> = iolist_to_binary(join(re:split(".a","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[{parts, - 2}]))), - <<".a">> = iolist_to_binary(join(re:split(".a","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), - <<"-a">> = iolist_to_binary(join(re:split("-a","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), + 2}]))), + <<".a">> = iolist_to_binary(join(re:split(".a","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), + <<"-a">> = iolist_to_binary(join(re:split("-a","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), <<"-a">> = iolist_to_binary(join(re:split("-a","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[{parts, - 2}]))), - <<"-a">> = iolist_to_binary(join(re:split("-a","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), - <<"a-">> = iolist_to_binary(join(re:split("a-","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), + 2}]))), + <<"-a">> = iolist_to_binary(join(re:split("-a","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), + <<"a-">> = iolist_to_binary(join(re:split("a-","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), <<"a-">> = iolist_to_binary(join(re:split("a-","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[{parts, - 2}]))), - <<"a-">> = iolist_to_binary(join(re:split("a-","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), - <<"a.">> = iolist_to_binary(join(re:split("a.","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), + 2}]))), + <<"a-">> = iolist_to_binary(join(re:split("a-","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), + <<"a.">> = iolist_to_binary(join(re:split("a.","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), <<"a.">> = iolist_to_binary(join(re:split("a.","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[{parts, - 2}]))), - <<"a.">> = iolist_to_binary(join(re:split("a.","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), - <<"a_b">> = iolist_to_binary(join(re:split("a_b","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), + 2}]))), + <<"a.">> = iolist_to_binary(join(re:split("a.","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), + <<"a_b">> = iolist_to_binary(join(re:split("a_b","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), <<"a_b">> = iolist_to_binary(join(re:split("a_b","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[{parts, - 2}]))), - <<"a_b">> = iolist_to_binary(join(re:split("a_b","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), - <<"a.-">> = iolist_to_binary(join(re:split("a.-","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), + 2}]))), + <<"a_b">> = iolist_to_binary(join(re:split("a_b","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), + <<"a.-">> = iolist_to_binary(join(re:split("a.-","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), <<"a.-">> = iolist_to_binary(join(re:split("a.-","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[{parts, - 2}]))), - <<"a.-">> = iolist_to_binary(join(re:split("a.-","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), - <<"a..">> = iolist_to_binary(join(re:split("a..","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), + 2}]))), + <<"a.-">> = iolist_to_binary(join(re:split("a.-","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), + <<"a..">> = iolist_to_binary(join(re:split("a..","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), <<"a..">> = iolist_to_binary(join(re:split("a..","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[{parts, - 2}]))), - <<"a..">> = iolist_to_binary(join(re:split("a..","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), - <<"ab..bc">> = iolist_to_binary(join(re:split("ab..bc","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), + 2}]))), + <<"a..">> = iolist_to_binary(join(re:split("a..","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), + <<"ab..bc">> = iolist_to_binary(join(re:split("ab..bc","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), <<"ab..bc">> = iolist_to_binary(join(re:split("ab..bc","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[{parts, - 2}]))), - <<"ab..bc">> = iolist_to_binary(join(re:split("ab..bc","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), - <<"the.quick.brown.fox-">> = iolist_to_binary(join(re:split("the.quick.brown.fox-","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), + 2}]))), + <<"ab..bc">> = iolist_to_binary(join(re:split("ab..bc","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), + <<"the.quick.brown.fox-">> = iolist_to_binary(join(re:split("the.quick.brown.fox-","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), <<"the.quick.brown.fox-">> = iolist_to_binary(join(re:split("the.quick.brown.fox-","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[{parts, - 2}]))), - <<"the.quick.brown.fox-">> = iolist_to_binary(join(re:split("the.quick.brown.fox-","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), - <<"the.quick.brown.fox.">> = iolist_to_binary(join(re:split("the.quick.brown.fox.","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), + 2}]))), + <<"the.quick.brown.fox-">> = iolist_to_binary(join(re:split("the.quick.brown.fox-","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), + <<"the.quick.brown.fox.">> = iolist_to_binary(join(re:split("the.quick.brown.fox.","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), <<"the.quick.brown.fox.">> = iolist_to_binary(join(re:split("the.quick.brown.fox.","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[{parts, - 2}]))), - <<"the.quick.brown.fox.">> = iolist_to_binary(join(re:split("the.quick.brown.fox.","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), - <<"the.quick.brown.fox_">> = iolist_to_binary(join(re:split("the.quick.brown.fox_","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), + 2}]))), + <<"the.quick.brown.fox.">> = iolist_to_binary(join(re:split("the.quick.brown.fox.","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), + <<"the.quick.brown.fox_">> = iolist_to_binary(join(re:split("the.quick.brown.fox_","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), <<"the.quick.brown.fox_">> = iolist_to_binary(join(re:split("the.quick.brown.fox_","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[{parts, - 2}]))), - <<"the.quick.brown.fox_">> = iolist_to_binary(join(re:split("the.quick.brown.fox_","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), - <<"the.quick.brown.fox+">> = iolist_to_binary(join(re:split("the.quick.brown.fox+","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), + 2}]))), + <<"the.quick.brown.fox_">> = iolist_to_binary(join(re:split("the.quick.brown.fox_","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), + <<"the.quick.brown.fox+">> = iolist_to_binary(join(re:split("the.quick.brown.fox+","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), <<"the.quick.brown.fox+">> = iolist_to_binary(join(re:split("the.quick.brown.fox+","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[{parts, - 2}]))), - <<"the.quick.brown.fox+">> = iolist_to_binary(join(re:split("the.quick.brown.fox+","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), - <<":abcd">> = iolist_to_binary(join(re:split("alphabetabcd","(?>.*)(?<=(abcd|wxyz))",[trim]))), + 2}]))), + <<"the.quick.brown.fox+">> = iolist_to_binary(join(re:split("the.quick.brown.fox+","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), + <<":abcd">> = iolist_to_binary(join(re:split("alphabetabcd","(?>.*)(?<=(abcd|wxyz))",[trim]))), <<":abcd:">> = iolist_to_binary(join(re:split("alphabetabcd","(?>.*)(?<=(abcd|wxyz))",[{parts, - 2}]))), - <<":abcd:">> = iolist_to_binary(join(re:split("alphabetabcd","(?>.*)(?<=(abcd|wxyz))",[]))), - <<":wxyz">> = iolist_to_binary(join(re:split("endingwxyz","(?>.*)(?<=(abcd|wxyz))",[trim]))), + 2}]))), + <<":abcd:">> = iolist_to_binary(join(re:split("alphabetabcd","(?>.*)(?<=(abcd|wxyz))",[]))), + <<":wxyz">> = iolist_to_binary(join(re:split("endingwxyz","(?>.*)(?<=(abcd|wxyz))",[trim]))), <<":wxyz:">> = iolist_to_binary(join(re:split("endingwxyz","(?>.*)(?<=(abcd|wxyz))",[{parts, - 2}]))), - <<":wxyz:">> = iolist_to_binary(join(re:split("endingwxyz","(?>.*)(?<=(abcd|wxyz))",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?>.*)(?<=(abcd|wxyz))",[trim]))), + 2}]))), + <<":wxyz:">> = iolist_to_binary(join(re:split("endingwxyz","(?>.*)(?<=(abcd|wxyz))",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?>.*)(?<=(abcd|wxyz))",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?>.*)(?<=(abcd|wxyz))",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?>.*)(?<=(abcd|wxyz))",[]))), - <<"a rather long string that doesn't end with one of them">> = iolist_to_binary(join(re:split("a rather long string that doesn't end with one of them","(?>.*)(?<=(abcd|wxyz))",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?>.*)(?<=(abcd|wxyz))",[]))), + <<"a rather long string that doesn't end with one of them">> = iolist_to_binary(join(re:split("a rather long string that doesn't end with one of them","(?>.*)(?<=(abcd|wxyz))",[trim]))), <<"a rather long string that doesn't end with one of them">> = iolist_to_binary(join(re:split("a rather long string that doesn't end with one of them","(?>.*)(?<=(abcd|wxyz))",[{parts, - 2}]))), - <<"a rather long string that doesn't end with one of them">> = iolist_to_binary(join(re:split("a rather long string that doesn't end with one of them","(?>.*)(?<=(abcd|wxyz))",[]))), - <<"">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark otherword","word (?>(?:(?!otherword)[a-zA-Z0-9]+ ){0,30})otherword",[trim]))), + 2}]))), + <<"a rather long string that doesn't end with one of them">> = iolist_to_binary(join(re:split("a rather long string that doesn't end with one of them","(?>.*)(?<=(abcd|wxyz))",[]))), + <<"">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark otherword","word (?>(?:(?!otherword)[a-zA-Z0-9]+ ){0,30})otherword",[trim]))), <<":">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark otherword","word (?>(?:(?!otherword)[a-zA-Z0-9]+ ){0,30})otherword",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark otherword","word (?>(?:(?!otherword)[a-zA-Z0-9]+ ){0,30})otherword",[]))), - <<"word cat dog elephant mussel cow horse canary baboon snake shark">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark","word (?>(?:(?!otherword)[a-zA-Z0-9]+ ){0,30})otherword",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark otherword","word (?>(?:(?!otherword)[a-zA-Z0-9]+ ){0,30})otherword",[]))), + <<"word cat dog elephant mussel cow horse canary baboon snake shark">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark","word (?>(?:(?!otherword)[a-zA-Z0-9]+ ){0,30})otherword",[trim]))), <<"word cat dog elephant mussel cow horse canary baboon snake shark">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark","word (?>(?:(?!otherword)[a-zA-Z0-9]+ ){0,30})otherword",[{parts, - 2}]))), - <<"word cat dog elephant mussel cow horse canary baboon snake shark">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark","word (?>(?:(?!otherword)[a-zA-Z0-9]+ ){0,30})otherword",[]))), - <<"word cat dog elephant mussel cow horse canary baboon snake shark the quick brown fox and the lazy dog and several other words getting close to thirty by now I hope">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark the quick brown fox and the lazy dog and several other words getting close to thirty by now I hope","word (?>[a-zA-Z0-9]+ ){0,30}otherword",[trim]))), + 2}]))), + <<"word cat dog elephant mussel cow horse canary baboon snake shark">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark","word (?>(?:(?!otherword)[a-zA-Z0-9]+ ){0,30})otherword",[]))), + <<"word cat dog elephant mussel cow horse canary baboon snake shark the quick brown fox and the lazy dog and several other words getting close to thirty by now I hope">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark the quick brown fox and the lazy dog and several other words getting close to thirty by now I hope","word (?>[a-zA-Z0-9]+ ){0,30}otherword",[trim]))), <<"word cat dog elephant mussel cow horse canary baboon snake shark the quick brown fox and the lazy dog and several other words getting close to thirty by now I hope">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark the quick brown fox and the lazy dog and several other words getting close to thirty by now I hope","word (?>[a-zA-Z0-9]+ ){0,30}otherword",[{parts, - 2}]))), - <<"word cat dog elephant mussel cow horse canary baboon snake shark the quick brown fox and the lazy dog and several other words getting close to thirty by now I hope">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark the quick brown fox and the lazy dog and several other words getting close to thirty by now I hope","word (?>[a-zA-Z0-9]+ ){0,30}otherword",[]))), - <<"999">> = iolist_to_binary(join(re:split("999foo","(?<=\\d{3}(?!999))foo",[trim]))), + 2}]))), + <<"word cat dog elephant mussel cow horse canary baboon snake shark the quick brown fox and the lazy dog and several other words getting close to thirty by now I hope">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark the quick brown fox and the lazy dog and several other words getting close to thirty by now I hope","word (?>[a-zA-Z0-9]+ ){0,30}otherword",[]))), + <<"999">> = iolist_to_binary(join(re:split("999foo","(?<=\\d{3}(?!999))foo",[trim]))), <<"999:">> = iolist_to_binary(join(re:split("999foo","(?<=\\d{3}(?!999))foo",[{parts, - 2}]))), - <<"999:">> = iolist_to_binary(join(re:split("999foo","(?<=\\d{3}(?!999))foo",[]))), - <<"123999">> = iolist_to_binary(join(re:split("123999foo","(?<=\\d{3}(?!999))foo",[trim]))), + 2}]))), + <<"999:">> = iolist_to_binary(join(re:split("999foo","(?<=\\d{3}(?!999))foo",[]))), + <<"123999">> = iolist_to_binary(join(re:split("123999foo","(?<=\\d{3}(?!999))foo",[trim]))), <<"123999:">> = iolist_to_binary(join(re:split("123999foo","(?<=\\d{3}(?!999))foo",[{parts, - 2}]))), - <<"123999:">> = iolist_to_binary(join(re:split("123999foo","(?<=\\d{3}(?!999))foo",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=\\d{3}(?!999))foo",[trim]))), + 2}]))), + <<"123999:">> = iolist_to_binary(join(re:split("123999foo","(?<=\\d{3}(?!999))foo",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=\\d{3}(?!999))foo",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=\\d{3}(?!999))foo",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=\\d{3}(?!999))foo",[]))), - <<"123abcfoo">> = iolist_to_binary(join(re:split("123abcfoo","(?<=\\d{3}(?!999))foo",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=\\d{3}(?!999))foo",[]))), + <<"123abcfoo">> = iolist_to_binary(join(re:split("123abcfoo","(?<=\\d{3}(?!999))foo",[trim]))), <<"123abcfoo">> = iolist_to_binary(join(re:split("123abcfoo","(?<=\\d{3}(?!999))foo",[{parts, - 2}]))), - <<"123abcfoo">> = iolist_to_binary(join(re:split("123abcfoo","(?<=\\d{3}(?!999))foo",[]))), - <<"999">> = iolist_to_binary(join(re:split("999foo","(?<=(?!...999)\\d{3})foo",[trim]))), + 2}]))), + <<"123abcfoo">> = iolist_to_binary(join(re:split("123abcfoo","(?<=\\d{3}(?!999))foo",[]))), + <<"999">> = iolist_to_binary(join(re:split("999foo","(?<=(?!...999)\\d{3})foo",[trim]))), <<"999:">> = iolist_to_binary(join(re:split("999foo","(?<=(?!...999)\\d{3})foo",[{parts, - 2}]))), - <<"999:">> = iolist_to_binary(join(re:split("999foo","(?<=(?!...999)\\d{3})foo",[]))), - <<"123999">> = iolist_to_binary(join(re:split("123999foo","(?<=(?!...999)\\d{3})foo",[trim]))), + 2}]))), + <<"999:">> = iolist_to_binary(join(re:split("999foo","(?<=(?!...999)\\d{3})foo",[]))), + <<"123999">> = iolist_to_binary(join(re:split("123999foo","(?<=(?!...999)\\d{3})foo",[trim]))), <<"123999:">> = iolist_to_binary(join(re:split("123999foo","(?<=(?!...999)\\d{3})foo",[{parts, - 2}]))), - <<"123999:">> = iolist_to_binary(join(re:split("123999foo","(?<=(?!...999)\\d{3})foo",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=(?!...999)\\d{3})foo",[trim]))), + 2}]))), + <<"123999:">> = iolist_to_binary(join(re:split("123999foo","(?<=(?!...999)\\d{3})foo",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=(?!...999)\\d{3})foo",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=(?!...999)\\d{3})foo",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=(?!...999)\\d{3})foo",[]))), - <<"123abcfoo">> = iolist_to_binary(join(re:split("123abcfoo","(?<=(?!...999)\\d{3})foo",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=(?!...999)\\d{3})foo",[]))), + <<"123abcfoo">> = iolist_to_binary(join(re:split("123abcfoo","(?<=(?!...999)\\d{3})foo",[trim]))), <<"123abcfoo">> = iolist_to_binary(join(re:split("123abcfoo","(?<=(?!...999)\\d{3})foo",[{parts, - 2}]))), - <<"123abcfoo">> = iolist_to_binary(join(re:split("123abcfoo","(?<=(?!...999)\\d{3})foo",[]))), - <<"123abc">> = iolist_to_binary(join(re:split("123abcfoo","(?<=\\d{3}(?!999)...)foo",[trim]))), + 2}]))), + <<"123abcfoo">> = iolist_to_binary(join(re:split("123abcfoo","(?<=(?!...999)\\d{3})foo",[]))), + <<"123abc">> = iolist_to_binary(join(re:split("123abcfoo","(?<=\\d{3}(?!999)...)foo",[trim]))), <<"123abc:">> = iolist_to_binary(join(re:split("123abcfoo","(?<=\\d{3}(?!999)...)foo",[{parts, - 2}]))), - <<"123abc:">> = iolist_to_binary(join(re:split("123abcfoo","(?<=\\d{3}(?!999)...)foo",[]))), - <<"123456">> = iolist_to_binary(join(re:split("123456foo","(?<=\\d{3}(?!999)...)foo",[trim]))), + 2}]))), + <<"123abc:">> = iolist_to_binary(join(re:split("123abcfoo","(?<=\\d{3}(?!999)...)foo",[]))), + <<"123456">> = iolist_to_binary(join(re:split("123456foo","(?<=\\d{3}(?!999)...)foo",[trim]))), <<"123456:">> = iolist_to_binary(join(re:split("123456foo","(?<=\\d{3}(?!999)...)foo",[{parts, - 2}]))), - <<"123456:">> = iolist_to_binary(join(re:split("123456foo","(?<=\\d{3}(?!999)...)foo",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=\\d{3}(?!999)...)foo",[trim]))), + 2}]))), + <<"123456:">> = iolist_to_binary(join(re:split("123456foo","(?<=\\d{3}(?!999)...)foo",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=\\d{3}(?!999)...)foo",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=\\d{3}(?!999)...)foo",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=\\d{3}(?!999)...)foo",[]))), - <<"123999foo">> = iolist_to_binary(join(re:split("123999foo","(?<=\\d{3}(?!999)...)foo",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=\\d{3}(?!999)...)foo",[]))), + <<"123999foo">> = iolist_to_binary(join(re:split("123999foo","(?<=\\d{3}(?!999)...)foo",[trim]))), <<"123999foo">> = iolist_to_binary(join(re:split("123999foo","(?<=\\d{3}(?!999)...)foo",[{parts, - 2}]))), - <<"123999foo">> = iolist_to_binary(join(re:split("123999foo","(?<=\\d{3}(?!999)...)foo",[]))), + 2}]))), + <<"123999foo">> = iolist_to_binary(join(re:split("123999foo","(?<=\\d{3}(?!999)...)foo",[]))), ok. run32() -> - <<"123abc">> = iolist_to_binary(join(re:split("123abcfoo","(?<=\\d{3}...)(?<!999)foo",[trim]))), + <<"123abc">> = iolist_to_binary(join(re:split("123abcfoo","(?<=\\d{3}...)(?<!999)foo",[trim]))), <<"123abc:">> = iolist_to_binary(join(re:split("123abcfoo","(?<=\\d{3}...)(?<!999)foo",[{parts, - 2}]))), - <<"123abc:">> = iolist_to_binary(join(re:split("123abcfoo","(?<=\\d{3}...)(?<!999)foo",[]))), - <<"123456">> = iolist_to_binary(join(re:split("123456foo","(?<=\\d{3}...)(?<!999)foo",[trim]))), + 2}]))), + <<"123abc:">> = iolist_to_binary(join(re:split("123abcfoo","(?<=\\d{3}...)(?<!999)foo",[]))), + <<"123456">> = iolist_to_binary(join(re:split("123456foo","(?<=\\d{3}...)(?<!999)foo",[trim]))), <<"123456:">> = iolist_to_binary(join(re:split("123456foo","(?<=\\d{3}...)(?<!999)foo",[{parts, - 2}]))), - <<"123456:">> = iolist_to_binary(join(re:split("123456foo","(?<=\\d{3}...)(?<!999)foo",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=\\d{3}...)(?<!999)foo",[trim]))), + 2}]))), + <<"123456:">> = iolist_to_binary(join(re:split("123456foo","(?<=\\d{3}...)(?<!999)foo",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=\\d{3}...)(?<!999)foo",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=\\d{3}...)(?<!999)foo",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=\\d{3}...)(?<!999)foo",[]))), - <<"123999foo">> = iolist_to_binary(join(re:split("123999foo","(?<=\\d{3}...)(?<!999)foo",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=\\d{3}...)(?<!999)foo",[]))), + <<"123999foo">> = iolist_to_binary(join(re:split("123999foo","(?<=\\d{3}...)(?<!999)foo",[trim]))), <<"123999foo">> = iolist_to_binary(join(re:split("123999foo","(?<=\\d{3}...)(?<!999)foo",[{parts, - 2}]))), - <<"123999foo">> = iolist_to_binary(join(re:split("123999foo","(?<=\\d{3}...)(?<!999)foo",[]))), + 2}]))), + <<"123999foo">> = iolist_to_binary(join(re:split("123999foo","(?<=\\d{3}...)(?<!999)foo",[]))), <<":::abcd: xyz">> = iolist_to_binary(join(re:split("<a href=abcd xyz","<a[\\s]+href[\\s]*=[\\s]* # find <a href= ([\\\"\\'])? # find single or double quote (?(1) (.*?)\\1 | ([^\\s]+)) # if quote found, match up to next matching # quote, otherwise match up to next space",[caseless, dotall, extended, - trim]))), + trim]))), <<":::abcd: xyz">> = iolist_to_binary(join(re:split("<a href=abcd xyz","<a[\\s]+href[\\s]*=[\\s]* # find <a href= ([\\\"\\'])? # find single or double quote (?(1) (.*?)\\1 | ([^\\s]+)) # if quote found, match up to next matching @@ -28108,20 +28108,20 @@ run32() -> dotall, extended, {parts, - 2}]))), + 2}]))), <<":::abcd: xyz">> = iolist_to_binary(join(re:split("<a href=abcd xyz","<a[\\s]+href[\\s]*=[\\s]* # find <a href= ([\\\"\\'])? # find single or double quote (?(1) (.*?)\\1 | ([^\\s]+)) # if quote found, match up to next matching # quote, otherwise match up to next space",[caseless, dotall, - extended]))), + extended]))), <<":\":abcd xyz pqr:: cats">> = iolist_to_binary(join(re:split("<a href=\"abcd xyz pqr\" cats","<a[\\s]+href[\\s]*=[\\s]* # find <a href= ([\\\"\\'])? # find single or double quote (?(1) (.*?)\\1 | ([^\\s]+)) # if quote found, match up to next matching # quote, otherwise match up to next space",[caseless, dotall, extended, - trim]))), + trim]))), <<":\":abcd xyz pqr:: cats">> = iolist_to_binary(join(re:split("<a href=\"abcd xyz pqr\" cats","<a[\\s]+href[\\s]*=[\\s]* # find <a href= ([\\\"\\'])? # find single or double quote (?(1) (.*?)\\1 | ([^\\s]+)) # if quote found, match up to next matching @@ -28129,20 +28129,20 @@ run32() -> dotall, extended, {parts, - 2}]))), + 2}]))), <<":\":abcd xyz pqr:: cats">> = iolist_to_binary(join(re:split("<a href=\"abcd xyz pqr\" cats","<a[\\s]+href[\\s]*=[\\s]* # find <a href= ([\\\"\\'])? # find single or double quote (?(1) (.*?)\\1 | ([^\\s]+)) # if quote found, match up to next matching # quote, otherwise match up to next space",[caseless, dotall, - extended]))), + extended]))), <<":':abcd xyz pqr:: cats">> = iolist_to_binary(join(re:split("<a href='abcd xyz pqr' cats","<a[\\s]+href[\\s]*=[\\s]* # find <a href= ([\\\"\\'])? # find single or double quote (?(1) (.*?)\\1 | ([^\\s]+)) # if quote found, match up to next matching # quote, otherwise match up to next space",[caseless, dotall, extended, - trim]))), + trim]))), <<":':abcd xyz pqr:: cats">> = iolist_to_binary(join(re:split("<a href='abcd xyz pqr' cats","<a[\\s]+href[\\s]*=[\\s]* # find <a href= ([\\\"\\'])? # find single or double quote (?(1) (.*?)\\1 | ([^\\s]+)) # if quote found, match up to next matching @@ -28150,20 +28150,20 @@ run32() -> dotall, extended, {parts, - 2}]))), + 2}]))), <<":':abcd xyz pqr:: cats">> = iolist_to_binary(join(re:split("<a href='abcd xyz pqr' cats","<a[\\s]+href[\\s]*=[\\s]* # find <a href= ([\\\"\\'])? # find single or double quote (?(1) (.*?)\\1 | ([^\\s]+)) # if quote found, match up to next matching # quote, otherwise match up to next space",[caseless, dotall, - extended]))), + extended]))), <<":::abcd: xyz">> = iolist_to_binary(join(re:split("<a href=abcd xyz","<a\\s+href\\s*=\\s* # find <a href= ([\"'])? # find single or double quote (?(1) (.*?)\\1 | (\\S+)) # if quote found, match up to next matching # quote, otherwise match up to next space",[caseless, dotall, extended, - trim]))), + trim]))), <<":::abcd: xyz">> = iolist_to_binary(join(re:split("<a href=abcd xyz","<a\\s+href\\s*=\\s* # find <a href= ([\"'])? # find single or double quote (?(1) (.*?)\\1 | (\\S+)) # if quote found, match up to next matching @@ -28171,20 +28171,20 @@ run32() -> dotall, extended, {parts, - 2}]))), + 2}]))), <<":::abcd: xyz">> = iolist_to_binary(join(re:split("<a href=abcd xyz","<a\\s+href\\s*=\\s* # find <a href= ([\"'])? # find single or double quote (?(1) (.*?)\\1 | (\\S+)) # if quote found, match up to next matching # quote, otherwise match up to next space",[caseless, dotall, - extended]))), + extended]))), <<":\":abcd xyz pqr:: cats">> = iolist_to_binary(join(re:split("<a href=\"abcd xyz pqr\" cats","<a\\s+href\\s*=\\s* # find <a href= ([\"'])? # find single or double quote (?(1) (.*?)\\1 | (\\S+)) # if quote found, match up to next matching # quote, otherwise match up to next space",[caseless, dotall, extended, - trim]))), + trim]))), <<":\":abcd xyz pqr:: cats">> = iolist_to_binary(join(re:split("<a href=\"abcd xyz pqr\" cats","<a\\s+href\\s*=\\s* # find <a href= ([\"'])? # find single or double quote (?(1) (.*?)\\1 | (\\S+)) # if quote found, match up to next matching @@ -28192,20 +28192,20 @@ run32() -> dotall, extended, {parts, - 2}]))), + 2}]))), <<":\":abcd xyz pqr:: cats">> = iolist_to_binary(join(re:split("<a href=\"abcd xyz pqr\" cats","<a\\s+href\\s*=\\s* # find <a href= ([\"'])? # find single or double quote (?(1) (.*?)\\1 | (\\S+)) # if quote found, match up to next matching # quote, otherwise match up to next space",[caseless, dotall, - extended]))), + extended]))), <<":':abcd xyz pqr:: cats">> = iolist_to_binary(join(re:split("<a href = 'abcd xyz pqr' cats","<a\\s+href\\s*=\\s* # find <a href= ([\"'])? # find single or double quote (?(1) (.*?)\\1 | (\\S+)) # if quote found, match up to next matching # quote, otherwise match up to next space",[caseless, dotall, extended, - trim]))), + trim]))), <<":':abcd xyz pqr:: cats">> = iolist_to_binary(join(re:split("<a href = 'abcd xyz pqr' cats","<a\\s+href\\s*=\\s* # find <a href= ([\"'])? # find single or double quote (?(1) (.*?)\\1 | (\\S+)) # if quote found, match up to next matching @@ -28213,20 +28213,20 @@ run32() -> dotall, extended, {parts, - 2}]))), + 2}]))), <<":':abcd xyz pqr:: cats">> = iolist_to_binary(join(re:split("<a href = 'abcd xyz pqr' cats","<a\\s+href\\s*=\\s* # find <a href= ([\"'])? # find single or double quote (?(1) (.*?)\\1 | (\\S+)) # if quote found, match up to next matching # quote, otherwise match up to next space",[caseless, dotall, - extended]))), + extended]))), <<":::abcd: xyz">> = iolist_to_binary(join(re:split("<a href=abcd xyz","<a\\s+href(?>\\s*)=(?>\\s*) # find <a href= ([\"'])? # find single or double quote (?(1) (.*?)\\1 | (\\S+)) # if quote found, match up to next matching # quote, otherwise match up to next space",[caseless, dotall, extended, - trim]))), + trim]))), <<":::abcd: xyz">> = iolist_to_binary(join(re:split("<a href=abcd xyz","<a\\s+href(?>\\s*)=(?>\\s*) # find <a href= ([\"'])? # find single or double quote (?(1) (.*?)\\1 | (\\S+)) # if quote found, match up to next matching @@ -28234,20 +28234,20 @@ run32() -> dotall, extended, {parts, - 2}]))), + 2}]))), <<":::abcd: xyz">> = iolist_to_binary(join(re:split("<a href=abcd xyz","<a\\s+href(?>\\s*)=(?>\\s*) # find <a href= ([\"'])? # find single or double quote (?(1) (.*?)\\1 | (\\S+)) # if quote found, match up to next matching # quote, otherwise match up to next space",[caseless, dotall, - extended]))), + extended]))), <<":\":abcd xyz pqr:: cats">> = iolist_to_binary(join(re:split("<a href=\"abcd xyz pqr\" cats","<a\\s+href(?>\\s*)=(?>\\s*) # find <a href= ([\"'])? # find single or double quote (?(1) (.*?)\\1 | (\\S+)) # if quote found, match up to next matching # quote, otherwise match up to next space",[caseless, dotall, extended, - trim]))), + trim]))), <<":\":abcd xyz pqr:: cats">> = iolist_to_binary(join(re:split("<a href=\"abcd xyz pqr\" cats","<a\\s+href(?>\\s*)=(?>\\s*) # find <a href= ([\"'])? # find single or double quote (?(1) (.*?)\\1 | (\\S+)) # if quote found, match up to next matching @@ -28255,20 +28255,20 @@ run32() -> dotall, extended, {parts, - 2}]))), + 2}]))), <<":\":abcd xyz pqr:: cats">> = iolist_to_binary(join(re:split("<a href=\"abcd xyz pqr\" cats","<a\\s+href(?>\\s*)=(?>\\s*) # find <a href= ([\"'])? # find single or double quote (?(1) (.*?)\\1 | (\\S+)) # if quote found, match up to next matching # quote, otherwise match up to next space",[caseless, dotall, - extended]))), + extended]))), <<":':abcd xyz pqr:: cats">> = iolist_to_binary(join(re:split("<a href = 'abcd xyz pqr' cats","<a\\s+href(?>\\s*)=(?>\\s*) # find <a href= ([\"'])? # find single or double quote (?(1) (.*?)\\1 | (\\S+)) # if quote found, match up to next matching # quote, otherwise match up to next space",[caseless, dotall, extended, - trim]))), + trim]))), <<":':abcd xyz pqr:: cats">> = iolist_to_binary(join(re:split("<a href = 'abcd xyz pqr' cats","<a\\s+href(?>\\s*)=(?>\\s*) # find <a href= ([\"'])? # find single or double quote (?(1) (.*?)\\1 | (\\S+)) # if quote found, match up to next matching @@ -28276,303 +28276,303 @@ run32() -> dotall, extended, {parts, - 2}]))), + 2}]))), <<":':abcd xyz pqr:: cats">> = iolist_to_binary(join(re:split("<a href = 'abcd xyz pqr' cats","<a\\s+href(?>\\s*)=(?>\\s*) # find <a href= ([\"'])? # find single or double quote (?(1) (.*?)\\1 | (\\S+)) # if quote found, match up to next matching # quote, otherwise match up to next space",[caseless, dotall, - extended]))), - <<":A:Z:B:::C:::D:::E:::F:::G">> = iolist_to_binary(join(re:split("ZABCDEFG","((Z)+|A)*",[trim]))), + extended]))), + <<":A:Z:B:::C:::D:::E:::F:::G">> = iolist_to_binary(join(re:split("ZABCDEFG","((Z)+|A)*",[trim]))), <<":A:Z:BCDEFG">> = iolist_to_binary(join(re:split("ZABCDEFG","((Z)+|A)*",[{parts, - 2}]))), - <<":A:Z:B:::C:::D:::E:::F:::G:::">> = iolist_to_binary(join(re:split("ZABCDEFG","((Z)+|A)*",[]))), - <<":A::B:::C:::D:::E:::F:::G">> = iolist_to_binary(join(re:split("ZABCDEFG","(Z()|A)*",[trim]))), + 2}]))), + <<":A:Z:B:::C:::D:::E:::F:::G:::">> = iolist_to_binary(join(re:split("ZABCDEFG","((Z)+|A)*",[]))), + <<":A::B:::C:::D:::E:::F:::G">> = iolist_to_binary(join(re:split("ZABCDEFG","(Z()|A)*",[trim]))), <<":A::BCDEFG">> = iolist_to_binary(join(re:split("ZABCDEFG","(Z()|A)*",[{parts, - 2}]))), - <<":A::B:::C:::D:::E:::F:::G:::">> = iolist_to_binary(join(re:split("ZABCDEFG","(Z()|A)*",[]))), - <<":A:::B::::C::::D::::E::::F::::G">> = iolist_to_binary(join(re:split("ZABCDEFG","(Z(())|A)*",[trim]))), + 2}]))), + <<":A::B:::C:::D:::E:::F:::G:::">> = iolist_to_binary(join(re:split("ZABCDEFG","(Z()|A)*",[]))), + <<":A:::B::::C::::D::::E::::F::::G">> = iolist_to_binary(join(re:split("ZABCDEFG","(Z(())|A)*",[trim]))), <<":A:::BCDEFG">> = iolist_to_binary(join(re:split("ZABCDEFG","(Z(())|A)*",[{parts, - 2}]))), - <<":A:::B::::C::::D::::E::::F::::G::::">> = iolist_to_binary(join(re:split("ZABCDEFG","(Z(())|A)*",[]))), - <<":A:B::C::D::E::F::G">> = iolist_to_binary(join(re:split("ZABCDEFG","((?>Z)+|A)*",[trim]))), + 2}]))), + <<":A:::B::::C::::D::::E::::F::::G::::">> = iolist_to_binary(join(re:split("ZABCDEFG","(Z(())|A)*",[]))), + <<":A:B::C::D::E::F::G">> = iolist_to_binary(join(re:split("ZABCDEFG","((?>Z)+|A)*",[trim]))), <<":A:BCDEFG">> = iolist_to_binary(join(re:split("ZABCDEFG","((?>Z)+|A)*",[{parts, - 2}]))), - <<":A:B::C::D::E::F::G::">> = iolist_to_binary(join(re:split("ZABCDEFG","((?>Z)+|A)*",[]))), - <<"Z::::B::C::D::E::F::G">> = iolist_to_binary(join(re:split("ZABCDEFG","((?>)+|A)*",[trim]))), + 2}]))), + <<":A:B::C::D::E::F::G::">> = iolist_to_binary(join(re:split("ZABCDEFG","((?>Z)+|A)*",[]))), + <<"Z::::B::C::D::E::F::G">> = iolist_to_binary(join(re:split("ZABCDEFG","((?>)+|A)*",[trim]))), <<"Z::ABCDEFG">> = iolist_to_binary(join(re:split("ZABCDEFG","((?>)+|A)*",[{parts, - 2}]))), - <<"Z::::B::C::D::E::F::G::">> = iolist_to_binary(join(re:split("ZABCDEFG","((?>)+|A)*",[]))), - <<":b:b:b">> = iolist_to_binary(join(re:split("abbab","a*",[trim]))), + 2}]))), + <<"Z::::B::C::D::E::F::G::">> = iolist_to_binary(join(re:split("ZABCDEFG","((?>)+|A)*",[]))), + <<":b:b:b">> = iolist_to_binary(join(re:split("abbab","a*",[trim]))), <<":bbab">> = iolist_to_binary(join(re:split("abbab","a*",[{parts, - 2}]))), - <<":b:b:b:">> = iolist_to_binary(join(re:split("abbab","a*",[]))), - <<":bcde">> = iolist_to_binary(join(re:split("abcde","^[\\d-a]",[trim]))), + 2}]))), + <<":b:b:b:">> = iolist_to_binary(join(re:split("abbab","a*",[]))), + <<":bcde">> = iolist_to_binary(join(re:split("abcde","^[\\d-a]",[trim]))), <<":bcde">> = iolist_to_binary(join(re:split("abcde","^[\\d-a]",[{parts, - 2}]))), - <<":bcde">> = iolist_to_binary(join(re:split("abcde","^[\\d-a]",[]))), - <<":things">> = iolist_to_binary(join(re:split("-things","^[\\d-a]",[trim]))), + 2}]))), + <<":bcde">> = iolist_to_binary(join(re:split("abcde","^[\\d-a]",[]))), + <<":things">> = iolist_to_binary(join(re:split("-things","^[\\d-a]",[trim]))), <<":things">> = iolist_to_binary(join(re:split("-things","^[\\d-a]",[{parts, - 2}]))), - <<":things">> = iolist_to_binary(join(re:split("-things","^[\\d-a]",[]))), - <<":digit">> = iolist_to_binary(join(re:split("0digit","^[\\d-a]",[trim]))), + 2}]))), + <<":things">> = iolist_to_binary(join(re:split("-things","^[\\d-a]",[]))), + <<":digit">> = iolist_to_binary(join(re:split("0digit","^[\\d-a]",[trim]))), <<":digit">> = iolist_to_binary(join(re:split("0digit","^[\\d-a]",[{parts, - 2}]))), - <<":digit">> = iolist_to_binary(join(re:split("0digit","^[\\d-a]",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[\\d-a]",[trim]))), + 2}]))), + <<":digit">> = iolist_to_binary(join(re:split("0digit","^[\\d-a]",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[\\d-a]",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[\\d-a]",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[\\d-a]",[]))), - <<"bcdef">> = iolist_to_binary(join(re:split("bcdef","^[\\d-a]",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[\\d-a]",[]))), + <<"bcdef">> = iolist_to_binary(join(re:split("bcdef","^[\\d-a]",[trim]))), <<"bcdef">> = iolist_to_binary(join(re:split("bcdef","^[\\d-a]",[{parts, - 2}]))), - <<"bcdef">> = iolist_to_binary(join(re:split("bcdef","^[\\d-a]",[]))), + 2}]))), + <<"bcdef">> = iolist_to_binary(join(re:split("bcdef","^[\\d-a]",[]))), <<">:<">> = iolist_to_binary(join(re:split("> -
<","[[:space:]]+",[trim]))), +
<","[[:space:]]+",[trim]))), <<">:<">> = iolist_to_binary(join(re:split("> -
<","[[:space:]]+",[{parts,2}]))), +
<","[[:space:]]+",[{parts,2}]))), <<">:<">> = iolist_to_binary(join(re:split("> -
<","[[:space:]]+",[]))), +
<","[[:space:]]+",[]))), <<">:
<">> = iolist_to_binary(join(re:split("> -
<","[[:blank:]]+",[trim]))), +
<","[[:blank:]]+",[trim]))), <<">:
<">> = iolist_to_binary(join(re:split("> -
<","[[:blank:]]+",[{parts,2}]))), +
<","[[:blank:]]+",[{parts,2}]))), <<">:
<">> = iolist_to_binary(join(re:split("> -
<","[[:blank:]]+",[]))), +
<","[[:blank:]]+",[]))), <<">:<">> = iolist_to_binary(join(re:split("> -
<","[\\s]+",[trim]))), +
<","[\\s]+",[trim]))), <<">:<">> = iolist_to_binary(join(re:split("> -
<","[\\s]+",[{parts,2}]))), +
<","[\\s]+",[{parts,2}]))), <<">:<">> = iolist_to_binary(join(re:split("> -
<","[\\s]+",[]))), +
<","[\\s]+",[]))), <<">:<">> = iolist_to_binary(join(re:split("> -
<","\\s+",[trim]))), +
<","\\s+",[trim]))), <<">:<">> = iolist_to_binary(join(re:split("> -
<","\\s+",[{parts,2}]))), +
<","\\s+",[{parts,2}]))), <<">:<">> = iolist_to_binary(join(re:split("> -
<","\\s+",[]))), +
<","\\s+",[]))), <<"">> = iolist_to_binary(join(re:split("ab","ab",[extended, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ab","ab",[extended, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ab","ab",[extended]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ab","ab",[extended]))), <<"a :b">> = iolist_to_binary(join(re:split("a -xb","(?!\\A)x",[multiline,trim]))), +xb","(?!\\A)x",[multiline,trim]))), <<"a :b">> = iolist_to_binary(join(re:split("a -xb","(?!\\A)x",[multiline,{parts,2}]))), +xb","(?!\\A)x",[multiline,{parts,2}]))), <<"a :b">> = iolist_to_binary(join(re:split("a -xb","(?!\\A)x",[multiline]))), +xb","(?!\\A)x",[multiline]))), <<"a xb">> = iolist_to_binary(join(re:split("a -xb","(?!^)x",[multiline,trim]))), +xb","(?!^)x",[multiline,trim]))), <<"a xb">> = iolist_to_binary(join(re:split("a -xb","(?!^)x",[multiline,{parts,2}]))), +xb","(?!^)x",[multiline,{parts,2}]))), <<"a xb">> = iolist_to_binary(join(re:split("a -xb","(?!^)x",[multiline]))), - <<"">> = iolist_to_binary(join(re:split("abcabcabc","abc\\Qabc\\Eabc",[trim]))), +xb","(?!^)x",[multiline]))), + <<"">> = iolist_to_binary(join(re:split("abcabcabc","abc\\Qabc\\Eabc",[trim]))), <<":">> = iolist_to_binary(join(re:split("abcabcabc","abc\\Qabc\\Eabc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abcabcabc","abc\\Qabc\\Eabc",[]))), - <<"">> = iolist_to_binary(join(re:split("abc(*+|abc","abc\\Q(*+|\\Eabc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abcabcabc","abc\\Qabc\\Eabc",[]))), + <<"">> = iolist_to_binary(join(re:split("abc(*+|abc","abc\\Q(*+|\\Eabc",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc(*+|abc","abc\\Q(*+|\\Eabc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc(*+|abc","abc\\Q(*+|\\Eabc",[]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc(*+|abc","abc\\Q(*+|\\Eabc",[]))), ok. run33() -> <<"">> = iolist_to_binary(join(re:split("abc abcabc"," abc\\Q abc\\Eabc",[extended, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("abc abcabc"," abc\\Q abc\\Eabc",[extended, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc abcabc"," abc\\Q abc\\Eabc",[extended]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc abcabc"," abc\\Q abc\\Eabc",[extended]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers"," abc\\Q abc\\Eabc",[extended, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers"," abc\\Q abc\\Eabc",[extended, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers"," abc\\Q abc\\Eabc",[extended]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers"," abc\\Q abc\\Eabc",[extended]))), <<"abcabcabc">> = iolist_to_binary(join(re:split("abcabcabc"," abc\\Q abc\\Eabc",[extended, - trim]))), + trim]))), <<"abcabcabc">> = iolist_to_binary(join(re:split("abcabcabc"," abc\\Q abc\\Eabc",[extended, {parts, - 2}]))), - <<"abcabcabc">> = iolist_to_binary(join(re:split("abcabcabc"," abc\\Q abc\\Eabc",[extended]))), + 2}]))), + <<"abcabcabc">> = iolist_to_binary(join(re:split("abcabcabc"," abc\\Q abc\\Eabc",[extended]))), <<"">> = iolist_to_binary(join(re:split("abc#not comment literal","abc#comment \\Q#not comment - literal\\E",[extended,trim]))), + literal\\E",[extended,trim]))), <<":">> = iolist_to_binary(join(re:split("abc#not comment literal","abc#comment \\Q#not comment - literal\\E",[extended,{parts,2}]))), + literal\\E",[extended,{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("abc#not comment literal","abc#comment \\Q#not comment - literal\\E",[extended]))), + literal\\E",[extended]))), <<"">> = iolist_to_binary(join(re:split("abc#not comment literal","abc#comment \\Q#not comment - literal",[extended,trim]))), + literal",[extended,trim]))), <<":">> = iolist_to_binary(join(re:split("abc#not comment literal","abc#comment \\Q#not comment - literal",[extended,{parts,2}]))), + literal",[extended,{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("abc#not comment literal","abc#comment \\Q#not comment - literal",[extended]))), + literal",[extended]))), <<"">> = iolist_to_binary(join(re:split("abc#not comment literal","abc#comment \\Q#not comment literal\\E #more comment - ",[extended,trim]))), + ",[extended,trim]))), <<":">> = iolist_to_binary(join(re:split("abc#not comment literal","abc#comment \\Q#not comment literal\\E #more comment - ",[extended,{parts,2}]))), + ",[extended,{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("abc#not comment literal","abc#comment \\Q#not comment literal\\E #more comment - ",[extended]))), + ",[extended]))), <<"">> = iolist_to_binary(join(re:split("abc#not comment literal","abc#comment \\Q#not comment - literal\\E #more comment",[extended,trim]))), + literal\\E #more comment",[extended,trim]))), <<":">> = iolist_to_binary(join(re:split("abc#not comment literal","abc#comment \\Q#not comment - literal\\E #more comment",[extended,{parts,2}]))), + literal\\E #more comment",[extended,{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("abc#not comment literal","abc#comment \\Q#not comment - literal\\E #more comment",[extended]))), - <<"">> = iolist_to_binary(join(re:split("abc\\$xyz","\\Qabc\\$xyz\\E",[trim]))), + literal\\E #more comment",[extended]))), + <<"">> = iolist_to_binary(join(re:split("abc\\$xyz","\\Qabc\\$xyz\\E",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc\\$xyz","\\Qabc\\$xyz\\E",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc\\$xyz","\\Qabc\\$xyz\\E",[]))), - <<"">> = iolist_to_binary(join(re:split("abc$xyz","\\Qabc\\E\\$\\Qxyz\\E",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc\\$xyz","\\Qabc\\$xyz\\E",[]))), + <<"">> = iolist_to_binary(join(re:split("abc$xyz","\\Qabc\\E\\$\\Qxyz\\E",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc$xyz","\\Qabc\\E\\$\\Qxyz\\E",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc$xyz","\\Qabc\\E\\$\\Qxyz\\E",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","\\Aabc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc$xyz","\\Qabc\\E\\$\\Qxyz\\E",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","\\Aabc",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","\\Aabc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","\\Aabc",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\Aabc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","\\Aabc",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\Aabc",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\Aabc",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\Aabc",[]))), - <<"xyzabc">> = iolist_to_binary(join(re:split("xyzabc","\\Aabc",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\Aabc",[]))), + <<"xyzabc">> = iolist_to_binary(join(re:split("xyzabc","\\Aabc",[trim]))), <<"xyzabc">> = iolist_to_binary(join(re:split("xyzabc","\\Aabc",[{parts, - 2}]))), - <<"xyzabc">> = iolist_to_binary(join(re:split("xyzabc","\\Aabc",[]))), - <<":abc2xyzabc3">> = iolist_to_binary(join(re:split("abc1abc2xyzabc3","\\Aabc.",[trim]))), + 2}]))), + <<"xyzabc">> = iolist_to_binary(join(re:split("xyzabc","\\Aabc",[]))), + <<":abc2xyzabc3">> = iolist_to_binary(join(re:split("abc1abc2xyzabc3","\\Aabc.",[trim]))), <<":abc2xyzabc3">> = iolist_to_binary(join(re:split("abc1abc2xyzabc3","\\Aabc.",[{parts, - 2}]))), - <<":abc2xyzabc3">> = iolist_to_binary(join(re:split("abc1abc2xyzabc3","\\Aabc.",[]))), - <<"::xyz">> = iolist_to_binary(join(re:split("abc1abc2xyzabc3","abc.",[trim]))), + 2}]))), + <<":abc2xyzabc3">> = iolist_to_binary(join(re:split("abc1abc2xyzabc3","\\Aabc.",[]))), + <<"::xyz">> = iolist_to_binary(join(re:split("abc1abc2xyzabc3","abc.",[trim]))), <<":abc2xyzabc3">> = iolist_to_binary(join(re:split("abc1abc2xyzabc3","abc.",[{parts, - 2}]))), - <<"::xyz:">> = iolist_to_binary(join(re:split("abc1abc2xyzabc3","abc.",[]))), - <<"X:Y">> = iolist_to_binary(join(re:split("XabcdY","a(?x: b c )d",[trim]))), + 2}]))), + <<"::xyz:">> = iolist_to_binary(join(re:split("abc1abc2xyzabc3","abc.",[]))), + <<"X:Y">> = iolist_to_binary(join(re:split("XabcdY","a(?x: b c )d",[trim]))), <<"X:Y">> = iolist_to_binary(join(re:split("XabcdY","a(?x: b c )d",[{parts, - 2}]))), - <<"X:Y">> = iolist_to_binary(join(re:split("XabcdY","a(?x: b c )d",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?x: b c )d",[trim]))), + 2}]))), + <<"X:Y">> = iolist_to_binary(join(re:split("XabcdY","a(?x: b c )d",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?x: b c )d",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?x: b c )d",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?x: b c )d",[]))), - <<"Xa b c d Y">> = iolist_to_binary(join(re:split("Xa b c d Y","a(?x: b c )d",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?x: b c )d",[]))), + <<"Xa b c d Y">> = iolist_to_binary(join(re:split("Xa b c d Y","a(?x: b c )d",[trim]))), <<"Xa b c d Y">> = iolist_to_binary(join(re:split("Xa b c d Y","a(?x: b c )d",[{parts, - 2}]))), - <<"Xa b c d Y">> = iolist_to_binary(join(re:split("Xa b c d Y","a(?x: b c )d",[]))), - <<"X:abc:Y">> = iolist_to_binary(join(re:split("XabcY","((?x)x y z | a b c)",[trim]))), + 2}]))), + <<"Xa b c d Y">> = iolist_to_binary(join(re:split("Xa b c d Y","a(?x: b c )d",[]))), + <<"X:abc:Y">> = iolist_to_binary(join(re:split("XabcY","((?x)x y z | a b c)",[trim]))), <<"X:abc:Y">> = iolist_to_binary(join(re:split("XabcY","((?x)x y z | a b c)",[{parts, - 2}]))), - <<"X:abc:Y">> = iolist_to_binary(join(re:split("XabcY","((?x)x y z | a b c)",[]))), - <<"A:xyz:B">> = iolist_to_binary(join(re:split("AxyzB","((?x)x y z | a b c)",[trim]))), + 2}]))), + <<"X:abc:Y">> = iolist_to_binary(join(re:split("XabcY","((?x)x y z | a b c)",[]))), + <<"A:xyz:B">> = iolist_to_binary(join(re:split("AxyzB","((?x)x y z | a b c)",[trim]))), <<"A:xyz:B">> = iolist_to_binary(join(re:split("AxyzB","((?x)x y z | a b c)",[{parts, - 2}]))), - <<"A:xyz:B">> = iolist_to_binary(join(re:split("AxyzB","((?x)x y z | a b c)",[]))), - <<"X:Y">> = iolist_to_binary(join(re:split("XabCY","(?i)AB(?-i)C",[trim]))), + 2}]))), + <<"A:xyz:B">> = iolist_to_binary(join(re:split("AxyzB","((?x)x y z | a b c)",[]))), + <<"X:Y">> = iolist_to_binary(join(re:split("XabCY","(?i)AB(?-i)C",[trim]))), <<"X:Y">> = iolist_to_binary(join(re:split("XabCY","(?i)AB(?-i)C",[{parts, - 2}]))), - <<"X:Y">> = iolist_to_binary(join(re:split("XabCY","(?i)AB(?-i)C",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?i)AB(?-i)C",[trim]))), + 2}]))), + <<"X:Y">> = iolist_to_binary(join(re:split("XabCY","(?i)AB(?-i)C",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?i)AB(?-i)C",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?i)AB(?-i)C",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?i)AB(?-i)C",[]))), - <<"XabcY">> = iolist_to_binary(join(re:split("XabcY","(?i)AB(?-i)C",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?i)AB(?-i)C",[]))), + <<"XabcY">> = iolist_to_binary(join(re:split("XabcY","(?i)AB(?-i)C",[trim]))), <<"XabcY">> = iolist_to_binary(join(re:split("XabcY","(?i)AB(?-i)C",[{parts, - 2}]))), - <<"XabcY">> = iolist_to_binary(join(re:split("XabcY","(?i)AB(?-i)C",[]))), - <<":abC">> = iolist_to_binary(join(re:split("abCE","((?i)AB(?-i)C|D)E",[trim]))), + 2}]))), + <<"XabcY">> = iolist_to_binary(join(re:split("XabcY","(?i)AB(?-i)C",[]))), + <<":abC">> = iolist_to_binary(join(re:split("abCE","((?i)AB(?-i)C|D)E",[trim]))), <<":abC:">> = iolist_to_binary(join(re:split("abCE","((?i)AB(?-i)C|D)E",[{parts, - 2}]))), - <<":abC:">> = iolist_to_binary(join(re:split("abCE","((?i)AB(?-i)C|D)E",[]))), - <<":D">> = iolist_to_binary(join(re:split("DE","((?i)AB(?-i)C|D)E",[trim]))), + 2}]))), + <<":abC:">> = iolist_to_binary(join(re:split("abCE","((?i)AB(?-i)C|D)E",[]))), + <<":D">> = iolist_to_binary(join(re:split("DE","((?i)AB(?-i)C|D)E",[trim]))), <<":D:">> = iolist_to_binary(join(re:split("DE","((?i)AB(?-i)C|D)E",[{parts, - 2}]))), - <<":D:">> = iolist_to_binary(join(re:split("DE","((?i)AB(?-i)C|D)E",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","((?i)AB(?-i)C|D)E",[trim]))), + 2}]))), + <<":D:">> = iolist_to_binary(join(re:split("DE","((?i)AB(?-i)C|D)E",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","((?i)AB(?-i)C|D)E",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","((?i)AB(?-i)C|D)E",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","((?i)AB(?-i)C|D)E",[]))), - <<"abcE">> = iolist_to_binary(join(re:split("abcE","((?i)AB(?-i)C|D)E",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","((?i)AB(?-i)C|D)E",[]))), + <<"abcE">> = iolist_to_binary(join(re:split("abcE","((?i)AB(?-i)C|D)E",[trim]))), <<"abcE">> = iolist_to_binary(join(re:split("abcE","((?i)AB(?-i)C|D)E",[{parts, - 2}]))), - <<"abcE">> = iolist_to_binary(join(re:split("abcE","((?i)AB(?-i)C|D)E",[]))), - <<"abCe">> = iolist_to_binary(join(re:split("abCe","((?i)AB(?-i)C|D)E",[trim]))), + 2}]))), + <<"abcE">> = iolist_to_binary(join(re:split("abcE","((?i)AB(?-i)C|D)E",[]))), + <<"abCe">> = iolist_to_binary(join(re:split("abCe","((?i)AB(?-i)C|D)E",[trim]))), <<"abCe">> = iolist_to_binary(join(re:split("abCe","((?i)AB(?-i)C|D)E",[{parts, - 2}]))), - <<"abCe">> = iolist_to_binary(join(re:split("abCe","((?i)AB(?-i)C|D)E",[]))), - <<"dE">> = iolist_to_binary(join(re:split("dE","((?i)AB(?-i)C|D)E",[trim]))), + 2}]))), + <<"abCe">> = iolist_to_binary(join(re:split("abCe","((?i)AB(?-i)C|D)E",[]))), + <<"dE">> = iolist_to_binary(join(re:split("dE","((?i)AB(?-i)C|D)E",[trim]))), <<"dE">> = iolist_to_binary(join(re:split("dE","((?i)AB(?-i)C|D)E",[{parts, - 2}]))), - <<"dE">> = iolist_to_binary(join(re:split("dE","((?i)AB(?-i)C|D)E",[]))), - <<"De">> = iolist_to_binary(join(re:split("De","((?i)AB(?-i)C|D)E",[trim]))), + 2}]))), + <<"dE">> = iolist_to_binary(join(re:split("dE","((?i)AB(?-i)C|D)E",[]))), + <<"De">> = iolist_to_binary(join(re:split("De","((?i)AB(?-i)C|D)E",[trim]))), <<"De">> = iolist_to_binary(join(re:split("De","((?i)AB(?-i)C|D)E",[{parts, - 2}]))), - <<"De">> = iolist_to_binary(join(re:split("De","((?i)AB(?-i)C|D)E",[]))), - <<":abc">> = iolist_to_binary(join(re:split("abc123abc","(.*)\\d+\\1",[trim]))), + 2}]))), + <<"De">> = iolist_to_binary(join(re:split("De","((?i)AB(?-i)C|D)E",[]))), + <<":abc">> = iolist_to_binary(join(re:split("abc123abc","(.*)\\d+\\1",[trim]))), <<":abc:">> = iolist_to_binary(join(re:split("abc123abc","(.*)\\d+\\1",[{parts, - 2}]))), - <<":abc:">> = iolist_to_binary(join(re:split("abc123abc","(.*)\\d+\\1",[]))), - <<"a:bc">> = iolist_to_binary(join(re:split("abc123bc","(.*)\\d+\\1",[trim]))), + 2}]))), + <<":abc:">> = iolist_to_binary(join(re:split("abc123abc","(.*)\\d+\\1",[]))), + <<"a:bc">> = iolist_to_binary(join(re:split("abc123bc","(.*)\\d+\\1",[trim]))), <<"a:bc:">> = iolist_to_binary(join(re:split("abc123bc","(.*)\\d+\\1",[{parts, - 2}]))), - <<"a:bc:">> = iolist_to_binary(join(re:split("abc123bc","(.*)\\d+\\1",[]))), + 2}]))), + <<"a:bc:">> = iolist_to_binary(join(re:split("abc123bc","(.*)\\d+\\1",[]))), <<":abc">> = iolist_to_binary(join(re:split("abc123abc","(.*)\\d+\\1",[dotall, - trim]))), + trim]))), <<":abc:">> = iolist_to_binary(join(re:split("abc123abc","(.*)\\d+\\1",[dotall, {parts, - 2}]))), - <<":abc:">> = iolist_to_binary(join(re:split("abc123abc","(.*)\\d+\\1",[dotall]))), + 2}]))), + <<":abc:">> = iolist_to_binary(join(re:split("abc123abc","(.*)\\d+\\1",[dotall]))), <<"a:bc">> = iolist_to_binary(join(re:split("abc123bc","(.*)\\d+\\1",[dotall, - trim]))), + trim]))), <<"a:bc:">> = iolist_to_binary(join(re:split("abc123bc","(.*)\\d+\\1",[dotall, {parts, - 2}]))), - <<"a:bc:">> = iolist_to_binary(join(re:split("abc123bc","(.*)\\d+\\1",[dotall]))), - <<":abc:abc">> = iolist_to_binary(join(re:split("abc123abc","((.*))\\d+\\1",[trim]))), + 2}]))), + <<"a:bc:">> = iolist_to_binary(join(re:split("abc123bc","(.*)\\d+\\1",[dotall]))), + <<":abc:abc">> = iolist_to_binary(join(re:split("abc123abc","((.*))\\d+\\1",[trim]))), <<":abc:abc:">> = iolist_to_binary(join(re:split("abc123abc","((.*))\\d+\\1",[{parts, - 2}]))), - <<":abc:abc:">> = iolist_to_binary(join(re:split("abc123abc","((.*))\\d+\\1",[]))), - <<"a:bc:bc">> = iolist_to_binary(join(re:split("abc123bc","((.*))\\d+\\1",[trim]))), + 2}]))), + <<":abc:abc:">> = iolist_to_binary(join(re:split("abc123abc","((.*))\\d+\\1",[]))), + <<"a:bc:bc">> = iolist_to_binary(join(re:split("abc123bc","((.*))\\d+\\1",[trim]))), <<"a:bc:bc:">> = iolist_to_binary(join(re:split("abc123bc","((.*))\\d+\\1",[{parts, - 2}]))), - <<"a:bc:bc:">> = iolist_to_binary(join(re:split("abc123bc","((.*))\\d+\\1",[]))), + 2}]))), + <<"a:bc:bc:">> = iolist_to_binary(join(re:split("abc123bc","((.*))\\d+\\1",[]))), <<"">> = iolist_to_binary(join(re:split("a123::a123","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28581,7 +28581,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,trim]))), + ",[extended,caseless,trim]))), <<"::">> = iolist_to_binary(join(re:split("a123::a123","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28590,7 +28590,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,{parts,2}]))), + ",[extended,caseless,{parts,2}]))), <<"::">> = iolist_to_binary(join(re:split("a123::a123","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28599,7 +28599,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless]))), + ",[extended,caseless]))), <<"">> = iolist_to_binary(join(re:split("a123:b342::abcd","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28608,7 +28608,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,trim]))), + ",[extended,caseless,trim]))), <<"::">> = iolist_to_binary(join(re:split("a123:b342::abcd","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28617,7 +28617,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,{parts,2}]))), + ",[extended,caseless,{parts,2}]))), <<"::">> = iolist_to_binary(join(re:split("a123:b342::abcd","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28626,7 +28626,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless]))), + ",[extended,caseless]))), <<"">> = iolist_to_binary(join(re:split("a123:b342::324e:abcd","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28635,7 +28635,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,trim]))), + ",[extended,caseless,trim]))), <<"::">> = iolist_to_binary(join(re:split("a123:b342::324e:abcd","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28644,7 +28644,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,{parts,2}]))), + ",[extended,caseless,{parts,2}]))), <<"::">> = iolist_to_binary(join(re:split("a123:b342::324e:abcd","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28653,7 +28653,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless]))), + ",[extended,caseless]))), <<"">> = iolist_to_binary(join(re:split("a123:ddde:b342::324e:abcd","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28662,7 +28662,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,trim]))), + ",[extended,caseless,trim]))), <<"::">> = iolist_to_binary(join(re:split("a123:ddde:b342::324e:abcd","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28671,7 +28671,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,{parts,2}]))), + ",[extended,caseless,{parts,2}]))), <<"::">> = iolist_to_binary(join(re:split("a123:ddde:b342::324e:abcd","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28680,7 +28680,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless]))), + ",[extended,caseless]))), <<"">> = iolist_to_binary(join(re:split("a123:ddde:b342::324e:dcba:abcd","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28689,7 +28689,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,trim]))), + ",[extended,caseless,trim]))), <<"::">> = iolist_to_binary(join(re:split("a123:ddde:b342::324e:dcba:abcd","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28698,7 +28698,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,{parts,2}]))), + ",[extended,caseless,{parts,2}]))), <<"::">> = iolist_to_binary(join(re:split("a123:ddde:b342::324e:dcba:abcd","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28707,7 +28707,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless]))), + ",[extended,caseless]))), <<"">> = iolist_to_binary(join(re:split("a123:ddde:9999:b342::324e:dcba:abcd","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28716,7 +28716,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,trim]))), + ",[extended,caseless,trim]))), <<"::">> = iolist_to_binary(join(re:split("a123:ddde:9999:b342::324e:dcba:abcd","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28725,7 +28725,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,{parts,2}]))), + ",[extended,caseless,{parts,2}]))), <<"::">> = iolist_to_binary(join(re:split("a123:ddde:9999:b342::324e:dcba:abcd","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28734,7 +28734,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless]))), + ",[extended,caseless]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28743,7 +28743,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,trim]))), + ",[extended,caseless,trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28752,7 +28752,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,{parts,2}]))), + ",[extended,caseless,{parts,2}]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28761,7 +28761,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless]))), + ",[extended,caseless]))), <<"1:2:3:4:5:6:7:8">> = iolist_to_binary(join(re:split("1:2:3:4:5:6:7:8","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28770,7 +28770,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,trim]))), + ",[extended,caseless,trim]))), <<"1:2:3:4:5:6:7:8">> = iolist_to_binary(join(re:split("1:2:3:4:5:6:7:8","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28779,7 +28779,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,{parts,2}]))), + ",[extended,caseless,{parts,2}]))), <<"1:2:3:4:5:6:7:8">> = iolist_to_binary(join(re:split("1:2:3:4:5:6:7:8","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28788,7 +28788,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless]))), + ",[extended,caseless]))), <<"a123:bce:ddde:9999:b342::324e:dcba:abcd">> = iolist_to_binary(join(re:split("a123:bce:ddde:9999:b342::324e:dcba:abcd","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28797,7 +28797,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,trim]))), + ",[extended,caseless,trim]))), <<"a123:bce:ddde:9999:b342::324e:dcba:abcd">> = iolist_to_binary(join(re:split("a123:bce:ddde:9999:b342::324e:dcba:abcd","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28806,7 +28806,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,{parts,2}]))), + ",[extended,caseless,{parts,2}]))), <<"a123:bce:ddde:9999:b342::324e:dcba:abcd">> = iolist_to_binary(join(re:split("a123:bce:ddde:9999:b342::324e:dcba:abcd","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28815,7 +28815,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless]))), + ",[extended,caseless]))), <<"a123::9999:b342::324e:dcba:abcd">> = iolist_to_binary(join(re:split("a123::9999:b342::324e:dcba:abcd","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28824,7 +28824,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,trim]))), + ",[extended,caseless,trim]))), <<"a123::9999:b342::324e:dcba:abcd">> = iolist_to_binary(join(re:split("a123::9999:b342::324e:dcba:abcd","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28833,7 +28833,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,{parts,2}]))), + ",[extended,caseless,{parts,2}]))), <<"a123::9999:b342::324e:dcba:abcd">> = iolist_to_binary(join(re:split("a123::9999:b342::324e:dcba:abcd","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28842,7 +28842,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless]))), + ",[extended,caseless]))), <<"abcde:2:3:4:5:6:7:8">> = iolist_to_binary(join(re:split("abcde:2:3:4:5:6:7:8","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28851,7 +28851,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,trim]))), + ",[extended,caseless,trim]))), <<"abcde:2:3:4:5:6:7:8">> = iolist_to_binary(join(re:split("abcde:2:3:4:5:6:7:8","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28860,7 +28860,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,{parts,2}]))), + ",[extended,caseless,{parts,2}]))), <<"abcde:2:3:4:5:6:7:8">> = iolist_to_binary(join(re:split("abcde:2:3:4:5:6:7:8","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28869,7 +28869,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless]))), + ",[extended,caseless]))), <<"::1">> = iolist_to_binary(join(re:split("::1","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28878,7 +28878,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,trim]))), + ",[extended,caseless,trim]))), <<"::1">> = iolist_to_binary(join(re:split("::1","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28887,7 +28887,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,{parts,2}]))), + ",[extended,caseless,{parts,2}]))), <<"::1">> = iolist_to_binary(join(re:split("::1","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28896,7 +28896,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless]))), + ",[extended,caseless]))), <<"abcd:fee0:123::">> = iolist_to_binary(join(re:split("abcd:fee0:123::","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28905,7 +28905,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,trim]))), + ",[extended,caseless,trim]))), <<"abcd:fee0:123::">> = iolist_to_binary(join(re:split("abcd:fee0:123::","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28914,7 +28914,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,{parts,2}]))), + ",[extended,caseless,{parts,2}]))), <<"abcd:fee0:123::">> = iolist_to_binary(join(re:split("abcd:fee0:123::","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28923,7 +28923,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless]))), + ",[extended,caseless]))), <<":1">> = iolist_to_binary(join(re:split(":1","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28932,7 +28932,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,trim]))), + ",[extended,caseless,trim]))), <<":1">> = iolist_to_binary(join(re:split(":1","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28941,7 +28941,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,{parts,2}]))), + ",[extended,caseless,{parts,2}]))), <<":1">> = iolist_to_binary(join(re:split(":1","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28950,7 +28950,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless]))), + ",[extended,caseless]))), <<"1:">> = iolist_to_binary(join(re:split("1:","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28959,7 +28959,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,trim]))), + ",[extended,caseless,trim]))), <<"1:">> = iolist_to_binary(join(re:split("1:","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28968,7 +28968,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,{parts,2}]))), + ",[extended,caseless,{parts,2}]))), <<"1:">> = iolist_to_binary(join(re:split("1:","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28977,3098 +28977,3147 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless]))), - <<"">> = iolist_to_binary(join(re:split("z","[z\\Qa-d]\\E]",[trim]))), + ",[extended,caseless]))), + <<"">> = iolist_to_binary(join(re:split("z","[z\\Qa-d]\\E]",[trim]))), <<":">> = iolist_to_binary(join(re:split("z","[z\\Qa-d]\\E]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("z","[z\\Qa-d]\\E]",[]))), - <<"">> = iolist_to_binary(join(re:split("a","[z\\Qa-d]\\E]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("z","[z\\Qa-d]\\E]",[]))), + <<"">> = iolist_to_binary(join(re:split("a","[z\\Qa-d]\\E]",[trim]))), <<":">> = iolist_to_binary(join(re:split("a","[z\\Qa-d]\\E]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a","[z\\Qa-d]\\E]",[]))), - <<"">> = iolist_to_binary(join(re:split("-","[z\\Qa-d]\\E]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a","[z\\Qa-d]\\E]",[]))), + <<"">> = iolist_to_binary(join(re:split("-","[z\\Qa-d]\\E]",[trim]))), <<":">> = iolist_to_binary(join(re:split("-","[z\\Qa-d]\\E]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("-","[z\\Qa-d]\\E]",[]))), - <<"">> = iolist_to_binary(join(re:split("d","[z\\Qa-d]\\E]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("-","[z\\Qa-d]\\E]",[]))), + <<"">> = iolist_to_binary(join(re:split("d","[z\\Qa-d]\\E]",[trim]))), <<":">> = iolist_to_binary(join(re:split("d","[z\\Qa-d]\\E]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("d","[z\\Qa-d]\\E]",[]))), - <<"">> = iolist_to_binary(join(re:split("]","[z\\Qa-d]\\E]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("d","[z\\Qa-d]\\E]",[]))), + <<"">> = iolist_to_binary(join(re:split("]","[z\\Qa-d]\\E]",[trim]))), <<":">> = iolist_to_binary(join(re:split("]","[z\\Qa-d]\\E]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("]","[z\\Qa-d]\\E]",[]))), - <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","[z\\Qa-d]\\E]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("]","[z\\Qa-d]\\E]",[]))), + <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","[z\\Qa-d]\\E]",[trim]))), <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","[z\\Qa-d]\\E]",[{parts, - 2}]))), - <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","[z\\Qa-d]\\E]",[]))), - <<"b">> = iolist_to_binary(join(re:split("b","[z\\Qa-d]\\E]",[trim]))), + 2}]))), + <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","[z\\Qa-d]\\E]",[]))), + <<"b">> = iolist_to_binary(join(re:split("b","[z\\Qa-d]\\E]",[trim]))), <<"b">> = iolist_to_binary(join(re:split("b","[z\\Qa-d]\\E]",[{parts, - 2}]))), - <<"b">> = iolist_to_binary(join(re:split("b","[z\\Qa-d]\\E]",[]))), + 2}]))), + <<"b">> = iolist_to_binary(join(re:split("b","[z\\Qa-d]\\E]",[]))), ok. run34() -> - <<"">> = iolist_to_binary(join(re:split("z","[\\z\\C]",[trim]))), + <<"">> = iolist_to_binary(join(re:split("z","[\\z\\C]",[trim]))), <<":">> = iolist_to_binary(join(re:split("z","[\\z\\C]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("z","[\\z\\C]",[]))), - <<"">> = iolist_to_binary(join(re:split("C","[\\z\\C]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("z","[\\z\\C]",[]))), + <<"">> = iolist_to_binary(join(re:split("C","[\\z\\C]",[trim]))), <<":">> = iolist_to_binary(join(re:split("C","[\\z\\C]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("C","[\\z\\C]",[]))), - <<"">> = iolist_to_binary(join(re:split("M","\\M",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("C","[\\z\\C]",[]))), + <<"">> = iolist_to_binary(join(re:split("M","\\M",[trim]))), <<":">> = iolist_to_binary(join(re:split("M","\\M",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("M","\\M",[]))), - <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(a+)*b",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("M","\\M",[]))), + <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(a+)*b",[trim]))), <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(a+)*b",[{parts, - 2}]))), - <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(a+)*b",[]))), - <<"XAZ">> = iolist_to_binary(join(re:split("XAZXB","(?<=Z)X.",[trim]))), + 2}]))), + <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(a+)*b",[]))), + <<"XAZ">> = iolist_to_binary(join(re:split("XAZXB","(?<=Z)X.",[trim]))), <<"XAZ:">> = iolist_to_binary(join(re:split("XAZXB","(?<=Z)X.",[{parts, - 2}]))), - <<"XAZ:">> = iolist_to_binary(join(re:split("XAZXB","(?<=Z)X.",[]))), - <<"">> = iolist_to_binary(join(re:split("ab cd defg","ab cd (?x) de fg",[trim]))), + 2}]))), + <<"XAZ:">> = iolist_to_binary(join(re:split("XAZXB","(?<=Z)X.",[]))), + <<"">> = iolist_to_binary(join(re:split("ab cd defg","ab cd (?x) de fg",[trim]))), <<":">> = iolist_to_binary(join(re:split("ab cd defg","ab cd (?x) de fg",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ab cd defg","ab cd (?x) de fg",[]))), - <<"">> = iolist_to_binary(join(re:split("ab cddefg","ab cd(?x) de fg",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ab cd defg","ab cd (?x) de fg",[]))), + <<"">> = iolist_to_binary(join(re:split("ab cddefg","ab cd(?x) de fg",[trim]))), <<":">> = iolist_to_binary(join(re:split("ab cddefg","ab cd(?x) de fg",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ab cddefg","ab cd(?x) de fg",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","ab cd(?x) de fg",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ab cddefg","ab cd(?x) de fg",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","ab cd(?x) de fg",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","ab cd(?x) de fg",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","ab cd(?x) de fg",[]))), - <<"abcddefg">> = iolist_to_binary(join(re:split("abcddefg","ab cd(?x) de fg",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","ab cd(?x) de fg",[]))), + <<"abcddefg">> = iolist_to_binary(join(re:split("abcddefg","ab cd(?x) de fg",[trim]))), <<"abcddefg">> = iolist_to_binary(join(re:split("abcddefg","ab cd(?x) de fg",[{parts, - 2}]))), - <<"abcddefg">> = iolist_to_binary(join(re:split("abcddefg","ab cd(?x) de fg",[]))), - <<"foo:bar:X">> = iolist_to_binary(join(re:split("foobarX","(?<![^f]oo)(bar)",[trim]))), + 2}]))), + <<"abcddefg">> = iolist_to_binary(join(re:split("abcddefg","ab cd(?x) de fg",[]))), + <<"foo:bar:X">> = iolist_to_binary(join(re:split("foobarX","(?<![^f]oo)(bar)",[trim]))), <<"foo:bar:X">> = iolist_to_binary(join(re:split("foobarX","(?<![^f]oo)(bar)",[{parts, - 2}]))), - <<"foo:bar:X">> = iolist_to_binary(join(re:split("foobarX","(?<![^f]oo)(bar)",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<![^f]oo)(bar)",[trim]))), + 2}]))), + <<"foo:bar:X">> = iolist_to_binary(join(re:split("foobarX","(?<![^f]oo)(bar)",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<![^f]oo)(bar)",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<![^f]oo)(bar)",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<![^f]oo)(bar)",[]))), - <<"boobarX">> = iolist_to_binary(join(re:split("boobarX","(?<![^f]oo)(bar)",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<![^f]oo)(bar)",[]))), + <<"boobarX">> = iolist_to_binary(join(re:split("boobarX","(?<![^f]oo)(bar)",[trim]))), <<"boobarX">> = iolist_to_binary(join(re:split("boobarX","(?<![^f]oo)(bar)",[{parts, - 2}]))), - <<"boobarX">> = iolist_to_binary(join(re:split("boobarX","(?<![^f]oo)(bar)",[]))), - <<"off">> = iolist_to_binary(join(re:split("offX","(?<![^f])X",[trim]))), + 2}]))), + <<"boobarX">> = iolist_to_binary(join(re:split("boobarX","(?<![^f]oo)(bar)",[]))), + <<"off">> = iolist_to_binary(join(re:split("offX","(?<![^f])X",[trim]))), <<"off:">> = iolist_to_binary(join(re:split("offX","(?<![^f])X",[{parts, - 2}]))), - <<"off:">> = iolist_to_binary(join(re:split("offX","(?<![^f])X",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<![^f])X",[trim]))), + 2}]))), + <<"off:">> = iolist_to_binary(join(re:split("offX","(?<![^f])X",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<![^f])X",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<![^f])X",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<![^f])X",[]))), - <<"onyX">> = iolist_to_binary(join(re:split("onyX","(?<![^f])X",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<![^f])X",[]))), + <<"onyX">> = iolist_to_binary(join(re:split("onyX","(?<![^f])X",[trim]))), <<"onyX">> = iolist_to_binary(join(re:split("onyX","(?<![^f])X",[{parts, - 2}]))), - <<"onyX">> = iolist_to_binary(join(re:split("onyX","(?<![^f])X",[]))), - <<"ony">> = iolist_to_binary(join(re:split("onyX","(?<=[^f])X",[trim]))), + 2}]))), + <<"onyX">> = iolist_to_binary(join(re:split("onyX","(?<![^f])X",[]))), + <<"ony">> = iolist_to_binary(join(re:split("onyX","(?<=[^f])X",[trim]))), <<"ony:">> = iolist_to_binary(join(re:split("onyX","(?<=[^f])X",[{parts, - 2}]))), - <<"ony:">> = iolist_to_binary(join(re:split("onyX","(?<=[^f])X",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=[^f])X",[trim]))), + 2}]))), + <<"ony:">> = iolist_to_binary(join(re:split("onyX","(?<=[^f])X",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=[^f])X",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=[^f])X",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=[^f])X",[]))), - <<"offX">> = iolist_to_binary(join(re:split("offX","(?<=[^f])X",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=[^f])X",[]))), + <<"offX">> = iolist_to_binary(join(re:split("offX","(?<=[^f])X",[trim]))), <<"offX">> = iolist_to_binary(join(re:split("offX","(?<=[^f])X",[{parts, - 2}]))), - <<"offX">> = iolist_to_binary(join(re:split("offX","(?<=[^f])X",[]))), + 2}]))), + <<"offX">> = iolist_to_binary(join(re:split("offX","(?<=[^f])X",[]))), <<"a :b :c">> = iolist_to_binary(join(re:split("a b -c","^",[multiline,trim]))), +c","^",[multiline,trim]))), <<"a :b c">> = iolist_to_binary(join(re:split("a b -c","^",[multiline,{parts,2}]))), +c","^",[multiline,{parts,2}]))), <<"a :b :c">> = iolist_to_binary(join(re:split("a b -c","^",[multiline]))), +c","^",[multiline]))), <<"">> = iolist_to_binary(join(re:split("","^",[multiline, - trim]))), + trim]))), <<"">> = iolist_to_binary(join(re:split("","^",[multiline, {parts, - 2}]))), - <<"">> = iolist_to_binary(join(re:split("","^",[multiline]))), + 2}]))), + <<"">> = iolist_to_binary(join(re:split("","^",[multiline]))), <<"A C :C">> = iolist_to_binary(join(re:split("A C -C","(?<=C\\n)^",[multiline,trim]))), +C","(?<=C\\n)^",[multiline,trim]))), <<"A C :C">> = iolist_to_binary(join(re:split("A C -C","(?<=C\\n)^",[multiline,{parts,2}]))), +C","(?<=C\\n)^",[multiline,{parts,2}]))), <<"A C :C">> = iolist_to_binary(join(re:split("A C -C","(?<=C\\n)^",[multiline]))), - <<":X">> = iolist_to_binary(join(re:split("bXaX","(?:(?(1)a|b)(X))+",[trim]))), +C","(?<=C\\n)^",[multiline]))), + <<":X">> = iolist_to_binary(join(re:split("bXaX","(?:(?(1)a|b)(X))+",[trim]))), <<":X:">> = iolist_to_binary(join(re:split("bXaX","(?:(?(1)a|b)(X))+",[{parts, - 2}]))), - <<":X:">> = iolist_to_binary(join(re:split("bXaX","(?:(?(1)a|b)(X))+",[]))), - <<":Y">> = iolist_to_binary(join(re:split("bXXaYYaY","(?:(?(1)\\1a|b)(X|Y))+",[trim]))), + 2}]))), + <<":X:">> = iolist_to_binary(join(re:split("bXaX","(?:(?(1)a|b)(X))+",[]))), + <<":Y">> = iolist_to_binary(join(re:split("bXXaYYaY","(?:(?(1)\\1a|b)(X|Y))+",[trim]))), <<":Y:">> = iolist_to_binary(join(re:split("bXXaYYaY","(?:(?(1)\\1a|b)(X|Y))+",[{parts, - 2}]))), - <<":Y:">> = iolist_to_binary(join(re:split("bXXaYYaY","(?:(?(1)\\1a|b)(X|Y))+",[]))), - <<":X:YaXXaX">> = iolist_to_binary(join(re:split("bXYaXXaX","(?:(?(1)\\1a|b)(X|Y))+",[trim]))), + 2}]))), + <<":Y:">> = iolist_to_binary(join(re:split("bXXaYYaY","(?:(?(1)\\1a|b)(X|Y))+",[]))), + <<":X:YaXXaX">> = iolist_to_binary(join(re:split("bXYaXXaX","(?:(?(1)\\1a|b)(X|Y))+",[trim]))), <<":X:YaXXaX">> = iolist_to_binary(join(re:split("bXYaXXaX","(?:(?(1)\\1a|b)(X|Y))+",[{parts, - 2}]))), - <<":X:YaXXaX">> = iolist_to_binary(join(re:split("bXYaXXaX","(?:(?(1)\\1a|b)(X|Y))+",[]))), - <<"::::::::::X:XaYYaY">> = iolist_to_binary(join(re:split("bXXaYYaY","()()()()()()()()()(?:(?(10)\\10a|b)(X|Y))+",[trim]))), + 2}]))), + <<":X:YaXXaX">> = iolist_to_binary(join(re:split("bXYaXXaX","(?:(?(1)\\1a|b)(X|Y))+",[]))), + <<"::::::::::X:XaYYaY">> = iolist_to_binary(join(re:split("bXXaYYaY","()()()()()()()()()(?:(?(10)\\10a|b)(X|Y))+",[trim]))), <<"::::::::::X:XaYYaY">> = iolist_to_binary(join(re:split("bXXaYYaY","()()()()()()()()()(?:(?(10)\\10a|b)(X|Y))+",[{parts, - 2}]))), - <<"::::::::::X:XaYYaY">> = iolist_to_binary(join(re:split("bXXaYYaY","()()()()()()()()()(?:(?(10)\\10a|b)(X|Y))+",[]))), - <<"">> = iolist_to_binary(join(re:split("abc]","[[,abc,]+]",[trim]))), + 2}]))), + <<"::::::::::X:XaYYaY">> = iolist_to_binary(join(re:split("bXXaYYaY","()()()()()()()()()(?:(?(10)\\10a|b)(X|Y))+",[]))), + <<"">> = iolist_to_binary(join(re:split("abc]","[[,abc,]+]",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc]","[[,abc,]+]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc]","[[,abc,]+]",[]))), - <<"">> = iolist_to_binary(join(re:split("a,b]","[[,abc,]+]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc]","[[,abc,]+]",[]))), + <<"">> = iolist_to_binary(join(re:split("a,b]","[[,abc,]+]",[trim]))), <<":">> = iolist_to_binary(join(re:split("a,b]","[[,abc,]+]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a,b]","[[,abc,]+]",[]))), - <<"">> = iolist_to_binary(join(re:split("[a,b,c]","[[,abc,]+]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a,b]","[[,abc,]+]",[]))), + <<"">> = iolist_to_binary(join(re:split("[a,b,c]","[[,abc,]+]",[trim]))), <<":">> = iolist_to_binary(join(re:split("[a,b,c]","[[,abc,]+]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("[a,b,c]","[[,abc,]+]",[]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("[a,b,c]","[[,abc,]+]",[]))), <<"A:B">> = iolist_to_binary(join(re:split("A B","(?-x: )",[extended, - trim]))), + trim]))), <<"A:B">> = iolist_to_binary(join(re:split("A B","(?-x: )",[extended, {parts, - 2}]))), - <<"A:B">> = iolist_to_binary(join(re:split("A B","(?-x: )",[extended]))), - <<"A:B">> = iolist_to_binary(join(re:split("A # B","(?x)(?-x: \\s*#\\s*)",[trim]))), + 2}]))), + <<"A:B">> = iolist_to_binary(join(re:split("A B","(?-x: )",[extended]))), + <<"A:B">> = iolist_to_binary(join(re:split("A # B","(?x)(?-x: \\s*#\\s*)",[trim]))), <<"A:B">> = iolist_to_binary(join(re:split("A # B","(?x)(?-x: \\s*#\\s*)",[{parts, - 2}]))), - <<"A:B">> = iolist_to_binary(join(re:split("A # B","(?x)(?-x: \\s*#\\s*)",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?x)(?-x: \\s*#\\s*)",[trim]))), + 2}]))), + <<"A:B">> = iolist_to_binary(join(re:split("A # B","(?x)(?-x: \\s*#\\s*)",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?x)(?-x: \\s*#\\s*)",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?x)(?-x: \\s*#\\s*)",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?x)(?-x: \\s*#\\s*)",[]))), - <<"#">> = iolist_to_binary(join(re:split("#","(?x)(?-x: \\s*#\\s*)",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?x)(?-x: \\s*#\\s*)",[]))), + <<"#">> = iolist_to_binary(join(re:split("#","(?x)(?-x: \\s*#\\s*)",[trim]))), <<"#">> = iolist_to_binary(join(re:split("#","(?x)(?-x: \\s*#\\s*)",[{parts, - 2}]))), - <<"#">> = iolist_to_binary(join(re:split("#","(?x)(?-x: \\s*#\\s*)",[]))), - <<"A">> = iolist_to_binary(join(re:split("A #include","(?x-is)(?:(?-ixs) \\s*#\\s*) include",[trim]))), + 2}]))), + <<"#">> = iolist_to_binary(join(re:split("#","(?x)(?-x: \\s*#\\s*)",[]))), + <<"A">> = iolist_to_binary(join(re:split("A #include","(?x-is)(?:(?-ixs) \\s*#\\s*) include",[trim]))), <<"A:">> = iolist_to_binary(join(re:split("A #include","(?x-is)(?:(?-ixs) \\s*#\\s*) include",[{parts, - 2}]))), - <<"A:">> = iolist_to_binary(join(re:split("A #include","(?x-is)(?:(?-ixs) \\s*#\\s*) include",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?x-is)(?:(?-ixs) \\s*#\\s*) include",[trim]))), + 2}]))), + <<"A:">> = iolist_to_binary(join(re:split("A #include","(?x-is)(?:(?-ixs) \\s*#\\s*) include",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?x-is)(?:(?-ixs) \\s*#\\s*) include",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?x-is)(?:(?-ixs) \\s*#\\s*) include",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?x-is)(?:(?-ixs) \\s*#\\s*) include",[]))), - <<"A#include">> = iolist_to_binary(join(re:split("A#include","(?x-is)(?:(?-ixs) \\s*#\\s*) include",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?x-is)(?:(?-ixs) \\s*#\\s*) include",[]))), + <<"A#include">> = iolist_to_binary(join(re:split("A#include","(?x-is)(?:(?-ixs) \\s*#\\s*) include",[trim]))), <<"A#include">> = iolist_to_binary(join(re:split("A#include","(?x-is)(?:(?-ixs) \\s*#\\s*) include",[{parts, - 2}]))), - <<"A#include">> = iolist_to_binary(join(re:split("A#include","(?x-is)(?:(?-ixs) \\s*#\\s*) include",[]))), - <<"A #Include">> = iolist_to_binary(join(re:split("A #Include","(?x-is)(?:(?-ixs) \\s*#\\s*) include",[trim]))), + 2}]))), + <<"A#include">> = iolist_to_binary(join(re:split("A#include","(?x-is)(?:(?-ixs) \\s*#\\s*) include",[]))), + <<"A #Include">> = iolist_to_binary(join(re:split("A #Include","(?x-is)(?:(?-ixs) \\s*#\\s*) include",[trim]))), <<"A #Include">> = iolist_to_binary(join(re:split("A #Include","(?x-is)(?:(?-ixs) \\s*#\\s*) include",[{parts, - 2}]))), - <<"A #Include">> = iolist_to_binary(join(re:split("A #Include","(?x-is)(?:(?-ixs) \\s*#\\s*) include",[]))), + 2}]))), + <<"A #Include">> = iolist_to_binary(join(re:split("A #Include","(?x-is)(?:(?-ixs) \\s*#\\s*) include",[]))), ok. run35() -> - <<"">> = iolist_to_binary(join(re:split("aaabbbb","a*b*\\w",[trim]))), + <<"">> = iolist_to_binary(join(re:split("aaabbbb","a*b*\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaabbbb","a*b*\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaabbbb","a*b*\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaa","a*b*\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaabbbb","a*b*\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaa","a*b*\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaa","a*b*\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaa","a*b*\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("a","a*b*\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaa","a*b*\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("a","a*b*\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("a","a*b*\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a","a*b*\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("aaabbbb","a*b?\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a","a*b*\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("aaabbbb","a*b?\\w",[trim]))), <<":bb">> = iolist_to_binary(join(re:split("aaabbbb","a*b?\\w",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("aaabbbb","a*b?\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaa","a*b?\\w",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("aaabbbb","a*b?\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaa","a*b?\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaa","a*b?\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaa","a*b?\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("a","a*b?\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaa","a*b?\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("a","a*b?\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("a","a*b?\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a","a*b?\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("aaabbbb","a*b{0,4}\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a","a*b?\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("aaabbbb","a*b{0,4}\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaabbbb","a*b{0,4}\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaabbbb","a*b{0,4}\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaa","a*b{0,4}\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaabbbb","a*b{0,4}\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaa","a*b{0,4}\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaa","a*b{0,4}\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaa","a*b{0,4}\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("a","a*b{0,4}\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaa","a*b{0,4}\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("a","a*b{0,4}\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("a","a*b{0,4}\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a","a*b{0,4}\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("aaabbbb","a*b{0,}\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a","a*b{0,4}\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("aaabbbb","a*b{0,}\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaabbbb","a*b{0,}\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaabbbb","a*b{0,}\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaa","a*b{0,}\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaabbbb","a*b{0,}\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaa","a*b{0,}\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaa","a*b{0,}\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaa","a*b{0,}\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("a","a*b{0,}\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaa","a*b{0,}\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("a","a*b{0,}\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("a","a*b{0,}\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a","a*b{0,}\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("0a","a*\\d*\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a","a*b{0,}\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("0a","a*\\d*\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("0a","a*\\d*\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("0a","a*\\d*\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("a","a*\\d*\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("0a","a*\\d*\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("a","a*\\d*\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("a","a*\\d*\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a","a*\\d*\\w",[]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a","a*\\d*\\w",[]))), <<"">> = iolist_to_binary(join(re:split("a","a*b *\\w",[extended, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("a","a*b *\\w",[extended, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a","a*b *\\w",[extended]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a","a*b *\\w",[extended]))), <<"">> = iolist_to_binary(join(re:split("a","a*b#comment - *\\w",[extended,trim]))), + *\\w",[extended,trim]))), <<":">> = iolist_to_binary(join(re:split("a","a*b#comment - *\\w",[extended,{parts,2}]))), + *\\w",[extended,{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("a","a*b#comment - *\\w",[extended]))), + *\\w",[extended]))), <<"">> = iolist_to_binary(join(re:split("a","a* b *\\w",[extended, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("a","a* b *\\w",[extended, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a","a* b *\\w",[extended]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a","a* b *\\w",[extended]))), <<":: pqr">> = iolist_to_binary(join(re:split("abc=xyz\\ -pqr","^\\w+=.*(\\\\\\n.*)*",[trim]))), +pqr","^\\w+=.*(\\\\\\n.*)*",[trim]))), <<":: pqr">> = iolist_to_binary(join(re:split("abc=xyz\\ -pqr","^\\w+=.*(\\\\\\n.*)*",[{parts,2}]))), +pqr","^\\w+=.*(\\\\\\n.*)*",[{parts,2}]))), <<":: pqr">> = iolist_to_binary(join(re:split("abc=xyz\\ -pqr","^\\w+=.*(\\\\\\n.*)*",[]))), - <<":abcd">> = iolist_to_binary(join(re:split("abcd:","(?=(\\w+))\\1:",[trim]))), +pqr","^\\w+=.*(\\\\\\n.*)*",[]))), + <<":abcd">> = iolist_to_binary(join(re:split("abcd:","(?=(\\w+))\\1:",[trim]))), <<":abcd:">> = iolist_to_binary(join(re:split("abcd:","(?=(\\w+))\\1:",[{parts, - 2}]))), - <<":abcd:">> = iolist_to_binary(join(re:split("abcd:","(?=(\\w+))\\1:",[]))), - <<":abcd">> = iolist_to_binary(join(re:split("abcd:","^(?=(\\w+))\\1:",[trim]))), + 2}]))), + <<":abcd:">> = iolist_to_binary(join(re:split("abcd:","(?=(\\w+))\\1:",[]))), + <<":abcd">> = iolist_to_binary(join(re:split("abcd:","^(?=(\\w+))\\1:",[trim]))), <<":abcd:">> = iolist_to_binary(join(re:split("abcd:","^(?=(\\w+))\\1:",[{parts, - 2}]))), - <<":abcd:">> = iolist_to_binary(join(re:split("abcd:","^(?=(\\w+))\\1:",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","^\\Eabc",[trim]))), + 2}]))), + <<":abcd:">> = iolist_to_binary(join(re:split("abcd:","^(?=(\\w+))\\1:",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","^\\Eabc",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","^\\Eabc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","^\\Eabc",[]))), - <<"">> = iolist_to_binary(join(re:split("a","^[\\Eabc]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","^\\Eabc",[]))), + <<"">> = iolist_to_binary(join(re:split("a","^[\\Eabc]",[trim]))), <<":">> = iolist_to_binary(join(re:split("a","^[\\Eabc]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a","^[\\Eabc]",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[\\Eabc]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a","^[\\Eabc]",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[\\Eabc]",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[\\Eabc]",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[\\Eabc]",[]))), - <<"E">> = iolist_to_binary(join(re:split("E","^[\\Eabc]",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[\\Eabc]",[]))), + <<"E">> = iolist_to_binary(join(re:split("E","^[\\Eabc]",[trim]))), <<"E">> = iolist_to_binary(join(re:split("E","^[\\Eabc]",[{parts, - 2}]))), - <<"E">> = iolist_to_binary(join(re:split("E","^[\\Eabc]",[]))), - <<"">> = iolist_to_binary(join(re:split("b","^[a-\\Ec]",[trim]))), + 2}]))), + <<"E">> = iolist_to_binary(join(re:split("E","^[\\Eabc]",[]))), + <<"">> = iolist_to_binary(join(re:split("b","^[a-\\Ec]",[trim]))), <<":">> = iolist_to_binary(join(re:split("b","^[a-\\Ec]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("b","^[a-\\Ec]",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[a-\\Ec]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("b","^[a-\\Ec]",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[a-\\Ec]",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[a-\\Ec]",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[a-\\Ec]",[]))), - <<"-">> = iolist_to_binary(join(re:split("-","^[a-\\Ec]",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[a-\\Ec]",[]))), + <<"-">> = iolist_to_binary(join(re:split("-","^[a-\\Ec]",[trim]))), <<"-">> = iolist_to_binary(join(re:split("-","^[a-\\Ec]",[{parts, - 2}]))), - <<"-">> = iolist_to_binary(join(re:split("-","^[a-\\Ec]",[]))), - <<"E">> = iolist_to_binary(join(re:split("E","^[a-\\Ec]",[trim]))), + 2}]))), + <<"-">> = iolist_to_binary(join(re:split("-","^[a-\\Ec]",[]))), + <<"E">> = iolist_to_binary(join(re:split("E","^[a-\\Ec]",[trim]))), <<"E">> = iolist_to_binary(join(re:split("E","^[a-\\Ec]",[{parts, - 2}]))), - <<"E">> = iolist_to_binary(join(re:split("E","^[a-\\Ec]",[]))), - <<"">> = iolist_to_binary(join(re:split("b","^[a\\E\\E-\\Ec]",[trim]))), + 2}]))), + <<"E">> = iolist_to_binary(join(re:split("E","^[a-\\Ec]",[]))), + <<"">> = iolist_to_binary(join(re:split("b","^[a\\E\\E-\\Ec]",[trim]))), <<":">> = iolist_to_binary(join(re:split("b","^[a\\E\\E-\\Ec]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("b","^[a\\E\\E-\\Ec]",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[a\\E\\E-\\Ec]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("b","^[a\\E\\E-\\Ec]",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[a\\E\\E-\\Ec]",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[a\\E\\E-\\Ec]",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[a\\E\\E-\\Ec]",[]))), - <<"-">> = iolist_to_binary(join(re:split("-","^[a\\E\\E-\\Ec]",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[a\\E\\E-\\Ec]",[]))), + <<"-">> = iolist_to_binary(join(re:split("-","^[a\\E\\E-\\Ec]",[trim]))), <<"-">> = iolist_to_binary(join(re:split("-","^[a\\E\\E-\\Ec]",[{parts, - 2}]))), - <<"-">> = iolist_to_binary(join(re:split("-","^[a\\E\\E-\\Ec]",[]))), - <<"E">> = iolist_to_binary(join(re:split("E","^[a\\E\\E-\\Ec]",[trim]))), + 2}]))), + <<"-">> = iolist_to_binary(join(re:split("-","^[a\\E\\E-\\Ec]",[]))), + <<"E">> = iolist_to_binary(join(re:split("E","^[a\\E\\E-\\Ec]",[trim]))), <<"E">> = iolist_to_binary(join(re:split("E","^[a\\E\\E-\\Ec]",[{parts, - 2}]))), - <<"E">> = iolist_to_binary(join(re:split("E","^[a\\E\\E-\\Ec]",[]))), - <<"">> = iolist_to_binary(join(re:split("b","^[\\E\\Qa\\E-\\Qz\\E]+",[trim]))), + 2}]))), + <<"E">> = iolist_to_binary(join(re:split("E","^[a\\E\\E-\\Ec]",[]))), + <<"">> = iolist_to_binary(join(re:split("b","^[\\E\\Qa\\E-\\Qz\\E]+",[trim]))), <<":">> = iolist_to_binary(join(re:split("b","^[\\E\\Qa\\E-\\Qz\\E]+",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("b","^[\\E\\Qa\\E-\\Qz\\E]+",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[\\E\\Qa\\E-\\Qz\\E]+",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("b","^[\\E\\Qa\\E-\\Qz\\E]+",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[\\E\\Qa\\E-\\Qz\\E]+",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[\\E\\Qa\\E-\\Qz\\E]+",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[\\E\\Qa\\E-\\Qz\\E]+",[]))), - <<"-">> = iolist_to_binary(join(re:split("-","^[\\E\\Qa\\E-\\Qz\\E]+",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[\\E\\Qa\\E-\\Qz\\E]+",[]))), + <<"-">> = iolist_to_binary(join(re:split("-","^[\\E\\Qa\\E-\\Qz\\E]+",[trim]))), <<"-">> = iolist_to_binary(join(re:split("-","^[\\E\\Qa\\E-\\Qz\\E]+",[{parts, - 2}]))), - <<"-">> = iolist_to_binary(join(re:split("-","^[\\E\\Qa\\E-\\Qz\\E]+",[]))), - <<"">> = iolist_to_binary(join(re:split("a","^[a\\Q]bc\\E]",[trim]))), + 2}]))), + <<"-">> = iolist_to_binary(join(re:split("-","^[\\E\\Qa\\E-\\Qz\\E]+",[]))), + <<"">> = iolist_to_binary(join(re:split("a","^[a\\Q]bc\\E]",[trim]))), <<":">> = iolist_to_binary(join(re:split("a","^[a\\Q]bc\\E]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a","^[a\\Q]bc\\E]",[]))), - <<"">> = iolist_to_binary(join(re:split("]","^[a\\Q]bc\\E]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a","^[a\\Q]bc\\E]",[]))), + <<"">> = iolist_to_binary(join(re:split("]","^[a\\Q]bc\\E]",[trim]))), <<":">> = iolist_to_binary(join(re:split("]","^[a\\Q]bc\\E]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("]","^[a\\Q]bc\\E]",[]))), - <<"">> = iolist_to_binary(join(re:split("c","^[a\\Q]bc\\E]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("]","^[a\\Q]bc\\E]",[]))), + <<"">> = iolist_to_binary(join(re:split("c","^[a\\Q]bc\\E]",[trim]))), <<":">> = iolist_to_binary(join(re:split("c","^[a\\Q]bc\\E]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("c","^[a\\Q]bc\\E]",[]))), - <<"">> = iolist_to_binary(join(re:split("a","^[a-\\Q\\E]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("c","^[a\\Q]bc\\E]",[]))), + <<"">> = iolist_to_binary(join(re:split("a","^[a-\\Q\\E]",[trim]))), <<":">> = iolist_to_binary(join(re:split("a","^[a-\\Q\\E]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a","^[a-\\Q\\E]",[]))), - <<"">> = iolist_to_binary(join(re:split("-","^[a-\\Q\\E]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a","^[a-\\Q\\E]",[]))), + <<"">> = iolist_to_binary(join(re:split("-","^[a-\\Q\\E]",[trim]))), <<":">> = iolist_to_binary(join(re:split("-","^[a-\\Q\\E]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("-","^[a-\\Q\\E]",[]))), - <<":a">> = iolist_to_binary(join(re:split("aaaa","^(a()*)*",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("-","^[a-\\Q\\E]",[]))), + <<":a">> = iolist_to_binary(join(re:split("aaaa","^(a()*)*",[trim]))), <<":a::">> = iolist_to_binary(join(re:split("aaaa","^(a()*)*",[{parts, - 2}]))), - <<":a::">> = iolist_to_binary(join(re:split("aaaa","^(a()*)*",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaa","^(?:a(?:(?:))*)*",[trim]))), + 2}]))), + <<":a::">> = iolist_to_binary(join(re:split("aaaa","^(a()*)*",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaa","^(?:a(?:(?:))*)*",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaa","^(?:a(?:(?:))*)*",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaa","^(?:a(?:(?:))*)*",[]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaa","^(?:a(?:(?:))*)*",[]))), ok. run36() -> - <<":a">> = iolist_to_binary(join(re:split("aaaa","^(a()+)+",[trim]))), + <<":a">> = iolist_to_binary(join(re:split("aaaa","^(a()+)+",[trim]))), <<":a::">> = iolist_to_binary(join(re:split("aaaa","^(a()+)+",[{parts, - 2}]))), - <<":a::">> = iolist_to_binary(join(re:split("aaaa","^(a()+)+",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaa","^(?:a(?:(?:))+)+",[trim]))), + 2}]))), + <<":a::">> = iolist_to_binary(join(re:split("aaaa","^(a()+)+",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaa","^(?:a(?:(?:))+)+",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaa","^(?:a(?:(?:))+)+",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaa","^(?:a(?:(?:))+)+",[]))), - <<":a">> = iolist_to_binary(join(re:split("abbD","(a){0,3}(?(1)b|(c|))*D",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaa","^(?:a(?:(?:))+)+",[]))), + <<":a">> = iolist_to_binary(join(re:split("abbD","(a){0,3}(?(1)b|(c|))*D",[trim]))), <<":a::">> = iolist_to_binary(join(re:split("abbD","(a){0,3}(?(1)b|(c|))*D",[{parts, - 2}]))), - <<":a::">> = iolist_to_binary(join(re:split("abbD","(a){0,3}(?(1)b|(c|))*D",[]))), - <<"">> = iolist_to_binary(join(re:split("ccccD","(a){0,3}(?(1)b|(c|))*D",[trim]))), + 2}]))), + <<":a::">> = iolist_to_binary(join(re:split("abbD","(a){0,3}(?(1)b|(c|))*D",[]))), + <<"">> = iolist_to_binary(join(re:split("ccccD","(a){0,3}(?(1)b|(c|))*D",[trim]))), <<":::">> = iolist_to_binary(join(re:split("ccccD","(a){0,3}(?(1)b|(c|))*D",[{parts, - 2}]))), - <<":::">> = iolist_to_binary(join(re:split("ccccD","(a){0,3}(?(1)b|(c|))*D",[]))), - <<"">> = iolist_to_binary(join(re:split("D","(a){0,3}(?(1)b|(c|))*D",[trim]))), + 2}]))), + <<":::">> = iolist_to_binary(join(re:split("ccccD","(a){0,3}(?(1)b|(c|))*D",[]))), + <<"">> = iolist_to_binary(join(re:split("D","(a){0,3}(?(1)b|(c|))*D",[trim]))), <<":::">> = iolist_to_binary(join(re:split("D","(a){0,3}(?(1)b|(c|))*D",[{parts, - 2}]))), - <<":::">> = iolist_to_binary(join(re:split("D","(a){0,3}(?(1)b|(c|))*D",[]))), - <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(a|)*\\d",[trim]))), + 2}]))), + <<":::">> = iolist_to_binary(join(re:split("D","(a){0,3}(?(1)b|(c|))*D",[]))), + <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(a|)*\\d",[trim]))), <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(a|)*\\d",[{parts, - 2}]))), - <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(a|)*\\d",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa4","(a|)*\\d",[trim]))), + 2}]))), + <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(a|)*\\d",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa4","(a|)*\\d",[trim]))), <<"::">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa4","(a|)*\\d",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa4","(a|)*\\d",[]))), - <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(?>a|)*\\d",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa4","(a|)*\\d",[]))), + <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(?>a|)*\\d",[trim]))), <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(?>a|)*\\d",[{parts, - 2}]))), - <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(?>a|)*\\d",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa4","(?>a|)*\\d",[trim]))), + 2}]))), + <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(?>a|)*\\d",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa4","(?>a|)*\\d",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa4","(?>a|)*\\d",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa4","(?>a|)*\\d",[]))), - <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(?:a|)*\\d",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa4","(?>a|)*\\d",[]))), + <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(?:a|)*\\d",[trim]))), <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(?:a|)*\\d",[{parts, - 2}]))), - <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(?:a|)*\\d",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa4","(?:a|)*\\d",[trim]))), + 2}]))), + <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(?:a|)*\\d",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa4","(?:a|)*\\d",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa4","(?:a|)*\\d",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa4","(?:a|)*\\d",[]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","\\Z",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa4","(?:a|)*\\d",[]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","\\Z",[trim]))), <<"abc:">> = iolist_to_binary(join(re:split("abc","\\Z",[{parts, - 2}]))), - <<"abc:">> = iolist_to_binary(join(re:split("abc","\\Z",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","^(?s)(?>.*)(?<!\\n)",[trim]))), + 2}]))), + <<"abc:">> = iolist_to_binary(join(re:split("abc","\\Z",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","^(?s)(?>.*)(?<!\\n)",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","^(?s)(?>.*)(?<!\\n)",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","^(?s)(?>.*)(?<!\\n)",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","^(?s)(?>.*)(?<!\\n)",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","^(?s)(?>.*)(?<!\\n)",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","^(?s)(?>.*)(?<!\\n)",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","^(?s)(?>.*)(?<!\\n)",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","^(?s)(?>.*)(?<!\\n)",[]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","^(?![^\\n]*\\n\\z)",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","^(?s)(?>.*)(?<!\\n)",[]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","^(?![^\\n]*\\n\\z)",[trim]))), <<"abc">> = iolist_to_binary(join(re:split("abc","^(?![^\\n]*\\n\\z)",[{parts, - 2}]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","^(?![^\\n]*\\n\\z)",[]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","^(?![^\\n]*\\n\\z)",[trim]))), + 2}]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","^(?![^\\n]*\\n\\z)",[]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","^(?![^\\n]*\\n\\z)",[trim]))), <<"abc">> = iolist_to_binary(join(re:split("abc","^(?![^\\n]*\\n\\z)",[{parts, - 2}]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","^(?![^\\n]*\\n\\z)",[]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","\\z(?<!\\n)",[trim]))), + 2}]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","^(?![^\\n]*\\n\\z)",[]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","\\z(?<!\\n)",[trim]))), <<"abc:">> = iolist_to_binary(join(re:split("abc","\\z(?<!\\n)",[{parts, - 2}]))), - <<"abc:">> = iolist_to_binary(join(re:split("abc","\\z(?<!\\n)",[]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","\\z(?<!\\n)",[trim]))), + 2}]))), + <<"abc:">> = iolist_to_binary(join(re:split("abc","\\z(?<!\\n)",[]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","\\z(?<!\\n)",[trim]))), <<"abc:">> = iolist_to_binary(join(re:split("abc","\\z(?<!\\n)",[{parts, - 2}]))), - <<"abc:">> = iolist_to_binary(join(re:split("abc","\\z(?<!\\n)",[]))), - <<"">> = iolist_to_binary(join(re:split("abcd","(.*(.)?)*",[trim]))), + 2}]))), + <<"abc:">> = iolist_to_binary(join(re:split("abc","\\z(?<!\\n)",[]))), + <<"">> = iolist_to_binary(join(re:split("abcd","(.*(.)?)*",[trim]))), <<":::">> = iolist_to_binary(join(re:split("abcd","(.*(.)?)*",[{parts, - 2}]))), - <<":::">> = iolist_to_binary(join(re:split("abcd","(.*(.)?)*",[]))), + 2}]))), + <<":::">> = iolist_to_binary(join(re:split("abcd","(.*(.)?)*",[]))), <<"a:::b:::c:::d">> = iolist_to_binary(join(re:split("abcd","( (A | (?(1)0|) )* )",[extended, - trim]))), + trim]))), <<"a:::bcd">> = iolist_to_binary(join(re:split("abcd","( (A | (?(1)0|) )* )",[extended, {parts, - 2}]))), - <<"a:::b:::c:::d:::">> = iolist_to_binary(join(re:split("abcd","( (A | (?(1)0|) )* )",[extended]))), + 2}]))), + <<"a:::b:::c:::d:::">> = iolist_to_binary(join(re:split("abcd","( (A | (?(1)0|) )* )",[extended]))), <<"a:::b:::c:::d">> = iolist_to_binary(join(re:split("abcd","( ( (?(1)0|) )* )",[extended, - trim]))), + trim]))), <<"a:::bcd">> = iolist_to_binary(join(re:split("abcd","( ( (?(1)0|) )* )",[extended, {parts, - 2}]))), - <<"a:::b:::c:::d:::">> = iolist_to_binary(join(re:split("abcd","( ( (?(1)0|) )* )",[extended]))), + 2}]))), + <<"a:::b:::c:::d:::">> = iolist_to_binary(join(re:split("abcd","( ( (?(1)0|) )* )",[extended]))), <<"a::b::c::d">> = iolist_to_binary(join(re:split("abcd","( (?(1)0|)* )",[extended, - trim]))), + trim]))), <<"a::bcd">> = iolist_to_binary(join(re:split("abcd","( (?(1)0|)* )",[extended, {parts, - 2}]))), - <<"a::b::c::d::">> = iolist_to_binary(join(re:split("abcd","( (?(1)0|)* )",[extended]))), - <<"">> = iolist_to_binary(join(re:split("a]","[[:abcd:xyz]]",[trim]))), + 2}]))), + <<"a::b::c::d::">> = iolist_to_binary(join(re:split("abcd","( (?(1)0|)* )",[extended]))), + <<"">> = iolist_to_binary(join(re:split("a]","[[:abcd:xyz]]",[trim]))), <<":">> = iolist_to_binary(join(re:split("a]","[[:abcd:xyz]]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a]","[[:abcd:xyz]]",[]))), - <<"">> = iolist_to_binary(join(re:split(":]","[[:abcd:xyz]]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a]","[[:abcd:xyz]]",[]))), + <<"">> = iolist_to_binary(join(re:split(":]","[[:abcd:xyz]]",[trim]))), <<":">> = iolist_to_binary(join(re:split(":]","[[:abcd:xyz]]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split(":]","[[:abcd:xyz]]",[]))), - <<"">> = iolist_to_binary(join(re:split("a","[abc[:x\\]pqr]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split(":]","[[:abcd:xyz]]",[]))), + <<"">> = iolist_to_binary(join(re:split("a","[abc[:x\\]pqr]",[trim]))), <<":">> = iolist_to_binary(join(re:split("a","[abc[:x\\]pqr]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a","[abc[:x\\]pqr]",[]))), - <<"">> = iolist_to_binary(join(re:split("[","[abc[:x\\]pqr]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a","[abc[:x\\]pqr]",[]))), + <<"">> = iolist_to_binary(join(re:split("[","[abc[:x\\]pqr]",[trim]))), <<":">> = iolist_to_binary(join(re:split("[","[abc[:x\\]pqr]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("[","[abc[:x\\]pqr]",[]))), - <<"">> = iolist_to_binary(join(re:split(":","[abc[:x\\]pqr]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("[","[abc[:x\\]pqr]",[]))), + <<"">> = iolist_to_binary(join(re:split(":","[abc[:x\\]pqr]",[trim]))), <<":">> = iolist_to_binary(join(re:split(":","[abc[:x\\]pqr]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split(":","[abc[:x\\]pqr]",[]))), - <<"">> = iolist_to_binary(join(re:split("]","[abc[:x\\]pqr]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split(":","[abc[:x\\]pqr]",[]))), + <<"">> = iolist_to_binary(join(re:split("]","[abc[:x\\]pqr]",[trim]))), <<":">> = iolist_to_binary(join(re:split("]","[abc[:x\\]pqr]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("]","[abc[:x\\]pqr]",[]))), - <<"">> = iolist_to_binary(join(re:split("p","[abc[:x\\]pqr]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("]","[abc[:x\\]pqr]",[]))), + <<"">> = iolist_to_binary(join(re:split("p","[abc[:x\\]pqr]",[trim]))), <<":">> = iolist_to_binary(join(re:split("p","[abc[:x\\]pqr]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("p","[abc[:x\\]pqr]",[]))), - <<"fooabcfoo">> = iolist_to_binary(join(re:split("fooabcfoo",".*[op][xyz]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("p","[abc[:x\\]pqr]",[]))), + <<"fooabcfoo">> = iolist_to_binary(join(re:split("fooabcfoo",".*[op][xyz]",[trim]))), <<"fooabcfoo">> = iolist_to_binary(join(re:split("fooabcfoo",".*[op][xyz]",[{parts, - 2}]))), - <<"fooabcfoo">> = iolist_to_binary(join(re:split("fooabcfoo",".*[op][xyz]",[]))), - <<"adc">> = iolist_to_binary(join(re:split("adc","(?(?=.*b)b|^)",[trim]))), + 2}]))), + <<"fooabcfoo">> = iolist_to_binary(join(re:split("fooabcfoo",".*[op][xyz]",[]))), + <<"adc">> = iolist_to_binary(join(re:split("adc","(?(?=.*b)b|^)",[trim]))), <<"adc">> = iolist_to_binary(join(re:split("adc","(?(?=.*b)b|^)",[{parts, - 2}]))), - <<"adc">> = iolist_to_binary(join(re:split("adc","(?(?=.*b)b|^)",[]))), - <<"a:c">> = iolist_to_binary(join(re:split("abc","(?(?=.*b)b|^)",[trim]))), + 2}]))), + <<"adc">> = iolist_to_binary(join(re:split("adc","(?(?=.*b)b|^)",[]))), + <<"a:c">> = iolist_to_binary(join(re:split("abc","(?(?=.*b)b|^)",[trim]))), <<"a:c">> = iolist_to_binary(join(re:split("abc","(?(?=.*b)b|^)",[{parts, - 2}]))), - <<"a:c">> = iolist_to_binary(join(re:split("abc","(?(?=.*b)b|^)",[]))), - <<"adc">> = iolist_to_binary(join(re:split("adc","(?(?=^.*b)b|^)",[trim]))), + 2}]))), + <<"a:c">> = iolist_to_binary(join(re:split("abc","(?(?=.*b)b|^)",[]))), + <<"adc">> = iolist_to_binary(join(re:split("adc","(?(?=^.*b)b|^)",[trim]))), <<"adc">> = iolist_to_binary(join(re:split("adc","(?(?=^.*b)b|^)",[{parts, - 2}]))), - <<"adc">> = iolist_to_binary(join(re:split("adc","(?(?=^.*b)b|^)",[]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","(?(?=^.*b)b|^)",[trim]))), + 2}]))), + <<"adc">> = iolist_to_binary(join(re:split("adc","(?(?=^.*b)b|^)",[]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","(?(?=^.*b)b|^)",[trim]))), <<"abc">> = iolist_to_binary(join(re:split("abc","(?(?=^.*b)b|^)",[{parts, - 2}]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","(?(?=^.*b)b|^)",[]))), - <<"a:d:c">> = iolist_to_binary(join(re:split("adc","(?(?=.*b)b|^)*",[trim]))), + 2}]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","(?(?=^.*b)b|^)",[]))), + <<"a:d:c">> = iolist_to_binary(join(re:split("adc","(?(?=.*b)b|^)*",[trim]))), <<"a:dc">> = iolist_to_binary(join(re:split("adc","(?(?=.*b)b|^)*",[{parts, - 2}]))), - <<"a:d:c:">> = iolist_to_binary(join(re:split("adc","(?(?=.*b)b|^)*",[]))), - <<"a:c">> = iolist_to_binary(join(re:split("abc","(?(?=.*b)b|^)*",[trim]))), + 2}]))), + <<"a:d:c:">> = iolist_to_binary(join(re:split("adc","(?(?=.*b)b|^)*",[]))), + <<"a:c">> = iolist_to_binary(join(re:split("abc","(?(?=.*b)b|^)*",[trim]))), <<"a:c">> = iolist_to_binary(join(re:split("abc","(?(?=.*b)b|^)*",[{parts, - 2}]))), - <<"a:c:">> = iolist_to_binary(join(re:split("abc","(?(?=.*b)b|^)*",[]))), + 2}]))), + <<"a:c:">> = iolist_to_binary(join(re:split("abc","(?(?=.*b)b|^)*",[]))), ok. run37() -> - <<"adc">> = iolist_to_binary(join(re:split("adc","(?(?=.*b)b|^)+",[trim]))), + <<"adc">> = iolist_to_binary(join(re:split("adc","(?(?=.*b)b|^)+",[trim]))), <<"adc">> = iolist_to_binary(join(re:split("adc","(?(?=.*b)b|^)+",[{parts, - 2}]))), - <<"adc">> = iolist_to_binary(join(re:split("adc","(?(?=.*b)b|^)+",[]))), - <<"a:c">> = iolist_to_binary(join(re:split("abc","(?(?=.*b)b|^)+",[trim]))), + 2}]))), + <<"adc">> = iolist_to_binary(join(re:split("adc","(?(?=.*b)b|^)+",[]))), + <<"a:c">> = iolist_to_binary(join(re:split("abc","(?(?=.*b)b|^)+",[trim]))), <<"a:c">> = iolist_to_binary(join(re:split("abc","(?(?=.*b)b|^)+",[{parts, - 2}]))), - <<"a:c">> = iolist_to_binary(join(re:split("abc","(?(?=.*b)b|^)+",[]))), - <<"a:c">> = iolist_to_binary(join(re:split("abc","(?(?=b).*b|^d)",[trim]))), + 2}]))), + <<"a:c">> = iolist_to_binary(join(re:split("abc","(?(?=.*b)b|^)+",[]))), + <<"a:c">> = iolist_to_binary(join(re:split("abc","(?(?=b).*b|^d)",[trim]))), <<"a:c">> = iolist_to_binary(join(re:split("abc","(?(?=b).*b|^d)",[{parts, - 2}]))), - <<"a:c">> = iolist_to_binary(join(re:split("abc","(?(?=b).*b|^d)",[]))), - <<":c">> = iolist_to_binary(join(re:split("abc","(?(?=.*b).*b|^d)",[trim]))), + 2}]))), + <<"a:c">> = iolist_to_binary(join(re:split("abc","(?(?=b).*b|^d)",[]))), + <<":c">> = iolist_to_binary(join(re:split("abc","(?(?=.*b).*b|^d)",[trim]))), <<":c">> = iolist_to_binary(join(re:split("abc","(?(?=.*b).*b|^d)",[{parts, - 2}]))), - <<":c">> = iolist_to_binary(join(re:split("abc","(?(?=.*b).*b|^d)",[]))), - <<"">> = iolist_to_binary(join(re:split("%ab%","^%((?(?=[a])[^%])|b)*%$",[trim]))), + 2}]))), + <<":c">> = iolist_to_binary(join(re:split("abc","(?(?=.*b).*b|^d)",[]))), + <<"">> = iolist_to_binary(join(re:split("%ab%","^%((?(?=[a])[^%])|b)*%$",[trim]))), <<"::">> = iolist_to_binary(join(re:split("%ab%","^%((?(?=[a])[^%])|b)*%$",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("%ab%","^%((?(?=[a])[^%])|b)*%$",[]))), - <<"X:X">> = iolist_to_binary(join(re:split("XabX","(?i)a(?-i)b|c",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("%ab%","^%((?(?=[a])[^%])|b)*%$",[]))), + <<"X:X">> = iolist_to_binary(join(re:split("XabX","(?i)a(?-i)b|c",[trim]))), <<"X:X">> = iolist_to_binary(join(re:split("XabX","(?i)a(?-i)b|c",[{parts, - 2}]))), - <<"X:X">> = iolist_to_binary(join(re:split("XabX","(?i)a(?-i)b|c",[]))), - <<"X:X">> = iolist_to_binary(join(re:split("XAbX","(?i)a(?-i)b|c",[trim]))), + 2}]))), + <<"X:X">> = iolist_to_binary(join(re:split("XabX","(?i)a(?-i)b|c",[]))), + <<"X:X">> = iolist_to_binary(join(re:split("XAbX","(?i)a(?-i)b|c",[trim]))), <<"X:X">> = iolist_to_binary(join(re:split("XAbX","(?i)a(?-i)b|c",[{parts, - 2}]))), - <<"X:X">> = iolist_to_binary(join(re:split("XAbX","(?i)a(?-i)b|c",[]))), - <<"C:C">> = iolist_to_binary(join(re:split("CcC","(?i)a(?-i)b|c",[trim]))), + 2}]))), + <<"X:X">> = iolist_to_binary(join(re:split("XAbX","(?i)a(?-i)b|c",[]))), + <<"C:C">> = iolist_to_binary(join(re:split("CcC","(?i)a(?-i)b|c",[trim]))), <<"C:C">> = iolist_to_binary(join(re:split("CcC","(?i)a(?-i)b|c",[{parts, - 2}]))), - <<"C:C">> = iolist_to_binary(join(re:split("CcC","(?i)a(?-i)b|c",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?i)a(?-i)b|c",[trim]))), + 2}]))), + <<"C:C">> = iolist_to_binary(join(re:split("CcC","(?i)a(?-i)b|c",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?i)a(?-i)b|c",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?i)a(?-i)b|c",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?i)a(?-i)b|c",[]))), - <<"XABX">> = iolist_to_binary(join(re:split("XABX","(?i)a(?-i)b|c",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?i)a(?-i)b|c",[]))), + <<"XABX">> = iolist_to_binary(join(re:split("XABX","(?i)a(?-i)b|c",[trim]))), <<"XABX">> = iolist_to_binary(join(re:split("XABX","(?i)a(?-i)b|c",[{parts, - 2}]))), - <<"XABX">> = iolist_to_binary(join(re:split("XABX","(?i)a(?-i)b|c",[]))), + 2}]))), + <<"XABX">> = iolist_to_binary(join(re:split("XABX","(?i)a(?-i)b|c",[]))), <<"">> = iolist_to_binary(join(re:split(" -
","[\\x00-\\xff\\s]+",[trim]))), +
","[\\x00-\\xff\\s]+",[trim]))), <<":">> = iolist_to_binary(join(re:split(" -
","[\\x00-\\xff\\s]+",[{parts,2}]))), +
","[\\x00-\\xff\\s]+",[{parts,2}]))), <<":">> = iolist_to_binary(join(re:split(" -
","[\\x00-\\xff\\s]+",[]))), +
","[\\x00-\\xff\\s]+",[]))), <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1",[caseless, - trim]))), + trim]))), <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1",[caseless, {parts, - 2}]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1",[caseless]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1",[trim]))), + 2}]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1",[caseless]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1",[trim]))), <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1",[{parts, - 2}]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1",[]))), + 2}]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1",[]))), <<":a">> = iolist_to_binary(join(re:split("12abc","[^a]*",[caseless, - trim]))), + trim]))), <<":abc">> = iolist_to_binary(join(re:split("12abc","[^a]*",[caseless, {parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("12abc","[^a]*",[caseless]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("12abc","[^a]*",[caseless]))), <<":A">> = iolist_to_binary(join(re:split("12ABC","[^a]*",[caseless, - trim]))), + trim]))), <<":ABC">> = iolist_to_binary(join(re:split("12ABC","[^a]*",[caseless, {parts, - 2}]))), - <<":A:">> = iolist_to_binary(join(re:split("12ABC","[^a]*",[caseless]))), + 2}]))), + <<":A:">> = iolist_to_binary(join(re:split("12ABC","[^a]*",[caseless]))), <<":a">> = iolist_to_binary(join(re:split("12abc","[^a]*+",[caseless, - trim]))), + trim]))), <<":abc">> = iolist_to_binary(join(re:split("12abc","[^a]*+",[caseless, {parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("12abc","[^a]*+",[caseless]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("12abc","[^a]*+",[caseless]))), <<":A">> = iolist_to_binary(join(re:split("12ABC","[^a]*+",[caseless, - trim]))), + trim]))), <<":ABC">> = iolist_to_binary(join(re:split("12ABC","[^a]*+",[caseless, {parts, - 2}]))), - <<":A:">> = iolist_to_binary(join(re:split("12ABC","[^a]*+",[caseless]))), + 2}]))), + <<":A:">> = iolist_to_binary(join(re:split("12ABC","[^a]*+",[caseless]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","[^a]*?X",[caseless, - trim]))), + trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","[^a]*?X",[caseless, {parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","[^a]*?X",[caseless]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","[^a]*?X",[caseless]))), <<"12abc">> = iolist_to_binary(join(re:split("12abc","[^a]*?X",[caseless, - trim]))), + trim]))), <<"12abc">> = iolist_to_binary(join(re:split("12abc","[^a]*?X",[caseless, {parts, - 2}]))), - <<"12abc">> = iolist_to_binary(join(re:split("12abc","[^a]*?X",[caseless]))), + 2}]))), + <<"12abc">> = iolist_to_binary(join(re:split("12abc","[^a]*?X",[caseless]))), <<"12ABC">> = iolist_to_binary(join(re:split("12ABC","[^a]*?X",[caseless, - trim]))), + trim]))), <<"12ABC">> = iolist_to_binary(join(re:split("12ABC","[^a]*?X",[caseless, {parts, - 2}]))), - <<"12ABC">> = iolist_to_binary(join(re:split("12ABC","[^a]*?X",[caseless]))), + 2}]))), + <<"12ABC">> = iolist_to_binary(join(re:split("12ABC","[^a]*?X",[caseless]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","[^a]+?X",[caseless, - trim]))), + trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","[^a]+?X",[caseless, {parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","[^a]+?X",[caseless]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","[^a]+?X",[caseless]))), <<"12abc">> = iolist_to_binary(join(re:split("12abc","[^a]+?X",[caseless, - trim]))), + trim]))), <<"12abc">> = iolist_to_binary(join(re:split("12abc","[^a]+?X",[caseless, {parts, - 2}]))), - <<"12abc">> = iolist_to_binary(join(re:split("12abc","[^a]+?X",[caseless]))), + 2}]))), + <<"12abc">> = iolist_to_binary(join(re:split("12abc","[^a]+?X",[caseless]))), <<"12ABC">> = iolist_to_binary(join(re:split("12ABC","[^a]+?X",[caseless, - trim]))), + trim]))), <<"12ABC">> = iolist_to_binary(join(re:split("12ABC","[^a]+?X",[caseless, {parts, - 2}]))), - <<"12ABC">> = iolist_to_binary(join(re:split("12ABC","[^a]+?X",[caseless]))), + 2}]))), + <<"12ABC">> = iolist_to_binary(join(re:split("12ABC","[^a]+?X",[caseless]))), <<"12a:b">> = iolist_to_binary(join(re:split("12aXbcX","[^a]?X",[caseless, - trim]))), + trim]))), <<"12a:bcX">> = iolist_to_binary(join(re:split("12aXbcX","[^a]?X",[caseless, {parts, - 2}]))), - <<"12a:b:">> = iolist_to_binary(join(re:split("12aXbcX","[^a]?X",[caseless]))), + 2}]))), + <<"12a:b:">> = iolist_to_binary(join(re:split("12aXbcX","[^a]?X",[caseless]))), <<"12A:B">> = iolist_to_binary(join(re:split("12AXBCX","[^a]?X",[caseless, - trim]))), + trim]))), <<"12A:BCX">> = iolist_to_binary(join(re:split("12AXBCX","[^a]?X",[caseless, {parts, - 2}]))), - <<"12A:B:">> = iolist_to_binary(join(re:split("12AXBCX","[^a]?X",[caseless]))), + 2}]))), + <<"12A:B:">> = iolist_to_binary(join(re:split("12AXBCX","[^a]?X",[caseless]))), <<"B">> = iolist_to_binary(join(re:split("BCX","[^a]?X",[caseless, - trim]))), + trim]))), <<"B:">> = iolist_to_binary(join(re:split("BCX","[^a]?X",[caseless, {parts, - 2}]))), - <<"B:">> = iolist_to_binary(join(re:split("BCX","[^a]?X",[caseless]))), + 2}]))), + <<"B:">> = iolist_to_binary(join(re:split("BCX","[^a]?X",[caseless]))), <<"12a:b">> = iolist_to_binary(join(re:split("12aXbcX","[^a]??X",[caseless, - trim]))), + trim]))), <<"12a:bcX">> = iolist_to_binary(join(re:split("12aXbcX","[^a]??X",[caseless, {parts, - 2}]))), - <<"12a:b:">> = iolist_to_binary(join(re:split("12aXbcX","[^a]??X",[caseless]))), + 2}]))), + <<"12a:b:">> = iolist_to_binary(join(re:split("12aXbcX","[^a]??X",[caseless]))), <<"12A:B">> = iolist_to_binary(join(re:split("12AXBCX","[^a]??X",[caseless, - trim]))), + trim]))), <<"12A:BCX">> = iolist_to_binary(join(re:split("12AXBCX","[^a]??X",[caseless, {parts, - 2}]))), - <<"12A:B:">> = iolist_to_binary(join(re:split("12AXBCX","[^a]??X",[caseless]))), + 2}]))), + <<"12A:B:">> = iolist_to_binary(join(re:split("12AXBCX","[^a]??X",[caseless]))), <<"B">> = iolist_to_binary(join(re:split("BCX","[^a]??X",[caseless, - trim]))), + trim]))), <<"B:">> = iolist_to_binary(join(re:split("BCX","[^a]??X",[caseless, {parts, - 2}]))), - <<"B:">> = iolist_to_binary(join(re:split("BCX","[^a]??X",[caseless]))), + 2}]))), + <<"B:">> = iolist_to_binary(join(re:split("BCX","[^a]??X",[caseless]))), <<"12aXb">> = iolist_to_binary(join(re:split("12aXbcX","[^a]?+X",[caseless, - trim]))), + trim]))), <<"12aXb:">> = iolist_to_binary(join(re:split("12aXbcX","[^a]?+X",[caseless, {parts, - 2}]))), - <<"12aXb:">> = iolist_to_binary(join(re:split("12aXbcX","[^a]?+X",[caseless]))), + 2}]))), + <<"12aXb:">> = iolist_to_binary(join(re:split("12aXbcX","[^a]?+X",[caseless]))), <<"12AXB">> = iolist_to_binary(join(re:split("12AXBCX","[^a]?+X",[caseless, - trim]))), + trim]))), <<"12AXB:">> = iolist_to_binary(join(re:split("12AXBCX","[^a]?+X",[caseless, {parts, - 2}]))), - <<"12AXB:">> = iolist_to_binary(join(re:split("12AXBCX","[^a]?+X",[caseless]))), + 2}]))), + <<"12AXB:">> = iolist_to_binary(join(re:split("12AXBCX","[^a]?+X",[caseless]))), <<"B">> = iolist_to_binary(join(re:split("BCX","[^a]?+X",[caseless, - trim]))), + trim]))), <<"B:">> = iolist_to_binary(join(re:split("BCX","[^a]?+X",[caseless, {parts, - 2}]))), - <<"B:">> = iolist_to_binary(join(re:split("BCX","[^a]?+X",[caseless]))), + 2}]))), + <<"B:">> = iolist_to_binary(join(re:split("BCX","[^a]?+X",[caseless]))), <<"a">> = iolist_to_binary(join(re:split("abcdef","[^a]{2,3}",[caseless, - trim]))), + trim]))), <<"a:ef">> = iolist_to_binary(join(re:split("abcdef","[^a]{2,3}",[caseless, {parts, - 2}]))), - <<"a::">> = iolist_to_binary(join(re:split("abcdef","[^a]{2,3}",[caseless]))), + 2}]))), + <<"a::">> = iolist_to_binary(join(re:split("abcdef","[^a]{2,3}",[caseless]))), <<"A">> = iolist_to_binary(join(re:split("ABCDEF","[^a]{2,3}",[caseless, - trim]))), + trim]))), <<"A:EF">> = iolist_to_binary(join(re:split("ABCDEF","[^a]{2,3}",[caseless, {parts, - 2}]))), - <<"A::">> = iolist_to_binary(join(re:split("ABCDEF","[^a]{2,3}",[caseless]))), + 2}]))), + <<"A::">> = iolist_to_binary(join(re:split("ABCDEF","[^a]{2,3}",[caseless]))), <<"a::f">> = iolist_to_binary(join(re:split("abcdef","[^a]{2,3}?",[caseless, - trim]))), + trim]))), <<"a:def">> = iolist_to_binary(join(re:split("abcdef","[^a]{2,3}?",[caseless, {parts, - 2}]))), - <<"a::f">> = iolist_to_binary(join(re:split("abcdef","[^a]{2,3}?",[caseless]))), + 2}]))), + <<"a::f">> = iolist_to_binary(join(re:split("abcdef","[^a]{2,3}?",[caseless]))), <<"A::F">> = iolist_to_binary(join(re:split("ABCDEF","[^a]{2,3}?",[caseless, - trim]))), + trim]))), <<"A:DEF">> = iolist_to_binary(join(re:split("ABCDEF","[^a]{2,3}?",[caseless, {parts, - 2}]))), - <<"A::F">> = iolist_to_binary(join(re:split("ABCDEF","[^a]{2,3}?",[caseless]))), + 2}]))), + <<"A::F">> = iolist_to_binary(join(re:split("ABCDEF","[^a]{2,3}?",[caseless]))), <<"a">> = iolist_to_binary(join(re:split("abcdef","[^a]{2,3}+",[caseless, - trim]))), + trim]))), <<"a:ef">> = iolist_to_binary(join(re:split("abcdef","[^a]{2,3}+",[caseless, {parts, - 2}]))), - <<"a::">> = iolist_to_binary(join(re:split("abcdef","[^a]{2,3}+",[caseless]))), + 2}]))), + <<"a::">> = iolist_to_binary(join(re:split("abcdef","[^a]{2,3}+",[caseless]))), <<"A">> = iolist_to_binary(join(re:split("ABCDEF","[^a]{2,3}+",[caseless, - trim]))), + trim]))), <<"A:EF">> = iolist_to_binary(join(re:split("ABCDEF","[^a]{2,3}+",[caseless, {parts, - 2}]))), - <<"A::">> = iolist_to_binary(join(re:split("ABCDEF","[^a]{2,3}+",[caseless]))), - <<"">> = iolist_to_binary(join(re:split("Z","((a|)+)+Z",[trim]))), + 2}]))), + <<"A::">> = iolist_to_binary(join(re:split("ABCDEF","[^a]{2,3}+",[caseless]))), + <<"">> = iolist_to_binary(join(re:split("Z","((a|)+)+Z",[trim]))), <<":::">> = iolist_to_binary(join(re:split("Z","((a|)+)+Z",[{parts, - 2}]))), - <<":::">> = iolist_to_binary(join(re:split("Z","((a|)+)+Z",[]))), + 2}]))), + <<":::">> = iolist_to_binary(join(re:split("Z","((a|)+)+Z",[]))), ok. run38() -> - <<"::a">> = iolist_to_binary(join(re:split("ac","(a)b|(a)c",[trim]))), + <<"::a">> = iolist_to_binary(join(re:split("ac","(a)b|(a)c",[trim]))), <<"::a:">> = iolist_to_binary(join(re:split("ac","(a)b|(a)c",[{parts, - 2}]))), - <<"::a:">> = iolist_to_binary(join(re:split("ac","(a)b|(a)c",[]))), - <<"::a">> = iolist_to_binary(join(re:split("ac","(?>(a))b|(a)c",[trim]))), + 2}]))), + <<"::a:">> = iolist_to_binary(join(re:split("ac","(a)b|(a)c",[]))), + <<"::a">> = iolist_to_binary(join(re:split("ac","(?>(a))b|(a)c",[trim]))), <<"::a:">> = iolist_to_binary(join(re:split("ac","(?>(a))b|(a)c",[{parts, - 2}]))), - <<"::a:">> = iolist_to_binary(join(re:split("ac","(?>(a))b|(a)c",[]))), - <<"::a">> = iolist_to_binary(join(re:split("ac","(?=(a))ab|(a)c",[trim]))), + 2}]))), + <<"::a:">> = iolist_to_binary(join(re:split("ac","(?>(a))b|(a)c",[]))), + <<"::a">> = iolist_to_binary(join(re:split("ac","(?=(a))ab|(a)c",[trim]))), <<"::a:">> = iolist_to_binary(join(re:split("ac","(?=(a))ab|(a)c",[{parts, - 2}]))), - <<"::a:">> = iolist_to_binary(join(re:split("ac","(?=(a))ab|(a)c",[]))), - <<":ac::a">> = iolist_to_binary(join(re:split("ac","((?>(a))b|(a)c)",[trim]))), + 2}]))), + <<"::a:">> = iolist_to_binary(join(re:split("ac","(?=(a))ab|(a)c",[]))), + <<":ac::a">> = iolist_to_binary(join(re:split("ac","((?>(a))b|(a)c)",[trim]))), <<":ac::a:">> = iolist_to_binary(join(re:split("ac","((?>(a))b|(a)c)",[{parts, - 2}]))), - <<":ac::a:">> = iolist_to_binary(join(re:split("ac","((?>(a))b|(a)c)",[]))), - <<":ac::a">> = iolist_to_binary(join(re:split("ac","((?>(a))b|(a)c)++",[trim]))), + 2}]))), + <<":ac::a:">> = iolist_to_binary(join(re:split("ac","((?>(a))b|(a)c)",[]))), + <<":ac::a">> = iolist_to_binary(join(re:split("ac","((?>(a))b|(a)c)++",[trim]))), <<":ac::a:">> = iolist_to_binary(join(re:split("ac","((?>(a))b|(a)c)++",[{parts, - 2}]))), - <<":ac::a:">> = iolist_to_binary(join(re:split("ac","((?>(a))b|(a)c)++",[]))), - <<"::a">> = iolist_to_binary(join(re:split("ac","(?:(?>(a))b|(a)c)++",[trim]))), + 2}]))), + <<":ac::a:">> = iolist_to_binary(join(re:split("ac","((?>(a))b|(a)c)++",[]))), + <<"::a">> = iolist_to_binary(join(re:split("ac","(?:(?>(a))b|(a)c)++",[trim]))), <<"::a:">> = iolist_to_binary(join(re:split("ac","(?:(?>(a))b|(a)c)++",[{parts, - 2}]))), - <<"::a:">> = iolist_to_binary(join(re:split("ac","(?:(?>(a))b|(a)c)++",[]))), - <<"::a:ac">> = iolist_to_binary(join(re:split("ac","(?=(?>(a))b|(a)c)(..)",[trim]))), + 2}]))), + <<"::a:">> = iolist_to_binary(join(re:split("ac","(?:(?>(a))b|(a)c)++",[]))), + <<"::a:ac">> = iolist_to_binary(join(re:split("ac","(?=(?>(a))b|(a)c)(..)",[trim]))), <<"::a:ac:">> = iolist_to_binary(join(re:split("ac","(?=(?>(a))b|(a)c)(..)",[{parts, - 2}]))), - <<"::a:ac:">> = iolist_to_binary(join(re:split("ac","(?=(?>(a))b|(a)c)(..)",[]))), - <<"::a">> = iolist_to_binary(join(re:split("ac","(?>(?>(a))b|(a)c)",[trim]))), + 2}]))), + <<"::a:ac:">> = iolist_to_binary(join(re:split("ac","(?=(?>(a))b|(a)c)(..)",[]))), + <<"::a">> = iolist_to_binary(join(re:split("ac","(?>(?>(a))b|(a)c)",[trim]))), <<"::a:">> = iolist_to_binary(join(re:split("ac","(?>(?>(a))b|(a)c)",[{parts, - 2}]))), - <<"::a:">> = iolist_to_binary(join(re:split("ac","(?>(?>(a))b|(a)c)",[]))), - <<":aaaabaaabaabab:aaa:aabab">> = iolist_to_binary(join(re:split("aaaabaaabaabab","((?>(a+)b)+(aabab))",[trim]))), + 2}]))), + <<"::a:">> = iolist_to_binary(join(re:split("ac","(?>(?>(a))b|(a)c)",[]))), + <<":aaaabaaabaabab:aaa:aabab">> = iolist_to_binary(join(re:split("aaaabaaabaabab","((?>(a+)b)+(aabab))",[trim]))), <<":aaaabaaabaabab:aaa:aabab:">> = iolist_to_binary(join(re:split("aaaabaaabaabab","((?>(a+)b)+(aabab))",[{parts, - 2}]))), - <<":aaaabaaabaabab:aaa:aabab:">> = iolist_to_binary(join(re:split("aaaabaaabaabab","((?>(a+)b)+(aabab))",[]))), - <<"aabc">> = iolist_to_binary(join(re:split("aabc","(?>a+|ab)+?c",[trim]))), + 2}]))), + <<":aaaabaaabaabab:aaa:aabab:">> = iolist_to_binary(join(re:split("aaaabaaabaabab","((?>(a+)b)+(aabab))",[]))), + <<"aabc">> = iolist_to_binary(join(re:split("aabc","(?>a+|ab)+?c",[trim]))), <<"aabc">> = iolist_to_binary(join(re:split("aabc","(?>a+|ab)+?c",[{parts, - 2}]))), - <<"aabc">> = iolist_to_binary(join(re:split("aabc","(?>a+|ab)+?c",[]))), - <<"aabc">> = iolist_to_binary(join(re:split("aabc","(?>a+|ab)+c",[trim]))), + 2}]))), + <<"aabc">> = iolist_to_binary(join(re:split("aabc","(?>a+|ab)+?c",[]))), + <<"aabc">> = iolist_to_binary(join(re:split("aabc","(?>a+|ab)+c",[trim]))), <<"aabc">> = iolist_to_binary(join(re:split("aabc","(?>a+|ab)+c",[{parts, - 2}]))), - <<"aabc">> = iolist_to_binary(join(re:split("aabc","(?>a+|ab)+c",[]))), - <<"">> = iolist_to_binary(join(re:split("aabc","(?:a+|ab)+c",[trim]))), + 2}]))), + <<"aabc">> = iolist_to_binary(join(re:split("aabc","(?>a+|ab)+c",[]))), + <<"">> = iolist_to_binary(join(re:split("aabc","(?:a+|ab)+c",[trim]))), <<":">> = iolist_to_binary(join(re:split("aabc","(?:a+|ab)+c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aabc","(?:a+|ab)+c",[]))), - <<":a">> = iolist_to_binary(join(re:split("a","(?(?=(a))a)",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aabc","(?:a+|ab)+c",[]))), + <<":a">> = iolist_to_binary(join(re:split("a","(?(?=(a))a)",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("a","(?(?=(a))a)",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("a","(?(?=(a))a)",[]))), - <<":a:b">> = iolist_to_binary(join(re:split("ab","(?(?=(a))a)(b)",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("a","(?(?=(a))a)",[]))), + <<":a:b">> = iolist_to_binary(join(re:split("ab","(?(?=(a))a)(b)",[trim]))), <<":a:b:">> = iolist_to_binary(join(re:split("ab","(?(?=(a))a)(b)",[{parts, - 2}]))), - <<":a:b:">> = iolist_to_binary(join(re:split("ab","(?(?=(a))a)(b)",[]))), - <<"aaaabc">> = iolist_to_binary(join(re:split("aaaabc","^(?:a|ab)++c",[trim]))), + 2}]))), + <<":a:b:">> = iolist_to_binary(join(re:split("ab","(?(?=(a))a)(b)",[]))), + <<"aaaabc">> = iolist_to_binary(join(re:split("aaaabc","^(?:a|ab)++c",[trim]))), <<"aaaabc">> = iolist_to_binary(join(re:split("aaaabc","^(?:a|ab)++c",[{parts, - 2}]))), - <<"aaaabc">> = iolist_to_binary(join(re:split("aaaabc","^(?:a|ab)++c",[]))), - <<"aaaabc">> = iolist_to_binary(join(re:split("aaaabc","^(?>a|ab)++c",[trim]))), + 2}]))), + <<"aaaabc">> = iolist_to_binary(join(re:split("aaaabc","^(?:a|ab)++c",[]))), + <<"aaaabc">> = iolist_to_binary(join(re:split("aaaabc","^(?>a|ab)++c",[trim]))), <<"aaaabc">> = iolist_to_binary(join(re:split("aaaabc","^(?>a|ab)++c",[{parts, - 2}]))), - <<"aaaabc">> = iolist_to_binary(join(re:split("aaaabc","^(?>a|ab)++c",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaabc","^(?:a|ab)+c",[trim]))), + 2}]))), + <<"aaaabc">> = iolist_to_binary(join(re:split("aaaabc","^(?>a|ab)++c",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaabc","^(?:a|ab)+c",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaabc","^(?:a|ab)+c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaabc","^(?:a|ab)+c",[]))), - <<"">> = iolist_to_binary(join(re:split("xyz","(?=abc){0}xyz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaabc","^(?:a|ab)+c",[]))), + <<"">> = iolist_to_binary(join(re:split("xyz","(?=abc){0}xyz",[trim]))), <<":">> = iolist_to_binary(join(re:split("xyz","(?=abc){0}xyz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("xyz","(?=abc){0}xyz",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?=abc){1}xyz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("xyz","(?=abc){0}xyz",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?=abc){1}xyz",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?=abc){1}xyz",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?=abc){1}xyz",[]))), - <<"xyz">> = iolist_to_binary(join(re:split("xyz","(?=abc){1}xyz",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?=abc){1}xyz",[]))), + <<"xyz">> = iolist_to_binary(join(re:split("xyz","(?=abc){1}xyz",[trim]))), <<"xyz">> = iolist_to_binary(join(re:split("xyz","(?=abc){1}xyz",[{parts, - 2}]))), - <<"xyz">> = iolist_to_binary(join(re:split("xyz","(?=abc){1}xyz",[]))), - <<":a">> = iolist_to_binary(join(re:split("ab","(?=(a))?.",[trim]))), + 2}]))), + <<"xyz">> = iolist_to_binary(join(re:split("xyz","(?=abc){1}xyz",[]))), + <<":a">> = iolist_to_binary(join(re:split("ab","(?=(a))?.",[trim]))), <<":a:b">> = iolist_to_binary(join(re:split("ab","(?=(a))?.",[{parts, - 2}]))), - <<":a:::">> = iolist_to_binary(join(re:split("ab","(?=(a))?.",[]))), - <<"">> = iolist_to_binary(join(re:split("bc","(?=(a))?.",[trim]))), + 2}]))), + <<":a:::">> = iolist_to_binary(join(re:split("ab","(?=(a))?.",[]))), + <<"">> = iolist_to_binary(join(re:split("bc","(?=(a))?.",[trim]))), <<"::c">> = iolist_to_binary(join(re:split("bc","(?=(a))?.",[{parts, - 2}]))), - <<"::::">> = iolist_to_binary(join(re:split("bc","(?=(a))?.",[]))), + 2}]))), + <<"::::">> = iolist_to_binary(join(re:split("bc","(?=(a))?.",[]))), ok. run39() -> - <<"">> = iolist_to_binary(join(re:split("ab","(?=(a))??.",[trim]))), + <<"">> = iolist_to_binary(join(re:split("ab","(?=(a))??.",[trim]))), <<"::b">> = iolist_to_binary(join(re:split("ab","(?=(a))??.",[{parts, - 2}]))), - <<"::::">> = iolist_to_binary(join(re:split("ab","(?=(a))??.",[]))), - <<"">> = iolist_to_binary(join(re:split("bc","(?=(a))??.",[trim]))), + 2}]))), + <<"::::">> = iolist_to_binary(join(re:split("ab","(?=(a))??.",[]))), + <<"">> = iolist_to_binary(join(re:split("bc","(?=(a))??.",[trim]))), <<"::c">> = iolist_to_binary(join(re:split("bc","(?=(a))??.",[{parts, - 2}]))), - <<"::::">> = iolist_to_binary(join(re:split("bc","(?=(a))??.",[]))), - <<":b">> = iolist_to_binary(join(re:split("abd","^(?=(?1))?[az]([abc])d",[trim]))), + 2}]))), + <<"::::">> = iolist_to_binary(join(re:split("bc","(?=(a))??.",[]))), + <<":b">> = iolist_to_binary(join(re:split("abd","^(?=(?1))?[az]([abc])d",[trim]))), <<":b:">> = iolist_to_binary(join(re:split("abd","^(?=(?1))?[az]([abc])d",[{parts, - 2}]))), - <<":b:">> = iolist_to_binary(join(re:split("abd","^(?=(?1))?[az]([abc])d",[]))), - <<":c:xx">> = iolist_to_binary(join(re:split("zcdxx","^(?=(?1))?[az]([abc])d",[trim]))), + 2}]))), + <<":b:">> = iolist_to_binary(join(re:split("abd","^(?=(?1))?[az]([abc])d",[]))), + <<":c:xx">> = iolist_to_binary(join(re:split("zcdxx","^(?=(?1))?[az]([abc])d",[trim]))), <<":c:xx">> = iolist_to_binary(join(re:split("zcdxx","^(?=(?1))?[az]([abc])d",[{parts, - 2}]))), - <<":c:xx">> = iolist_to_binary(join(re:split("zcdxx","^(?=(?1))?[az]([abc])d",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaaa","^(?!a){0}\\w+",[trim]))), + 2}]))), + <<":c:xx">> = iolist_to_binary(join(re:split("zcdxx","^(?=(?1))?[az]([abc])d",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaaa","^(?!a){0}\\w+",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaaa","^(?!a){0}\\w+",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaaa","^(?!a){0}\\w+",[]))), - <<"abc:abc">> = iolist_to_binary(join(re:split("abcxyz","(?<=(abc))?xyz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaaa","^(?!a){0}\\w+",[]))), + <<"abc:abc">> = iolist_to_binary(join(re:split("abcxyz","(?<=(abc))?xyz",[trim]))), <<"abc:abc:">> = iolist_to_binary(join(re:split("abcxyz","(?<=(abc))?xyz",[{parts, - 2}]))), - <<"abc:abc:">> = iolist_to_binary(join(re:split("abcxyz","(?<=(abc))?xyz",[]))), - <<"pqr">> = iolist_to_binary(join(re:split("pqrxyz","(?<=(abc))?xyz",[trim]))), + 2}]))), + <<"abc:abc:">> = iolist_to_binary(join(re:split("abcxyz","(?<=(abc))?xyz",[]))), + <<"pqr">> = iolist_to_binary(join(re:split("pqrxyz","(?<=(abc))?xyz",[trim]))), <<"pqr::">> = iolist_to_binary(join(re:split("pqrxyz","(?<=(abc))?xyz",[{parts, - 2}]))), - <<"pqr::">> = iolist_to_binary(join(re:split("pqrxyz","(?<=(abc))?xyz",[]))), - <<"">> = iolist_to_binary(join(re:split("ggg<<<aaa>>>","^[\\g<a>]+",[trim]))), + 2}]))), + <<"pqr::">> = iolist_to_binary(join(re:split("pqrxyz","(?<=(abc))?xyz",[]))), + <<"">> = iolist_to_binary(join(re:split("ggg<<<aaa>>>","^[\\g<a>]+",[trim]))), <<":">> = iolist_to_binary(join(re:split("ggg<<<aaa>>>","^[\\g<a>]+",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ggg<<<aaa>>>","^[\\g<a>]+",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[\\g<a>]+",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ggg<<<aaa>>>","^[\\g<a>]+",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[\\g<a>]+",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[\\g<a>]+",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[\\g<a>]+",[]))), - <<"\\ga">> = iolist_to_binary(join(re:split("\\ga","^[\\g<a>]+",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[\\g<a>]+",[]))), + <<"\\ga">> = iolist_to_binary(join(re:split("\\ga","^[\\g<a>]+",[trim]))), <<"\\ga">> = iolist_to_binary(join(re:split("\\ga","^[\\g<a>]+",[{parts, - 2}]))), - <<"\\ga">> = iolist_to_binary(join(re:split("\\ga","^[\\g<a>]+",[]))), - <<":xyz">> = iolist_to_binary(join(re:split("gggagagaxyz","^[\\ga]+",[trim]))), + 2}]))), + <<"\\ga">> = iolist_to_binary(join(re:split("\\ga","^[\\g<a>]+",[]))), + <<":xyz">> = iolist_to_binary(join(re:split("gggagagaxyz","^[\\ga]+",[trim]))), <<":xyz">> = iolist_to_binary(join(re:split("gggagagaxyz","^[\\ga]+",[{parts, - 2}]))), - <<":xyz">> = iolist_to_binary(join(re:split("gggagagaxyz","^[\\ga]+",[]))), - <<":Z">> = iolist_to_binary(join(re:split("aaaa444:::Z","^[:a[:digit:]]+",[trim]))), + 2}]))), + <<":xyz">> = iolist_to_binary(join(re:split("gggagagaxyz","^[\\ga]+",[]))), + <<":Z">> = iolist_to_binary(join(re:split("aaaa444:::Z","^[:a[:digit:]]+",[trim]))), <<":Z">> = iolist_to_binary(join(re:split("aaaa444:::Z","^[:a[:digit:]]+",[{parts, - 2}]))), - <<":Z">> = iolist_to_binary(join(re:split("aaaa444:::Z","^[:a[:digit:]]+",[]))), - <<":Z">> = iolist_to_binary(join(re:split("aaaa444:::bbbZ","^[:a[:digit:]:b]+",[trim]))), + 2}]))), + <<":Z">> = iolist_to_binary(join(re:split("aaaa444:::Z","^[:a[:digit:]]+",[]))), + <<":Z">> = iolist_to_binary(join(re:split("aaaa444:::bbbZ","^[:a[:digit:]:b]+",[trim]))), <<":Z">> = iolist_to_binary(join(re:split("aaaa444:::bbbZ","^[:a[:digit:]:b]+",[{parts, - 2}]))), - <<":Z">> = iolist_to_binary(join(re:split("aaaa444:::bbbZ","^[:a[:digit:]:b]+",[]))), - <<"">> = iolist_to_binary(join(re:split(":xxx:","[:a]xxx[b:]",[trim]))), + 2}]))), + <<":Z">> = iolist_to_binary(join(re:split("aaaa444:::bbbZ","^[:a[:digit:]:b]+",[]))), + <<"">> = iolist_to_binary(join(re:split(":xxx:","[:a]xxx[b:]",[trim]))), <<":">> = iolist_to_binary(join(re:split(":xxx:","[:a]xxx[b:]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split(":xxx:","[:a]xxx[b:]",[]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split(":xxx:","[:a]xxx[b:]",[]))), <<"xaa:c">> = iolist_to_binary(join(re:split("xaabc","(?<=a{2})b",[caseless, - trim]))), + trim]))), <<"xaa:c">> = iolist_to_binary(join(re:split("xaabc","(?<=a{2})b",[caseless, {parts, - 2}]))), - <<"xaa:c">> = iolist_to_binary(join(re:split("xaabc","(?<=a{2})b",[caseless]))), + 2}]))), + <<"xaa:c">> = iolist_to_binary(join(re:split("xaabc","(?<=a{2})b",[caseless]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=a{2})b",[caseless, - trim]))), + trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=a{2})b",[caseless, {parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=a{2})b",[caseless]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=a{2})b",[caseless]))), <<"xabc">> = iolist_to_binary(join(re:split("xabc","(?<=a{2})b",[caseless, - trim]))), + trim]))), <<"xabc">> = iolist_to_binary(join(re:split("xabc","(?<=a{2})b",[caseless, {parts, - 2}]))), - <<"xabc">> = iolist_to_binary(join(re:split("xabc","(?<=a{2})b",[caseless]))), + 2}]))), + <<"xabc">> = iolist_to_binary(join(re:split("xabc","(?<=a{2})b",[caseless]))), <<"xa:c">> = iolist_to_binary(join(re:split("xabc","(?<!a{2})b",[caseless, - trim]))), + trim]))), <<"xa:c">> = iolist_to_binary(join(re:split("xabc","(?<!a{2})b",[caseless, {parts, - 2}]))), - <<"xa:c">> = iolist_to_binary(join(re:split("xabc","(?<!a{2})b",[caseless]))), + 2}]))), + <<"xa:c">> = iolist_to_binary(join(re:split("xabc","(?<!a{2})b",[caseless]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<!a{2})b",[caseless, - trim]))), + trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<!a{2})b",[caseless, {parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<!a{2})b",[caseless]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<!a{2})b",[caseless]))), <<"xaabc">> = iolist_to_binary(join(re:split("xaabc","(?<!a{2})b",[caseless, - trim]))), + trim]))), <<"xaabc">> = iolist_to_binary(join(re:split("xaabc","(?<!a{2})b",[caseless, {parts, - 2}]))), - <<"xaabc">> = iolist_to_binary(join(re:split("xaabc","(?<!a{2})b",[caseless]))), - <<"xa ">> = iolist_to_binary(join(re:split("xa c","(?<=a\\h)c",[trim]))), + 2}]))), + <<"xaabc">> = iolist_to_binary(join(re:split("xaabc","(?<!a{2})b",[caseless]))), + <<"xa ">> = iolist_to_binary(join(re:split("xa c","(?<=a\\h)c",[trim]))), <<"xa :">> = iolist_to_binary(join(re:split("xa c","(?<=a\\h)c",[{parts, - 2}]))), - <<"xa :">> = iolist_to_binary(join(re:split("xa c","(?<=a\\h)c",[]))), - <<"axx:c">> = iolist_to_binary(join(re:split("axxbc","(?<=[^a]{2})b",[trim]))), + 2}]))), + <<"xa :">> = iolist_to_binary(join(re:split("xa c","(?<=a\\h)c",[]))), + <<"axx:c">> = iolist_to_binary(join(re:split("axxbc","(?<=[^a]{2})b",[trim]))), <<"axx:c">> = iolist_to_binary(join(re:split("axxbc","(?<=[^a]{2})b",[{parts, - 2}]))), - <<"axx:c">> = iolist_to_binary(join(re:split("axxbc","(?<=[^a]{2})b",[]))), - <<"aAA:c">> = iolist_to_binary(join(re:split("aAAbc","(?<=[^a]{2})b",[trim]))), + 2}]))), + <<"axx:c">> = iolist_to_binary(join(re:split("axxbc","(?<=[^a]{2})b",[]))), + <<"aAA:c">> = iolist_to_binary(join(re:split("aAAbc","(?<=[^a]{2})b",[trim]))), <<"aAA:c">> = iolist_to_binary(join(re:split("aAAbc","(?<=[^a]{2})b",[{parts, - 2}]))), - <<"aAA:c">> = iolist_to_binary(join(re:split("aAAbc","(?<=[^a]{2})b",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=[^a]{2})b",[trim]))), + 2}]))), + <<"aAA:c">> = iolist_to_binary(join(re:split("aAAbc","(?<=[^a]{2})b",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=[^a]{2})b",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=[^a]{2})b",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=[^a]{2})b",[]))), - <<"xaabc">> = iolist_to_binary(join(re:split("xaabc","(?<=[^a]{2})b",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=[^a]{2})b",[]))), + <<"xaabc">> = iolist_to_binary(join(re:split("xaabc","(?<=[^a]{2})b",[trim]))), <<"xaabc">> = iolist_to_binary(join(re:split("xaabc","(?<=[^a]{2})b",[{parts, - 2}]))), - <<"xaabc">> = iolist_to_binary(join(re:split("xaabc","(?<=[^a]{2})b",[]))), + 2}]))), + <<"xaabc">> = iolist_to_binary(join(re:split("xaabc","(?<=[^a]{2})b",[]))), <<"axx:c">> = iolist_to_binary(join(re:split("axxbc","(?<=[^a]{2})b",[caseless, - trim]))), + trim]))), <<"axx:c">> = iolist_to_binary(join(re:split("axxbc","(?<=[^a]{2})b",[caseless, {parts, - 2}]))), - <<"axx:c">> = iolist_to_binary(join(re:split("axxbc","(?<=[^a]{2})b",[caseless]))), + 2}]))), + <<"axx:c">> = iolist_to_binary(join(re:split("axxbc","(?<=[^a]{2})b",[caseless]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=[^a]{2})b",[caseless, - trim]))), + trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=[^a]{2})b",[caseless, {parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=[^a]{2})b",[caseless]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=[^a]{2})b",[caseless]))), <<"aAAbc">> = iolist_to_binary(join(re:split("aAAbc","(?<=[^a]{2})b",[caseless, - trim]))), + trim]))), <<"aAAbc">> = iolist_to_binary(join(re:split("aAAbc","(?<=[^a]{2})b",[caseless, {parts, - 2}]))), - <<"aAAbc">> = iolist_to_binary(join(re:split("aAAbc","(?<=[^a]{2})b",[caseless]))), + 2}]))), + <<"aAAbc">> = iolist_to_binary(join(re:split("aAAbc","(?<=[^a]{2})b",[caseless]))), <<"xaabc">> = iolist_to_binary(join(re:split("xaabc","(?<=[^a]{2})b",[caseless, - trim]))), + trim]))), <<"xaabc">> = iolist_to_binary(join(re:split("xaabc","(?<=[^a]{2})b",[caseless, {parts, - 2}]))), - <<"xaabc">> = iolist_to_binary(join(re:split("xaabc","(?<=[^a]{2})b",[caseless]))), - <<"ab">> = iolist_to_binary(join(re:split("abc","(?<=a\\H)c",[trim]))), + 2}]))), + <<"xaabc">> = iolist_to_binary(join(re:split("xaabc","(?<=[^a]{2})b",[caseless]))), + <<"ab">> = iolist_to_binary(join(re:split("abc","(?<=a\\H)c",[trim]))), <<"ab:">> = iolist_to_binary(join(re:split("abc","(?<=a\\H)c",[{parts, - 2}]))), - <<"ab:">> = iolist_to_binary(join(re:split("abc","(?<=a\\H)c",[]))), - <<"ab">> = iolist_to_binary(join(re:split("abc","(?<=a\\V)c",[trim]))), + 2}]))), + <<"ab:">> = iolist_to_binary(join(re:split("abc","(?<=a\\H)c",[]))), + <<"ab">> = iolist_to_binary(join(re:split("abc","(?<=a\\V)c",[trim]))), <<"ab:">> = iolist_to_binary(join(re:split("abc","(?<=a\\V)c",[{parts, - 2}]))), - <<"ab:">> = iolist_to_binary(join(re:split("abc","(?<=a\\V)c",[]))), + 2}]))), + <<"ab:">> = iolist_to_binary(join(re:split("abc","(?<=a\\V)c",[]))), <<"a ">> = iolist_to_binary(join(re:split("a -c","(?<=a\\v)c",[trim]))), +c","(?<=a\\v)c",[trim]))), <<"a :">> = iolist_to_binary(join(re:split("a -c","(?<=a\\v)c",[{parts,2}]))), +c","(?<=a\\v)c",[{parts,2}]))), <<"a :">> = iolist_to_binary(join(re:split("a -c","(?<=a\\v)c",[]))), - <<"X:X">> = iolist_to_binary(join(re:split("XcccddYX","(?(?=c)c|d)++Y",[trim]))), +c","(?<=a\\v)c",[]))), + <<"X:X">> = iolist_to_binary(join(re:split("XcccddYX","(?(?=c)c|d)++Y",[trim]))), <<"X:X">> = iolist_to_binary(join(re:split("XcccddYX","(?(?=c)c|d)++Y",[{parts, - 2}]))), - <<"X:X">> = iolist_to_binary(join(re:split("XcccddYX","(?(?=c)c|d)++Y",[]))), - <<"X:X">> = iolist_to_binary(join(re:split("XcccddYX","(?(?=c)c|d)*+Y",[trim]))), + 2}]))), + <<"X:X">> = iolist_to_binary(join(re:split("XcccddYX","(?(?=c)c|d)++Y",[]))), + <<"X:X">> = iolist_to_binary(join(re:split("XcccddYX","(?(?=c)c|d)*+Y",[trim]))), <<"X:X">> = iolist_to_binary(join(re:split("XcccddYX","(?(?=c)c|d)*+Y",[{parts, - 2}]))), - <<"X:X">> = iolist_to_binary(join(re:split("XcccddYX","(?(?=c)c|d)*+Y",[]))), - <<":aaa">> = iolist_to_binary(join(re:split("aaaaaaa","^(a{2,3}){2,}+a",[trim]))), + 2}]))), + <<"X:X">> = iolist_to_binary(join(re:split("XcccddYX","(?(?=c)c|d)*+Y",[]))), + <<":aaa">> = iolist_to_binary(join(re:split("aaaaaaa","^(a{2,3}){2,}+a",[trim]))), <<":aaa:">> = iolist_to_binary(join(re:split("aaaaaaa","^(a{2,3}){2,}+a",[{parts, - 2}]))), - <<":aaa:">> = iolist_to_binary(join(re:split("aaaaaaa","^(a{2,3}){2,}+a",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a{2,3}){2,}+a",[trim]))), + 2}]))), + <<":aaa:">> = iolist_to_binary(join(re:split("aaaaaaa","^(a{2,3}){2,}+a",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a{2,3}){2,}+a",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a{2,3}){2,}+a",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a{2,3}){2,}+a",[]))), - <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^(a{2,3}){2,}+a",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a{2,3}){2,}+a",[]))), + <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^(a{2,3}){2,}+a",[trim]))), <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^(a{2,3}){2,}+a",[{parts, - 2}]))), - <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^(a{2,3}){2,}+a",[]))), - <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a{2,3}){2,}+a",[trim]))), + 2}]))), + <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^(a{2,3}){2,}+a",[]))), + <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a{2,3}){2,}+a",[trim]))), <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a{2,3}){2,}+a",[{parts, - 2}]))), - <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a{2,3}){2,}+a",[]))), + 2}]))), + <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a{2,3}){2,}+a",[]))), ok. run40() -> - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a{2,3})++a",[trim]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a{2,3})++a",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a{2,3})++a",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a{2,3})++a",[]))), - <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^(a{2,3})++a",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a{2,3})++a",[]))), + <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^(a{2,3})++a",[trim]))), <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^(a{2,3})++a",[{parts, - 2}]))), - <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^(a{2,3})++a",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a{2,3})*+a",[trim]))), + 2}]))), + <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^(a{2,3})++a",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a{2,3})*+a",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a{2,3})*+a",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a{2,3})*+a",[]))), - <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^(a{2,3})*+a",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a{2,3})*+a",[]))), + <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^(a{2,3})*+a",[trim]))), <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^(a{2,3})*+a",[{parts, - 2}]))), - <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^(a{2,3})*+a",[]))), - <<"">> = iolist_to_binary(join(re:split("abXde","ab\\Cde",[trim]))), + 2}]))), + <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^(a{2,3})*+a",[]))), + <<"">> = iolist_to_binary(join(re:split("abXde","ab\\Cde",[trim]))), <<":">> = iolist_to_binary(join(re:split("abXde","ab\\Cde",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abXde","ab\\Cde",[]))), - <<"abZde">> = iolist_to_binary(join(re:split("abZdeX","(?<=ab\\Cde)X",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abXde","ab\\Cde",[]))), + <<"abZde">> = iolist_to_binary(join(re:split("abZdeX","(?<=ab\\Cde)X",[trim]))), <<"abZde:">> = iolist_to_binary(join(re:split("abZdeX","(?<=ab\\Cde)X",[{parts, - 2}]))), - <<"abZde:">> = iolist_to_binary(join(re:split("abZdeX","(?<=ab\\Cde)X",[]))), - <<"">> = iolist_to_binary(join(re:split("aCb","a[\\CD]b",[trim]))), + 2}]))), + <<"abZde:">> = iolist_to_binary(join(re:split("abZdeX","(?<=ab\\Cde)X",[]))), + <<"">> = iolist_to_binary(join(re:split("aCb","a[\\CD]b",[trim]))), <<":">> = iolist_to_binary(join(re:split("aCb","a[\\CD]b",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aCb","a[\\CD]b",[]))), - <<"">> = iolist_to_binary(join(re:split("aDb","a[\\CD]b",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aCb","a[\\CD]b",[]))), + <<"">> = iolist_to_binary(join(re:split("aDb","a[\\CD]b",[trim]))), <<":">> = iolist_to_binary(join(re:split("aDb","a[\\CD]b",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aDb","a[\\CD]b",[]))), - <<"">> = iolist_to_binary(join(re:split("aJb","a[\\C-X]b",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aDb","a[\\CD]b",[]))), + <<"">> = iolist_to_binary(join(re:split("aJb","a[\\C-X]b",[trim]))), <<":">> = iolist_to_binary(join(re:split("aJb","a[\\C-X]b",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aJb","a[\\C-X]b",[]))), - <<"X X">> = iolist_to_binary(join(re:split("X X","\\H\\h\\V\\v",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aJb","a[\\C-X]b",[]))), + <<"X X">> = iolist_to_binary(join(re:split("X X","\\H\\h\\V\\v",[trim]))), <<"X X">> = iolist_to_binary(join(re:split("X X","\\H\\h\\V\\v",[{parts, - 2}]))), - <<"X X">> = iolist_to_binary(join(re:split("X X","\\H\\h\\V\\v",[]))), - <<"">> = iolist_to_binary(join(re:split("X X","\\H\\h\\V\\v",[trim]))), + 2}]))), + <<"X X">> = iolist_to_binary(join(re:split("X X","\\H\\h\\V\\v",[]))), + <<"">> = iolist_to_binary(join(re:split("X X","\\H\\h\\V\\v",[trim]))), <<":">> = iolist_to_binary(join(re:split("X X","\\H\\h\\V\\v",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("X X","\\H\\h\\V\\v",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","\\H\\h\\V\\v",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("X X","\\H\\h\\V\\v",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","\\H\\h\\V\\v",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","\\H\\h\\V\\v",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","\\H\\h\\V\\v",[]))), - <<" X">> = iolist_to_binary(join(re:split(" X","\\H\\h\\V\\v",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","\\H\\h\\V\\v",[]))), + <<" X">> = iolist_to_binary(join(re:split(" X","\\H\\h\\V\\v",[trim]))), <<" X">> = iolist_to_binary(join(re:split(" X","\\H\\h\\V\\v",[{parts, - 2}]))), - <<" X">> = iolist_to_binary(join(re:split(" X","\\H\\h\\V\\v",[]))), + 2}]))), + <<" X">> = iolist_to_binary(join(re:split(" X","\\H\\h\\V\\v",[]))), <<"">> = iolist_to_binary(join(re:split(" X -
","\\H*\\h+\\V?\\v{3,4}",[trim]))), +
","\\H*\\h+\\V?\\v{3,4}",[trim]))), <<":">> = iolist_to_binary(join(re:split(" X -
","\\H*\\h+\\V?\\v{3,4}",[{parts,2}]))), +
","\\H*\\h+\\V?\\v{3,4}",[{parts,2}]))), <<":">> = iolist_to_binary(join(re:split(" X -
","\\H*\\h+\\V?\\v{3,4}",[]))), +
","\\H*\\h+\\V?\\v{3,4}",[]))), <<"">> = iolist_to_binary(join(re:split(" -
","\\H*\\h+\\V?\\v{3,4}",[trim]))), +
","\\H*\\h+\\V?\\v{3,4}",[trim]))), <<":">> = iolist_to_binary(join(re:split(" -
","\\H*\\h+\\V?\\v{3,4}",[{parts,2}]))), +
","\\H*\\h+\\V?\\v{3,4}",[{parts,2}]))), <<":">> = iolist_to_binary(join(re:split(" -
","\\H*\\h+\\V?\\v{3,4}",[]))), +
","\\H*\\h+\\V?\\v{3,4}",[]))), <<"">> = iolist_to_binary(join(re:split(" -","\\H*\\h+\\V?\\v{3,4}",[trim]))), +","\\H*\\h+\\V?\\v{3,4}",[trim]))), <<":">> = iolist_to_binary(join(re:split(" -","\\H*\\h+\\V?\\v{3,4}",[{parts,2}]))), +","\\H*\\h+\\V?\\v{3,4}",[{parts,2}]))), <<":">> = iolist_to_binary(join(re:split(" -","\\H*\\h+\\V?\\v{3,4}",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","\\H*\\h+\\V?\\v{3,4}",[trim]))), +","\\H*\\h+\\V?\\v{3,4}",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","\\H*\\h+\\V?\\v{3,4}",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","\\H*\\h+\\V?\\v{3,4}",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","\\H*\\h+\\V?\\v{3,4}",[]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","\\H*\\h+\\V?\\v{3,4}",[]))), <<" ">> = iolist_to_binary(join(re:split(" -","\\H*\\h+\\V?\\v{3,4}",[trim]))), +","\\H*\\h+\\V?\\v{3,4}",[trim]))), <<" ">> = iolist_to_binary(join(re:split(" -","\\H*\\h+\\V?\\v{3,4}",[{parts,2}]))), +","\\H*\\h+\\V?\\v{3,4}",[{parts,2}]))), <<" ">> = iolist_to_binary(join(re:split(" -","\\H*\\h+\\V?\\v{3,4}",[]))), - <<"XY :E">> = iolist_to_binary(join(re:split("XY ABCDE","\\H{3,4}",[trim]))), +","\\H*\\h+\\V?\\v{3,4}",[]))), + <<"XY :E">> = iolist_to_binary(join(re:split("XY ABCDE","\\H{3,4}",[trim]))), <<"XY :E">> = iolist_to_binary(join(re:split("XY ABCDE","\\H{3,4}",[{parts, - 2}]))), - <<"XY :E">> = iolist_to_binary(join(re:split("XY ABCDE","\\H{3,4}",[]))), - <<"XY : ST">> = iolist_to_binary(join(re:split("XY PQR ST","\\H{3,4}",[trim]))), + 2}]))), + <<"XY :E">> = iolist_to_binary(join(re:split("XY ABCDE","\\H{3,4}",[]))), + <<"XY : ST">> = iolist_to_binary(join(re:split("XY PQR ST","\\H{3,4}",[trim]))), <<"XY : ST">> = iolist_to_binary(join(re:split("XY PQR ST","\\H{3,4}",[{parts, - 2}]))), - <<"XY : ST">> = iolist_to_binary(join(re:split("XY PQR ST","\\H{3,4}",[]))), - <<"XY A:QRS">> = iolist_to_binary(join(re:split("XY AB PQRS",".\\h{3,4}.",[trim]))), + 2}]))), + <<"XY : ST">> = iolist_to_binary(join(re:split("XY PQR ST","\\H{3,4}",[]))), + <<"XY A:QRS">> = iolist_to_binary(join(re:split("XY AB PQRS",".\\h{3,4}.",[trim]))), <<"XY A:QRS">> = iolist_to_binary(join(re:split("XY AB PQRS",".\\h{3,4}.",[{parts, - 2}]))), - <<"XY A:QRS">> = iolist_to_binary(join(re:split("XY AB PQRS",".\\h{3,4}.",[]))), - <<">">> = iolist_to_binary(join(re:split(">XNNNYZ","\\h*X\\h?\\H+Y\\H?Z",[trim]))), + 2}]))), + <<"XY A:QRS">> = iolist_to_binary(join(re:split("XY AB PQRS",".\\h{3,4}.",[]))), + <<">">> = iolist_to_binary(join(re:split(">XNNNYZ","\\h*X\\h?\\H+Y\\H?Z",[trim]))), <<">:">> = iolist_to_binary(join(re:split(">XNNNYZ","\\h*X\\h?\\H+Y\\H?Z",[{parts, - 2}]))), - <<">:">> = iolist_to_binary(join(re:split(">XNNNYZ","\\h*X\\h?\\H+Y\\H?Z",[]))), - <<">">> = iolist_to_binary(join(re:split("> X NYQZ","\\h*X\\h?\\H+Y\\H?Z",[trim]))), + 2}]))), + <<">:">> = iolist_to_binary(join(re:split(">XNNNYZ","\\h*X\\h?\\H+Y\\H?Z",[]))), + <<">">> = iolist_to_binary(join(re:split("> X NYQZ","\\h*X\\h?\\H+Y\\H?Z",[trim]))), <<">:">> = iolist_to_binary(join(re:split("> X NYQZ","\\h*X\\h?\\H+Y\\H?Z",[{parts, - 2}]))), - <<">:">> = iolist_to_binary(join(re:split("> X NYQZ","\\h*X\\h?\\H+Y\\H?Z",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","\\h*X\\h?\\H+Y\\H?Z",[trim]))), + 2}]))), + <<">:">> = iolist_to_binary(join(re:split("> X NYQZ","\\h*X\\h?\\H+Y\\H?Z",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","\\h*X\\h?\\H+Y\\H?Z",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","\\h*X\\h?\\H+Y\\H?Z",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","\\h*X\\h?\\H+Y\\H?Z",[]))), - <<">XYZ">> = iolist_to_binary(join(re:split(">XYZ","\\h*X\\h?\\H+Y\\H?Z",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","\\h*X\\h?\\H+Y\\H?Z",[]))), + <<">XYZ">> = iolist_to_binary(join(re:split(">XYZ","\\h*X\\h?\\H+Y\\H?Z",[trim]))), <<">XYZ">> = iolist_to_binary(join(re:split(">XYZ","\\h*X\\h?\\H+Y\\H?Z",[{parts, - 2}]))), - <<">XYZ">> = iolist_to_binary(join(re:split(">XYZ","\\h*X\\h?\\H+Y\\H?Z",[]))), - <<"> X NY Z">> = iolist_to_binary(join(re:split("> X NY Z","\\h*X\\h?\\H+Y\\H?Z",[trim]))), + 2}]))), + <<">XYZ">> = iolist_to_binary(join(re:split(">XYZ","\\h*X\\h?\\H+Y\\H?Z",[]))), + <<"> X NY Z">> = iolist_to_binary(join(re:split("> X NY Z","\\h*X\\h?\\H+Y\\H?Z",[trim]))), <<"> X NY Z">> = iolist_to_binary(join(re:split("> X NY Z","\\h*X\\h?\\H+Y\\H?Z",[{parts, - 2}]))), - <<"> X NY Z">> = iolist_to_binary(join(re:split("> X NY Z","\\h*X\\h?\\H+Y\\H?Z",[]))), + 2}]))), + <<"> X NY Z">> = iolist_to_binary(join(re:split("> X NY Z","\\h*X\\h?\\H+Y\\H?Z",[]))), <<">">> = iolist_to_binary(join(re:split(">XY Z -ANN","\\v*X\\v?Y\\v+Z\\V*\\x0a\\V+\\x0b\\V{2,3}\\x0c",[trim]))), +ANN","\\v*X\\v?Y\\v+Z\\V*\\x0a\\V+\\x0b\\V{2,3}\\x0c",[trim]))), <<">:">> = iolist_to_binary(join(re:split(">XY Z ANN","\\v*X\\v?Y\\v+Z\\V*\\x0a\\V+\\x0b\\V{2,3}\\x0c",[{parts, - 2}]))), + 2}]))), <<">:">> = iolist_to_binary(join(re:split(">XY Z -ANN","\\v*X\\v?Y\\v+Z\\V*\\x0a\\V+\\x0b\\V{2,3}\\x0c",[]))), +ANN","\\v*X\\v?Y\\v+Z\\V*\\x0a\\V+\\x0b\\V{2,3}\\x0c",[]))), <<">">> = iolist_to_binary(join(re:split(">
X Y ZZZ -AAANNN","\\v*X\\v?Y\\v+Z\\V*\\x0a\\V+\\x0b\\V{2,3}\\x0c",[trim]))), +AAANNN","\\v*X\\v?Y\\v+Z\\V*\\x0a\\V+\\x0b\\V{2,3}\\x0c",[trim]))), <<">:">> = iolist_to_binary(join(re:split(">
X Y ZZZ AAANNN","\\v*X\\v?Y\\v+Z\\V*\\x0a\\V+\\x0b\\V{2,3}\\x0c",[{parts, - 2}]))), + 2}]))), <<">:">> = iolist_to_binary(join(re:split(">
X Y ZZZ -AAANNN","\\v*X\\v?Y\\v+Z\\V*\\x0a\\V+\\x0b\\V{2,3}\\x0c",[]))), - <<"foo:foo">> = iolist_to_binary(join(re:split("foobar","(foo)\\Kbar",[trim]))), +AAANNN","\\v*X\\v?Y\\v+Z\\V*\\x0a\\V+\\x0b\\V{2,3}\\x0c",[]))), + <<"foo:foo">> = iolist_to_binary(join(re:split("foobar","(foo)\\Kbar",[trim]))), <<"foo:foo:">> = iolist_to_binary(join(re:split("foobar","(foo)\\Kbar",[{parts, - 2}]))), - <<"foo:foo:">> = iolist_to_binary(join(re:split("foobar","(foo)\\Kbar",[]))), - <<"foo:foo:bar">> = iolist_to_binary(join(re:split("foobar","(foo)(\\Kbar|baz)",[trim]))), + 2}]))), + <<"foo:foo:">> = iolist_to_binary(join(re:split("foobar","(foo)\\Kbar",[]))), + <<"foo:foo:bar">> = iolist_to_binary(join(re:split("foobar","(foo)(\\Kbar|baz)",[trim]))), <<"foo:foo:bar:">> = iolist_to_binary(join(re:split("foobar","(foo)(\\Kbar|baz)",[{parts, - 2}]))), - <<"foo:foo:bar:">> = iolist_to_binary(join(re:split("foobar","(foo)(\\Kbar|baz)",[]))), - <<":foo:baz">> = iolist_to_binary(join(re:split("foobaz","(foo)(\\Kbar|baz)",[trim]))), + 2}]))), + <<"foo:foo:bar:">> = iolist_to_binary(join(re:split("foobar","(foo)(\\Kbar|baz)",[]))), + <<":foo:baz">> = iolist_to_binary(join(re:split("foobaz","(foo)(\\Kbar|baz)",[trim]))), <<":foo:baz:">> = iolist_to_binary(join(re:split("foobaz","(foo)(\\Kbar|baz)",[{parts, - 2}]))), - <<":foo:baz:">> = iolist_to_binary(join(re:split("foobaz","(foo)(\\Kbar|baz)",[]))), - <<"foo:foobar">> = iolist_to_binary(join(re:split("foobarbaz","(foo\\Kbar)baz",[trim]))), + 2}]))), + <<":foo:baz:">> = iolist_to_binary(join(re:split("foobaz","(foo)(\\Kbar|baz)",[]))), + <<"foo:foobar">> = iolist_to_binary(join(re:split("foobarbaz","(foo\\Kbar)baz",[trim]))), <<"foo:foobar:">> = iolist_to_binary(join(re:split("foobarbaz","(foo\\Kbar)baz",[{parts, - 2}]))), - <<"foo:foobar:">> = iolist_to_binary(join(re:split("foobarbaz","(foo\\Kbar)baz",[]))), - <<":tom">> = iolist_to_binary(join(re:split("tom-tom","(?<A>tom|bon)-\\g{A}",[trim]))), + 2}]))), + <<"foo:foobar:">> = iolist_to_binary(join(re:split("foobarbaz","(foo\\Kbar)baz",[]))), + <<":tom">> = iolist_to_binary(join(re:split("tom-tom","(?<A>tom|bon)-\\g{A}",[trim]))), <<":tom:">> = iolist_to_binary(join(re:split("tom-tom","(?<A>tom|bon)-\\g{A}",[{parts, - 2}]))), - <<":tom:">> = iolist_to_binary(join(re:split("tom-tom","(?<A>tom|bon)-\\g{A}",[]))), - <<":bon">> = iolist_to_binary(join(re:split("bon-bon","(?<A>tom|bon)-\\g{A}",[trim]))), + 2}]))), + <<":tom:">> = iolist_to_binary(join(re:split("tom-tom","(?<A>tom|bon)-\\g{A}",[]))), + <<":bon">> = iolist_to_binary(join(re:split("bon-bon","(?<A>tom|bon)-\\g{A}",[trim]))), <<":bon:">> = iolist_to_binary(join(re:split("bon-bon","(?<A>tom|bon)-\\g{A}",[{parts, - 2}]))), - <<":bon:">> = iolist_to_binary(join(re:split("bon-bon","(?<A>tom|bon)-\\g{A}",[]))), - <<"bacxxx">> = iolist_to_binary(join(re:split("bacxxx","(^(a|b\\g{-1}))",[trim]))), + 2}]))), + <<":bon:">> = iolist_to_binary(join(re:split("bon-bon","(?<A>tom|bon)-\\g{A}",[]))), + <<"bacxxx">> = iolist_to_binary(join(re:split("bacxxx","(^(a|b\\g{-1}))",[trim]))), <<"bacxxx">> = iolist_to_binary(join(re:split("bacxxx","(^(a|b\\g{-1}))",[{parts, - 2}]))), - <<"bacxxx">> = iolist_to_binary(join(re:split("bacxxx","(^(a|b\\g{-1}))",[]))), - <<":abc">> = iolist_to_binary(join(re:split("abcabc","(?|(abc)|(xyz))\\1",[trim]))), + 2}]))), + <<"bacxxx">> = iolist_to_binary(join(re:split("bacxxx","(^(a|b\\g{-1}))",[]))), + <<":abc">> = iolist_to_binary(join(re:split("abcabc","(?|(abc)|(xyz))\\1",[trim]))), <<":abc:">> = iolist_to_binary(join(re:split("abcabc","(?|(abc)|(xyz))\\1",[{parts, - 2}]))), - <<":abc:">> = iolist_to_binary(join(re:split("abcabc","(?|(abc)|(xyz))\\1",[]))), - <<":xyz">> = iolist_to_binary(join(re:split("xyzxyz","(?|(abc)|(xyz))\\1",[trim]))), + 2}]))), + <<":abc:">> = iolist_to_binary(join(re:split("abcabc","(?|(abc)|(xyz))\\1",[]))), + <<":xyz">> = iolist_to_binary(join(re:split("xyzxyz","(?|(abc)|(xyz))\\1",[trim]))), <<":xyz:">> = iolist_to_binary(join(re:split("xyzxyz","(?|(abc)|(xyz))\\1",[{parts, - 2}]))), - <<":xyz:">> = iolist_to_binary(join(re:split("xyzxyz","(?|(abc)|(xyz))\\1",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?|(abc)|(xyz))\\1",[trim]))), + 2}]))), + <<":xyz:">> = iolist_to_binary(join(re:split("xyzxyz","(?|(abc)|(xyz))\\1",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?|(abc)|(xyz))\\1",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?|(abc)|(xyz))\\1",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?|(abc)|(xyz))\\1",[]))), - <<"abcxyz">> = iolist_to_binary(join(re:split("abcxyz","(?|(abc)|(xyz))\\1",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?|(abc)|(xyz))\\1",[]))), + <<"abcxyz">> = iolist_to_binary(join(re:split("abcxyz","(?|(abc)|(xyz))\\1",[trim]))), <<"abcxyz">> = iolist_to_binary(join(re:split("abcxyz","(?|(abc)|(xyz))\\1",[{parts, - 2}]))), - <<"abcxyz">> = iolist_to_binary(join(re:split("abcxyz","(?|(abc)|(xyz))\\1",[]))), - <<"xyzabc">> = iolist_to_binary(join(re:split("xyzabc","(?|(abc)|(xyz))\\1",[trim]))), + 2}]))), + <<"abcxyz">> = iolist_to_binary(join(re:split("abcxyz","(?|(abc)|(xyz))\\1",[]))), + <<"xyzabc">> = iolist_to_binary(join(re:split("xyzabc","(?|(abc)|(xyz))\\1",[trim]))), <<"xyzabc">> = iolist_to_binary(join(re:split("xyzabc","(?|(abc)|(xyz))\\1",[{parts, - 2}]))), - <<"xyzabc">> = iolist_to_binary(join(re:split("xyzabc","(?|(abc)|(xyz))\\1",[]))), - <<":abc">> = iolist_to_binary(join(re:split("abcabc","(?|(abc)|(xyz))(?1)",[trim]))), + 2}]))), + <<"xyzabc">> = iolist_to_binary(join(re:split("xyzabc","(?|(abc)|(xyz))\\1",[]))), + <<":abc">> = iolist_to_binary(join(re:split("abcabc","(?|(abc)|(xyz))(?1)",[trim]))), <<":abc:">> = iolist_to_binary(join(re:split("abcabc","(?|(abc)|(xyz))(?1)",[{parts, - 2}]))), - <<":abc:">> = iolist_to_binary(join(re:split("abcabc","(?|(abc)|(xyz))(?1)",[]))), - <<":xyz">> = iolist_to_binary(join(re:split("xyzabc","(?|(abc)|(xyz))(?1)",[trim]))), + 2}]))), + <<":abc:">> = iolist_to_binary(join(re:split("abcabc","(?|(abc)|(xyz))(?1)",[]))), + <<":xyz">> = iolist_to_binary(join(re:split("xyzabc","(?|(abc)|(xyz))(?1)",[trim]))), <<":xyz:">> = iolist_to_binary(join(re:split("xyzabc","(?|(abc)|(xyz))(?1)",[{parts, - 2}]))), - <<":xyz:">> = iolist_to_binary(join(re:split("xyzabc","(?|(abc)|(xyz))(?1)",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?|(abc)|(xyz))(?1)",[trim]))), + 2}]))), + <<":xyz:">> = iolist_to_binary(join(re:split("xyzabc","(?|(abc)|(xyz))(?1)",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?|(abc)|(xyz))(?1)",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?|(abc)|(xyz))(?1)",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?|(abc)|(xyz))(?1)",[]))), - <<"xyzxyz">> = iolist_to_binary(join(re:split("xyzxyz","(?|(abc)|(xyz))(?1)",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?|(abc)|(xyz))(?1)",[]))), + <<"xyzxyz">> = iolist_to_binary(join(re:split("xyzxyz","(?|(abc)|(xyz))(?1)",[trim]))), <<"xyzxyz">> = iolist_to_binary(join(re:split("xyzxyz","(?|(abc)|(xyz))(?1)",[{parts, - 2}]))), - <<"xyzxyz">> = iolist_to_binary(join(re:split("xyzxyz","(?|(abc)|(xyz))(?1)",[]))), + 2}]))), + <<"xyzxyz">> = iolist_to_binary(join(re:split("xyzxyz","(?|(abc)|(xyz))(?1)",[]))), ok. run41() -> - <<":a:b:c:d:Y">> = iolist_to_binary(join(re:split("XYabcdY","^X(?5)(a)(?|(b)|(q))(c)(d)(Y)",[trim]))), + <<":a:b:c:d:Y">> = iolist_to_binary(join(re:split("XYabcdY","^X(?5)(a)(?|(b)|(q))(c)(d)(Y)",[trim]))), <<":a:b:c:d:Y:">> = iolist_to_binary(join(re:split("XYabcdY","^X(?5)(a)(?|(b)|(q))(c)(d)(Y)",[{parts, - 2}]))), - <<":a:b:c:d:Y:">> = iolist_to_binary(join(re:split("XYabcdY","^X(?5)(a)(?|(b)|(q))(c)(d)(Y)",[]))), - <<":a:b:::c:d:Y">> = iolist_to_binary(join(re:split("XYabcdY","^X(?7)(a)(?|(b|(r)(s))|(q))(c)(d)(Y)",[trim]))), + 2}]))), + <<":a:b:c:d:Y:">> = iolist_to_binary(join(re:split("XYabcdY","^X(?5)(a)(?|(b)|(q))(c)(d)(Y)",[]))), + <<":a:b:::c:d:Y">> = iolist_to_binary(join(re:split("XYabcdY","^X(?7)(a)(?|(b|(r)(s))|(q))(c)(d)(Y)",[trim]))), <<":a:b:::c:d:Y:">> = iolist_to_binary(join(re:split("XYabcdY","^X(?7)(a)(?|(b|(r)(s))|(q))(c)(d)(Y)",[{parts, - 2}]))), - <<":a:b:::c:d:Y:">> = iolist_to_binary(join(re:split("XYabcdY","^X(?7)(a)(?|(b|(r)(s))|(q))(c)(d)(Y)",[]))), - <<":a:b:::c:d:Y">> = iolist_to_binary(join(re:split("XYabcdY","^X(?7)(a)(?|(b|(?|(r)|(t))(s))|(q))(c)(d)(Y)",[trim]))), + 2}]))), + <<":a:b:::c:d:Y:">> = iolist_to_binary(join(re:split("XYabcdY","^X(?7)(a)(?|(b|(r)(s))|(q))(c)(d)(Y)",[]))), + <<":a:b:::c:d:Y">> = iolist_to_binary(join(re:split("XYabcdY","^X(?7)(a)(?|(b|(?|(r)|(t))(s))|(q))(c)(d)(Y)",[trim]))), <<":a:b:::c:d:Y:">> = iolist_to_binary(join(re:split("XYabcdY","^X(?7)(a)(?|(b|(?|(r)|(t))(s))|(q))(c)(d)(Y)",[{parts, - 2}]))), - <<":a:b:::c:d:Y:">> = iolist_to_binary(join(re:split("XYabcdY","^X(?7)(a)(?|(b|(?|(r)|(t))(s))|(q))(c)(d)(Y)",[]))), - <<":a:xyz">> = iolist_to_binary(join(re:split("a:aaxyz","(?'abc'\\w+):\\k<abc>{2}",[trim]))), + 2}]))), + <<":a:b:::c:d:Y:">> = iolist_to_binary(join(re:split("XYabcdY","^X(?7)(a)(?|(b|(?|(r)|(t))(s))|(q))(c)(d)(Y)",[]))), + <<":a:xyz">> = iolist_to_binary(join(re:split("a:aaxyz","(?'abc'\\w+):\\k<abc>{2}",[trim]))), <<":a:xyz">> = iolist_to_binary(join(re:split("a:aaxyz","(?'abc'\\w+):\\k<abc>{2}",[{parts, - 2}]))), - <<":a:xyz">> = iolist_to_binary(join(re:split("a:aaxyz","(?'abc'\\w+):\\k<abc>{2}",[]))), - <<":ab:xyz">> = iolist_to_binary(join(re:split("ab:ababxyz","(?'abc'\\w+):\\k<abc>{2}",[trim]))), + 2}]))), + <<":a:xyz">> = iolist_to_binary(join(re:split("a:aaxyz","(?'abc'\\w+):\\k<abc>{2}",[]))), + <<":ab:xyz">> = iolist_to_binary(join(re:split("ab:ababxyz","(?'abc'\\w+):\\k<abc>{2}",[trim]))), <<":ab:xyz">> = iolist_to_binary(join(re:split("ab:ababxyz","(?'abc'\\w+):\\k<abc>{2}",[{parts, - 2}]))), - <<":ab:xyz">> = iolist_to_binary(join(re:split("ab:ababxyz","(?'abc'\\w+):\\k<abc>{2}",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?'abc'\\w+):\\k<abc>{2}",[trim]))), + 2}]))), + <<":ab:xyz">> = iolist_to_binary(join(re:split("ab:ababxyz","(?'abc'\\w+):\\k<abc>{2}",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?'abc'\\w+):\\k<abc>{2}",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?'abc'\\w+):\\k<abc>{2}",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?'abc'\\w+):\\k<abc>{2}",[]))), - <<"a:axyz">> = iolist_to_binary(join(re:split("a:axyz","(?'abc'\\w+):\\k<abc>{2}",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?'abc'\\w+):\\k<abc>{2}",[]))), + <<"a:axyz">> = iolist_to_binary(join(re:split("a:axyz","(?'abc'\\w+):\\k<abc>{2}",[trim]))), <<"a:axyz">> = iolist_to_binary(join(re:split("a:axyz","(?'abc'\\w+):\\k<abc>{2}",[{parts, - 2}]))), - <<"a:axyz">> = iolist_to_binary(join(re:split("a:axyz","(?'abc'\\w+):\\k<abc>{2}",[]))), - <<"ab:abxyz">> = iolist_to_binary(join(re:split("ab:abxyz","(?'abc'\\w+):\\k<abc>{2}",[trim]))), + 2}]))), + <<"a:axyz">> = iolist_to_binary(join(re:split("a:axyz","(?'abc'\\w+):\\k<abc>{2}",[]))), + <<"ab:abxyz">> = iolist_to_binary(join(re:split("ab:abxyz","(?'abc'\\w+):\\k<abc>{2}",[trim]))), <<"ab:abxyz">> = iolist_to_binary(join(re:split("ab:abxyz","(?'abc'\\w+):\\k<abc>{2}",[{parts, - 2}]))), - <<"ab:abxyz">> = iolist_to_binary(join(re:split("ab:abxyz","(?'abc'\\w+):\\k<abc>{2}",[]))), - <<":a:xyz">> = iolist_to_binary(join(re:split("a:aaxyz","(?'abc'\\w+):\\g{abc}{2}",[trim]))), + 2}]))), + <<"ab:abxyz">> = iolist_to_binary(join(re:split("ab:abxyz","(?'abc'\\w+):\\k<abc>{2}",[]))), + <<":a:xyz">> = iolist_to_binary(join(re:split("a:aaxyz","(?'abc'\\w+):\\g{abc}{2}",[trim]))), <<":a:xyz">> = iolist_to_binary(join(re:split("a:aaxyz","(?'abc'\\w+):\\g{abc}{2}",[{parts, - 2}]))), - <<":a:xyz">> = iolist_to_binary(join(re:split("a:aaxyz","(?'abc'\\w+):\\g{abc}{2}",[]))), - <<":ab:xyz">> = iolist_to_binary(join(re:split("ab:ababxyz","(?'abc'\\w+):\\g{abc}{2}",[trim]))), + 2}]))), + <<":a:xyz">> = iolist_to_binary(join(re:split("a:aaxyz","(?'abc'\\w+):\\g{abc}{2}",[]))), + <<":ab:xyz">> = iolist_to_binary(join(re:split("ab:ababxyz","(?'abc'\\w+):\\g{abc}{2}",[trim]))), <<":ab:xyz">> = iolist_to_binary(join(re:split("ab:ababxyz","(?'abc'\\w+):\\g{abc}{2}",[{parts, - 2}]))), - <<":ab:xyz">> = iolist_to_binary(join(re:split("ab:ababxyz","(?'abc'\\w+):\\g{abc}{2}",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?'abc'\\w+):\\g{abc}{2}",[trim]))), + 2}]))), + <<":ab:xyz">> = iolist_to_binary(join(re:split("ab:ababxyz","(?'abc'\\w+):\\g{abc}{2}",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?'abc'\\w+):\\g{abc}{2}",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?'abc'\\w+):\\g{abc}{2}",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?'abc'\\w+):\\g{abc}{2}",[]))), - <<"a:axyz">> = iolist_to_binary(join(re:split("a:axyz","(?'abc'\\w+):\\g{abc}{2}",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?'abc'\\w+):\\g{abc}{2}",[]))), + <<"a:axyz">> = iolist_to_binary(join(re:split("a:axyz","(?'abc'\\w+):\\g{abc}{2}",[trim]))), <<"a:axyz">> = iolist_to_binary(join(re:split("a:axyz","(?'abc'\\w+):\\g{abc}{2}",[{parts, - 2}]))), - <<"a:axyz">> = iolist_to_binary(join(re:split("a:axyz","(?'abc'\\w+):\\g{abc}{2}",[]))), - <<"ab:abxyz">> = iolist_to_binary(join(re:split("ab:abxyz","(?'abc'\\w+):\\g{abc}{2}",[trim]))), + 2}]))), + <<"a:axyz">> = iolist_to_binary(join(re:split("a:axyz","(?'abc'\\w+):\\g{abc}{2}",[]))), + <<"ab:abxyz">> = iolist_to_binary(join(re:split("ab:abxyz","(?'abc'\\w+):\\g{abc}{2}",[trim]))), <<"ab:abxyz">> = iolist_to_binary(join(re:split("ab:abxyz","(?'abc'\\w+):\\g{abc}{2}",[{parts, - 2}]))), - <<"ab:abxyz">> = iolist_to_binary(join(re:split("ab:abxyz","(?'abc'\\w+):\\g{abc}{2}",[]))), + 2}]))), + <<"ab:abxyz">> = iolist_to_binary(join(re:split("ab:abxyz","(?'abc'\\w+):\\g{abc}{2}",[]))), <<":a">> = iolist_to_binary(join(re:split("abd","^(?<ab>a)? (?(<ab>)b|c) (?('ab')d|e)",[extended, - trim]))), + trim]))), <<":a:">> = iolist_to_binary(join(re:split("abd","^(?<ab>a)? (?(<ab>)b|c) (?('ab')d|e)",[extended, {parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("abd","^(?<ab>a)? (?(<ab>)b|c) (?('ab')d|e)",[extended]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("abd","^(?<ab>a)? (?(<ab>)b|c) (?('ab')d|e)",[extended]))), <<"">> = iolist_to_binary(join(re:split("ce","^(?<ab>a)? (?(<ab>)b|c) (?('ab')d|e)",[extended, - trim]))), + trim]))), <<"::">> = iolist_to_binary(join(re:split("ce","^(?<ab>a)? (?(<ab>)b|c) (?('ab')d|e)",[extended, {parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("ce","^(?<ab>a)? (?(<ab>)b|c) (?('ab')d|e)",[extended]))), - <<":aX">> = iolist_to_binary(join(re:split("aXaXZ","^(a.)\\g-1Z",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("ce","^(?<ab>a)? (?(<ab>)b|c) (?('ab')d|e)",[extended]))), + <<":aX">> = iolist_to_binary(join(re:split("aXaXZ","^(a.)\\g-1Z",[trim]))), <<":aX:">> = iolist_to_binary(join(re:split("aXaXZ","^(a.)\\g-1Z",[{parts, - 2}]))), - <<":aX:">> = iolist_to_binary(join(re:split("aXaXZ","^(a.)\\g-1Z",[]))), - <<":aX">> = iolist_to_binary(join(re:split("aXaXZ","^(a.)\\g{-1}Z",[trim]))), + 2}]))), + <<":aX:">> = iolist_to_binary(join(re:split("aXaXZ","^(a.)\\g-1Z",[]))), + <<":aX">> = iolist_to_binary(join(re:split("aXaXZ","^(a.)\\g{-1}Z",[trim]))), <<":aX:">> = iolist_to_binary(join(re:split("aXaXZ","^(a.)\\g{-1}Z",[{parts, - 2}]))), - <<":aX:">> = iolist_to_binary(join(re:split("aXaXZ","^(a.)\\g{-1}Z",[]))), + 2}]))), + <<":aX:">> = iolist_to_binary(join(re:split("aXaXZ","^(a.)\\g{-1}Z",[]))), <<":::cd">> = iolist_to_binary(join(re:split("abcd","^(?(DEFINE) (?<A> a) (?<B> b) ) (?&A) (?&B) ",[extended, - trim]))), + trim]))), <<":::cd">> = iolist_to_binary(join(re:split("abcd","^(?(DEFINE) (?<A> a) (?<B> b) ) (?&A) (?&B) ",[extended, {parts, - 2}]))), - <<":::cd">> = iolist_to_binary(join(re:split("abcd","^(?(DEFINE) (?<A> a) (?<B> b) ) (?&A) (?&B) ",[extended]))), + 2}]))), + <<":::cd">> = iolist_to_binary(join(re:split("abcd","^(?(DEFINE) (?<A> a) (?<B> b) ) (?&A) (?&B) ",[extended]))), <<":metcalfe:33">> = iolist_to_binary(join(re:split("metcalfe 33","(?<NAME>(?&NAME_PAT))\\s+(?<ADDR>(?&ADDRESS_PAT)) (?(DEFINE) (?<NAME_PAT>[a-z]+) (?<ADDRESS_PAT>\\d+) - )",[extended,trim]))), + )",[extended,trim]))), <<":metcalfe:33:::">> = iolist_to_binary(join(re:split("metcalfe 33","(?<NAME>(?&NAME_PAT))\\s+(?<ADDR>(?&ADDRESS_PAT)) (?(DEFINE) (?<NAME_PAT>[a-z]+) (?<ADDRESS_PAT>\\d+) - )",[extended,{parts,2}]))), + )",[extended,{parts,2}]))), <<":metcalfe:33:::">> = iolist_to_binary(join(re:split("metcalfe 33","(?<NAME>(?&NAME_PAT))\\s+(?<ADDR>(?&ADDRESS_PAT)) (?(DEFINE) (?<NAME_PAT>[a-z]+) (?<ADDRESS_PAT>\\d+) - )",[extended]))), - <<"::.4">> = iolist_to_binary(join(re:split("1.2.3.4","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[trim]))), + )",[extended]))), + <<"::.4">> = iolist_to_binary(join(re:split("1.2.3.4","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[trim]))), <<"::.4:">> = iolist_to_binary(join(re:split("1.2.3.4","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[{parts, - 2}]))), - <<"::.4:">> = iolist_to_binary(join(re:split("1.2.3.4","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[]))), - <<"::.206">> = iolist_to_binary(join(re:split("131.111.10.206","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[trim]))), + 2}]))), + <<"::.4:">> = iolist_to_binary(join(re:split("1.2.3.4","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[]))), + <<"::.206">> = iolist_to_binary(join(re:split("131.111.10.206","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[trim]))), <<"::.206:">> = iolist_to_binary(join(re:split("131.111.10.206","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[{parts, - 2}]))), - <<"::.206:">> = iolist_to_binary(join(re:split("131.111.10.206","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[]))), - <<"::.0">> = iolist_to_binary(join(re:split("10.0.0.0","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[trim]))), + 2}]))), + <<"::.206:">> = iolist_to_binary(join(re:split("131.111.10.206","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[]))), + <<"::.0">> = iolist_to_binary(join(re:split("10.0.0.0","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[trim]))), <<"::.0:">> = iolist_to_binary(join(re:split("10.0.0.0","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[{parts, - 2}]))), - <<"::.0:">> = iolist_to_binary(join(re:split("10.0.0.0","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[trim]))), + 2}]))), + <<"::.0:">> = iolist_to_binary(join(re:split("10.0.0.0","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[]))), - <<"10.6">> = iolist_to_binary(join(re:split("10.6","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[]))), + <<"10.6">> = iolist_to_binary(join(re:split("10.6","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[trim]))), <<"10.6">> = iolist_to_binary(join(re:split("10.6","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[{parts, - 2}]))), - <<"10.6">> = iolist_to_binary(join(re:split("10.6","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[]))), - <<"455.3.4.5">> = iolist_to_binary(join(re:split("455.3.4.5","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[trim]))), + 2}]))), + <<"10.6">> = iolist_to_binary(join(re:split("10.6","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[]))), + <<"455.3.4.5">> = iolist_to_binary(join(re:split("455.3.4.5","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[trim]))), <<"455.3.4.5">> = iolist_to_binary(join(re:split("455.3.4.5","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[{parts, - 2}]))), - <<"455.3.4.5">> = iolist_to_binary(join(re:split("455.3.4.5","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[]))), - <<":.4">> = iolist_to_binary(join(re:split("1.2.3.4","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[trim]))), + 2}]))), + <<"455.3.4.5">> = iolist_to_binary(join(re:split("455.3.4.5","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[]))), + <<":.4">> = iolist_to_binary(join(re:split("1.2.3.4","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[trim]))), <<":.4::">> = iolist_to_binary(join(re:split("1.2.3.4","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[{parts, - 2}]))), - <<":.4::">> = iolist_to_binary(join(re:split("1.2.3.4","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[]))), - <<":.206">> = iolist_to_binary(join(re:split("131.111.10.206","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[trim]))), + 2}]))), + <<":.4::">> = iolist_to_binary(join(re:split("1.2.3.4","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[]))), + <<":.206">> = iolist_to_binary(join(re:split("131.111.10.206","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[trim]))), <<":.206::">> = iolist_to_binary(join(re:split("131.111.10.206","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[{parts, - 2}]))), - <<":.206::">> = iolist_to_binary(join(re:split("131.111.10.206","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[]))), - <<":.0">> = iolist_to_binary(join(re:split("10.0.0.0","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[trim]))), + 2}]))), + <<":.206::">> = iolist_to_binary(join(re:split("131.111.10.206","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[]))), + <<":.0">> = iolist_to_binary(join(re:split("10.0.0.0","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[trim]))), <<":.0::">> = iolist_to_binary(join(re:split("10.0.0.0","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[{parts, - 2}]))), - <<":.0::">> = iolist_to_binary(join(re:split("10.0.0.0","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[trim]))), + 2}]))), + <<":.0::">> = iolist_to_binary(join(re:split("10.0.0.0","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[]))), - <<"10.6">> = iolist_to_binary(join(re:split("10.6","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[]))), + <<"10.6">> = iolist_to_binary(join(re:split("10.6","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[trim]))), <<"10.6">> = iolist_to_binary(join(re:split("10.6","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[{parts, - 2}]))), - <<"10.6">> = iolist_to_binary(join(re:split("10.6","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[]))), - <<"455.3.4.5">> = iolist_to_binary(join(re:split("455.3.4.5","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[trim]))), + 2}]))), + <<"10.6">> = iolist_to_binary(join(re:split("10.6","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[]))), + <<"455.3.4.5">> = iolist_to_binary(join(re:split("455.3.4.5","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[trim]))), <<"455.3.4.5">> = iolist_to_binary(join(re:split("455.3.4.5","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[{parts, - 2}]))), - <<"455.3.4.5">> = iolist_to_binary(join(re:split("455.3.4.5","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[]))), - <<":party">> = iolist_to_binary(join(re:split("now is the time for all good men to come to the aid of the party","^(\\w++|\\s++)*$",[trim]))), + 2}]))), + <<"455.3.4.5">> = iolist_to_binary(join(re:split("455.3.4.5","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[]))), + <<":party">> = iolist_to_binary(join(re:split("now is the time for all good men to come to the aid of the party","^(\\w++|\\s++)*$",[trim]))), <<":party:">> = iolist_to_binary(join(re:split("now is the time for all good men to come to the aid of the party","^(\\w++|\\s++)*$",[{parts, - 2}]))), - <<":party:">> = iolist_to_binary(join(re:split("now is the time for all good men to come to the aid of the party","^(\\w++|\\s++)*$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\w++|\\s++)*$",[trim]))), + 2}]))), + <<":party:">> = iolist_to_binary(join(re:split("now is the time for all good men to come to the aid of the party","^(\\w++|\\s++)*$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\w++|\\s++)*$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\w++|\\s++)*$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\w++|\\s++)*$",[]))), - <<"this is not a line with only words and spaces!">> = iolist_to_binary(join(re:split("this is not a line with only words and spaces!","^(\\w++|\\s++)*$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\w++|\\s++)*$",[]))), + <<"this is not a line with only words and spaces!">> = iolist_to_binary(join(re:split("this is not a line with only words and spaces!","^(\\w++|\\s++)*$",[trim]))), <<"this is not a line with only words and spaces!">> = iolist_to_binary(join(re:split("this is not a line with only words and spaces!","^(\\w++|\\s++)*$",[{parts, - 2}]))), - <<"this is not a line with only words and spaces!">> = iolist_to_binary(join(re:split("this is not a line with only words and spaces!","^(\\w++|\\s++)*$",[]))), - <<":12345:a">> = iolist_to_binary(join(re:split("12345a","(\\d++)(\\w)",[trim]))), + 2}]))), + <<"this is not a line with only words and spaces!">> = iolist_to_binary(join(re:split("this is not a line with only words and spaces!","^(\\w++|\\s++)*$",[]))), + <<":12345:a">> = iolist_to_binary(join(re:split("12345a","(\\d++)(\\w)",[trim]))), <<":12345:a:">> = iolist_to_binary(join(re:split("12345a","(\\d++)(\\w)",[{parts, - 2}]))), - <<":12345:a:">> = iolist_to_binary(join(re:split("12345a","(\\d++)(\\w)",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(\\d++)(\\w)",[trim]))), + 2}]))), + <<":12345:a:">> = iolist_to_binary(join(re:split("12345a","(\\d++)(\\w)",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(\\d++)(\\w)",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(\\d++)(\\w)",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(\\d++)(\\w)",[]))), - <<"12345+">> = iolist_to_binary(join(re:split("12345+","(\\d++)(\\w)",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(\\d++)(\\w)",[]))), + <<"12345+">> = iolist_to_binary(join(re:split("12345+","(\\d++)(\\w)",[trim]))), <<"12345+">> = iolist_to_binary(join(re:split("12345+","(\\d++)(\\w)",[{parts, - 2}]))), - <<"12345+">> = iolist_to_binary(join(re:split("12345+","(\\d++)(\\w)",[]))), - <<"">> = iolist_to_binary(join(re:split("aaab","a++b",[trim]))), + 2}]))), + <<"12345+">> = iolist_to_binary(join(re:split("12345+","(\\d++)(\\w)",[]))), + <<"">> = iolist_to_binary(join(re:split("aaab","a++b",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaab","a++b",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaab","a++b",[]))), - <<":aaab">> = iolist_to_binary(join(re:split("aaab","(a++b)",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaab","a++b",[]))), + <<":aaab">> = iolist_to_binary(join(re:split("aaab","(a++b)",[trim]))), <<":aaab:">> = iolist_to_binary(join(re:split("aaab","(a++b)",[{parts, - 2}]))), - <<":aaab:">> = iolist_to_binary(join(re:split("aaab","(a++b)",[]))), - <<":aaa">> = iolist_to_binary(join(re:split("aaab","(a++)b",[trim]))), + 2}]))), + <<":aaab:">> = iolist_to_binary(join(re:split("aaab","(a++b)",[]))), + <<":aaa">> = iolist_to_binary(join(re:split("aaab","(a++)b",[trim]))), <<":aaa:">> = iolist_to_binary(join(re:split("aaab","(a++)b",[{parts, - 2}]))), - <<":aaa:">> = iolist_to_binary(join(re:split("aaab","(a++)b",[]))), - <<"((:x">> = iolist_to_binary(join(re:split("((abc(ade)ufh()()x","([^()]++|\\([^()]*\\))+",[trim]))), + 2}]))), + <<":aaa:">> = iolist_to_binary(join(re:split("aaab","(a++)b",[]))), + <<"((:x">> = iolist_to_binary(join(re:split("((abc(ade)ufh()()x","([^()]++|\\([^()]*\\))+",[trim]))), <<"((:x:">> = iolist_to_binary(join(re:split("((abc(ade)ufh()()x","([^()]++|\\([^()]*\\))+",[{parts, - 2}]))), - <<"((:x:">> = iolist_to_binary(join(re:split("((abc(ade)ufh()()x","([^()]++|\\([^()]*\\))+",[]))), - <<":abc">> = iolist_to_binary(join(re:split("(abc)","\\(([^()]++|\\([^()]+\\))+\\)",[trim]))), + 2}]))), + <<"((:x:">> = iolist_to_binary(join(re:split("((abc(ade)ufh()()x","([^()]++|\\([^()]*\\))+",[]))), + <<":abc">> = iolist_to_binary(join(re:split("(abc)","\\(([^()]++|\\([^()]+\\))+\\)",[trim]))), <<":abc:">> = iolist_to_binary(join(re:split("(abc)","\\(([^()]++|\\([^()]+\\))+\\)",[{parts, - 2}]))), - <<":abc:">> = iolist_to_binary(join(re:split("(abc)","\\(([^()]++|\\([^()]+\\))+\\)",[]))), - <<":xyz">> = iolist_to_binary(join(re:split("(abc(def)xyz)","\\(([^()]++|\\([^()]+\\))+\\)",[trim]))), + 2}]))), + <<":abc:">> = iolist_to_binary(join(re:split("(abc)","\\(([^()]++|\\([^()]+\\))+\\)",[]))), + <<":xyz">> = iolist_to_binary(join(re:split("(abc(def)xyz)","\\(([^()]++|\\([^()]+\\))+\\)",[trim]))), <<":xyz:">> = iolist_to_binary(join(re:split("(abc(def)xyz)","\\(([^()]++|\\([^()]+\\))+\\)",[{parts, - 2}]))), - <<":xyz:">> = iolist_to_binary(join(re:split("(abc(def)xyz)","\\(([^()]++|\\([^()]+\\))+\\)",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\(([^()]++|\\([^()]+\\))+\\)",[trim]))), + 2}]))), + <<":xyz:">> = iolist_to_binary(join(re:split("(abc(def)xyz)","\\(([^()]++|\\([^()]+\\))+\\)",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\(([^()]++|\\([^()]+\\))+\\)",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\(([^()]++|\\([^()]+\\))+\\)",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\(([^()]++|\\([^()]+\\))+\\)",[]))), - <<"((()aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("((()aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","\\(([^()]++|\\([^()]+\\))+\\)",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\(([^()]++|\\([^()]+\\))+\\)",[]))), + <<"((()aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("((()aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","\\(([^()]++|\\([^()]+\\))+\\)",[trim]))), <<"((()aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("((()aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","\\(([^()]++|\\([^()]+\\))+\\)",[{parts, - 2}]))), - <<"((()aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("((()aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","\\(([^()]++|\\([^()]+\\))+\\)",[]))), - <<":c">> = iolist_to_binary(join(re:split("abc","^([^()]|\\((?1)*\\))*$",[trim]))), + 2}]))), + <<"((()aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("((()aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","\\(([^()]++|\\([^()]+\\))+\\)",[]))), + <<":c">> = iolist_to_binary(join(re:split("abc","^([^()]|\\((?1)*\\))*$",[trim]))), <<":c:">> = iolist_to_binary(join(re:split("abc","^([^()]|\\((?1)*\\))*$",[{parts, - 2}]))), - <<":c:">> = iolist_to_binary(join(re:split("abc","^([^()]|\\((?1)*\\))*$",[]))), - <<":c">> = iolist_to_binary(join(re:split("a(b)c","^([^()]|\\((?1)*\\))*$",[trim]))), + 2}]))), + <<":c:">> = iolist_to_binary(join(re:split("abc","^([^()]|\\((?1)*\\))*$",[]))), + <<":c">> = iolist_to_binary(join(re:split("a(b)c","^([^()]|\\((?1)*\\))*$",[trim]))), <<":c:">> = iolist_to_binary(join(re:split("a(b)c","^([^()]|\\((?1)*\\))*$",[{parts, - 2}]))), - <<":c:">> = iolist_to_binary(join(re:split("a(b)c","^([^()]|\\((?1)*\\))*$",[]))), - <<":d">> = iolist_to_binary(join(re:split("a(b(c))d","^([^()]|\\((?1)*\\))*$",[trim]))), + 2}]))), + <<":c:">> = iolist_to_binary(join(re:split("a(b)c","^([^()]|\\((?1)*\\))*$",[]))), + <<":d">> = iolist_to_binary(join(re:split("a(b(c))d","^([^()]|\\((?1)*\\))*$",[trim]))), <<":d:">> = iolist_to_binary(join(re:split("a(b(c))d","^([^()]|\\((?1)*\\))*$",[{parts, - 2}]))), - <<":d:">> = iolist_to_binary(join(re:split("a(b(c))d","^([^()]|\\((?1)*\\))*$",[]))), - <<"*** Failers)">> = iolist_to_binary(join(re:split("*** Failers)","^([^()]|\\((?1)*\\))*$",[trim]))), + 2}]))), + <<":d:">> = iolist_to_binary(join(re:split("a(b(c))d","^([^()]|\\((?1)*\\))*$",[]))), + <<"*** Failers)">> = iolist_to_binary(join(re:split("*** Failers)","^([^()]|\\((?1)*\\))*$",[trim]))), <<"*** Failers)">> = iolist_to_binary(join(re:split("*** Failers)","^([^()]|\\((?1)*\\))*$",[{parts, - 2}]))), - <<"*** Failers)">> = iolist_to_binary(join(re:split("*** Failers)","^([^()]|\\((?1)*\\))*$",[]))), - <<"a(b(c)d">> = iolist_to_binary(join(re:split("a(b(c)d","^([^()]|\\((?1)*\\))*$",[trim]))), + 2}]))), + <<"*** Failers)">> = iolist_to_binary(join(re:split("*** Failers)","^([^()]|\\((?1)*\\))*$",[]))), + <<"a(b(c)d">> = iolist_to_binary(join(re:split("a(b(c)d","^([^()]|\\((?1)*\\))*$",[trim]))), <<"a(b(c)d">> = iolist_to_binary(join(re:split("a(b(c)d","^([^()]|\\((?1)*\\))*$",[{parts, - 2}]))), - <<"a(b(c)d">> = iolist_to_binary(join(re:split("a(b(c)d","^([^()]|\\((?1)*\\))*$",[]))), + 2}]))), + <<"a(b(c)d">> = iolist_to_binary(join(re:split("a(b(c)d","^([^()]|\\((?1)*\\))*$",[]))), ok. run42() -> - <<":3">> = iolist_to_binary(join(re:split(">abc>123<xyz<","^>abc>([^()]|\\((?1)*\\))*<xyz<$",[trim]))), + <<":3">> = iolist_to_binary(join(re:split(">abc>123<xyz<","^>abc>([^()]|\\((?1)*\\))*<xyz<$",[trim]))), <<":3:">> = iolist_to_binary(join(re:split(">abc>123<xyz<","^>abc>([^()]|\\((?1)*\\))*<xyz<$",[{parts, - 2}]))), - <<":3:">> = iolist_to_binary(join(re:split(">abc>123<xyz<","^>abc>([^()]|\\((?1)*\\))*<xyz<$",[]))), - <<":3">> = iolist_to_binary(join(re:split(">abc>1(2)3<xyz<","^>abc>([^()]|\\((?1)*\\))*<xyz<$",[trim]))), + 2}]))), + <<":3:">> = iolist_to_binary(join(re:split(">abc>123<xyz<","^>abc>([^()]|\\((?1)*\\))*<xyz<$",[]))), + <<":3">> = iolist_to_binary(join(re:split(">abc>1(2)3<xyz<","^>abc>([^()]|\\((?1)*\\))*<xyz<$",[trim]))), <<":3:">> = iolist_to_binary(join(re:split(">abc>1(2)3<xyz<","^>abc>([^()]|\\((?1)*\\))*<xyz<$",[{parts, - 2}]))), - <<":3:">> = iolist_to_binary(join(re:split(">abc>1(2)3<xyz<","^>abc>([^()]|\\((?1)*\\))*<xyz<$",[]))), - <<":(1(2)3)">> = iolist_to_binary(join(re:split(">abc>(1(2)3)<xyz<","^>abc>([^()]|\\((?1)*\\))*<xyz<$",[trim]))), + 2}]))), + <<":3:">> = iolist_to_binary(join(re:split(">abc>1(2)3<xyz<","^>abc>([^()]|\\((?1)*\\))*<xyz<$",[]))), + <<":(1(2)3)">> = iolist_to_binary(join(re:split(">abc>(1(2)3)<xyz<","^>abc>([^()]|\\((?1)*\\))*<xyz<$",[trim]))), <<":(1(2)3):">> = iolist_to_binary(join(re:split(">abc>(1(2)3)<xyz<","^>abc>([^()]|\\((?1)*\\))*<xyz<$",[{parts, - 2}]))), - <<":(1(2)3):">> = iolist_to_binary(join(re:split(">abc>(1(2)3)<xyz<","^>abc>([^()]|\\((?1)*\\))*<xyz<$",[]))), + 2}]))), + <<":(1(2)3):">> = iolist_to_binary(join(re:split(">abc>(1(2)3)<xyz<","^>abc>([^()]|\\((?1)*\\))*<xyz<$",[]))), <<":1221:1">> = iolist_to_binary(join(re:split("1221","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless, - trim]))), + trim]))), <<":1221:1:::">> = iolist_to_binary(join(re:split("1221","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless, {parts, - 2}]))), - <<":1221:1:::">> = iolist_to_binary(join(re:split("1221","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless]))), + 2}]))), + <<":1221:1:::">> = iolist_to_binary(join(re:split("1221","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless]))), <<":::Satanoscillatemymetallicsonatas:S">> = iolist_to_binary(join(re:split("Satanoscillatemymetallicsonatas","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless, - trim]))), + trim]))), <<":::Satanoscillatemymetallicsonatas:S:">> = iolist_to_binary(join(re:split("Satanoscillatemymetallicsonatas","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless, {parts, - 2}]))), - <<":::Satanoscillatemymetallicsonatas:S:">> = iolist_to_binary(join(re:split("Satanoscillatemymetallicsonatas","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless]))), + 2}]))), + <<":::Satanoscillatemymetallicsonatas:S:">> = iolist_to_binary(join(re:split("Satanoscillatemymetallicsonatas","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless]))), <<":::AmanaplanacanalPanama:A">> = iolist_to_binary(join(re:split("AmanaplanacanalPanama","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless, - trim]))), + trim]))), <<":::AmanaplanacanalPanama:A:">> = iolist_to_binary(join(re:split("AmanaplanacanalPanama","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless, {parts, - 2}]))), - <<":::AmanaplanacanalPanama:A:">> = iolist_to_binary(join(re:split("AmanaplanacanalPanama","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless]))), + 2}]))), + <<":::AmanaplanacanalPanama:A:">> = iolist_to_binary(join(re:split("AmanaplanacanalPanama","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless]))), <<":::AblewasIereIsawElba:A">> = iolist_to_binary(join(re:split("AblewasIereIsawElba","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless, - trim]))), + trim]))), <<":::AblewasIereIsawElba:A:">> = iolist_to_binary(join(re:split("AblewasIereIsawElba","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless, {parts, - 2}]))), - <<":::AblewasIereIsawElba:A:">> = iolist_to_binary(join(re:split("AblewasIereIsawElba","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless]))), + 2}]))), + <<":::AblewasIereIsawElba:A:">> = iolist_to_binary(join(re:split("AblewasIereIsawElba","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless]))), <<"Thequickbrownfox">> = iolist_to_binary(join(re:split("Thequickbrownfox","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless, - trim]))), + trim]))), <<"Thequickbrownfox">> = iolist_to_binary(join(re:split("Thequickbrownfox","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless, {parts, - 2}]))), - <<"Thequickbrownfox">> = iolist_to_binary(join(re:split("Thequickbrownfox","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless]))), - <<":12">> = iolist_to_binary(join(re:split("12","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[trim]))), + 2}]))), + <<"Thequickbrownfox">> = iolist_to_binary(join(re:split("Thequickbrownfox","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless]))), + <<":12">> = iolist_to_binary(join(re:split("12","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[trim]))), <<":12::">> = iolist_to_binary(join(re:split("12","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[{parts, - 2}]))), - <<":12::">> = iolist_to_binary(join(re:split("12","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[]))), - <<":(((2+2)*-3)-7):-">> = iolist_to_binary(join(re:split("(((2+2)*-3)-7)","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[trim]))), + 2}]))), + <<":12::">> = iolist_to_binary(join(re:split("12","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[]))), + <<":(((2+2)*-3)-7):-">> = iolist_to_binary(join(re:split("(((2+2)*-3)-7)","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[trim]))), <<":(((2+2)*-3)-7):-:">> = iolist_to_binary(join(re:split("(((2+2)*-3)-7)","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[{parts, - 2}]))), - <<":(((2+2)*-3)-7):-:">> = iolist_to_binary(join(re:split("(((2+2)*-3)-7)","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[]))), - <<":-12">> = iolist_to_binary(join(re:split("-12","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[trim]))), + 2}]))), + <<":(((2+2)*-3)-7):-:">> = iolist_to_binary(join(re:split("(((2+2)*-3)-7)","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[]))), + <<":-12">> = iolist_to_binary(join(re:split("-12","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[trim]))), <<":-12::">> = iolist_to_binary(join(re:split("-12","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[{parts, - 2}]))), - <<":-12::">> = iolist_to_binary(join(re:split("-12","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[trim]))), + 2}]))), + <<":-12::">> = iolist_to_binary(join(re:split("-12","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[]))), - <<"((2+2)*-3)-7)">> = iolist_to_binary(join(re:split("((2+2)*-3)-7)","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[]))), + <<"((2+2)*-3)-7)">> = iolist_to_binary(join(re:split("((2+2)*-3)-7)","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[trim]))), <<"((2+2)*-3)-7)">> = iolist_to_binary(join(re:split("((2+2)*-3)-7)","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[{parts, - 2}]))), - <<"((2+2)*-3)-7)">> = iolist_to_binary(join(re:split("((2+2)*-3)-7)","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[]))), - <<":xyz:y">> = iolist_to_binary(join(re:split("xyz","^(x(y|(?1){2})z)",[trim]))), + 2}]))), + <<"((2+2)*-3)-7)">> = iolist_to_binary(join(re:split("((2+2)*-3)-7)","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[]))), + <<":xyz:y">> = iolist_to_binary(join(re:split("xyz","^(x(y|(?1){2})z)",[trim]))), <<":xyz:y:">> = iolist_to_binary(join(re:split("xyz","^(x(y|(?1){2})z)",[{parts, - 2}]))), - <<":xyz:y:">> = iolist_to_binary(join(re:split("xyz","^(x(y|(?1){2})z)",[]))), - <<":xxyzxyzz:xyzxyz">> = iolist_to_binary(join(re:split("xxyzxyzz","^(x(y|(?1){2})z)",[trim]))), + 2}]))), + <<":xyz:y:">> = iolist_to_binary(join(re:split("xyz","^(x(y|(?1){2})z)",[]))), + <<":xxyzxyzz:xyzxyz">> = iolist_to_binary(join(re:split("xxyzxyzz","^(x(y|(?1){2})z)",[trim]))), <<":xxyzxyzz:xyzxyz:">> = iolist_to_binary(join(re:split("xxyzxyzz","^(x(y|(?1){2})z)",[{parts, - 2}]))), - <<":xxyzxyzz:xyzxyz:">> = iolist_to_binary(join(re:split("xxyzxyzz","^(x(y|(?1){2})z)",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(x(y|(?1){2})z)",[trim]))), + 2}]))), + <<":xxyzxyzz:xyzxyz:">> = iolist_to_binary(join(re:split("xxyzxyzz","^(x(y|(?1){2})z)",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(x(y|(?1){2})z)",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(x(y|(?1){2})z)",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(x(y|(?1){2})z)",[]))), - <<"xxyzz">> = iolist_to_binary(join(re:split("xxyzz","^(x(y|(?1){2})z)",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(x(y|(?1){2})z)",[]))), + <<"xxyzz">> = iolist_to_binary(join(re:split("xxyzz","^(x(y|(?1){2})z)",[trim]))), <<"xxyzz">> = iolist_to_binary(join(re:split("xxyzz","^(x(y|(?1){2})z)",[{parts, - 2}]))), - <<"xxyzz">> = iolist_to_binary(join(re:split("xxyzz","^(x(y|(?1){2})z)",[]))), - <<"xxyzxyzxyzz">> = iolist_to_binary(join(re:split("xxyzxyzxyzz","^(x(y|(?1){2})z)",[trim]))), + 2}]))), + <<"xxyzz">> = iolist_to_binary(join(re:split("xxyzz","^(x(y|(?1){2})z)",[]))), + <<"xxyzxyzxyzz">> = iolist_to_binary(join(re:split("xxyzxyzxyzz","^(x(y|(?1){2})z)",[trim]))), <<"xxyzxyzxyzz">> = iolist_to_binary(join(re:split("xxyzxyzxyzz","^(x(y|(?1){2})z)",[{parts, - 2}]))), - <<"xxyzxyzxyzz">> = iolist_to_binary(join(re:split("xxyzxyzxyzz","^(x(y|(?1){2})z)",[]))), + 2}]))), + <<"xxyzxyzxyzz">> = iolist_to_binary(join(re:split("xxyzxyzxyzz","^(x(y|(?1){2})z)",[]))), <<":<>:<>">> = iolist_to_binary(join(re:split("<>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended, - trim]))), + trim]))), <<":<>:<>:">> = iolist_to_binary(join(re:split("<>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended, {parts, - 2}]))), - <<":<>:<>:">> = iolist_to_binary(join(re:split("<>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended]))), + 2}]))), + <<":<>:<>:">> = iolist_to_binary(join(re:split("<>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended]))), <<":<abcd>:<abcd>">> = iolist_to_binary(join(re:split("<abcd>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended, - trim]))), + trim]))), <<":<abcd>:<abcd>:">> = iolist_to_binary(join(re:split("<abcd>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended, {parts, - 2}]))), - <<":<abcd>:<abcd>:">> = iolist_to_binary(join(re:split("<abcd>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended]))), + 2}]))), + <<":<abcd>:<abcd>:">> = iolist_to_binary(join(re:split("<abcd>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended]))), <<":<abc <123> hij>:<abc <123> hij>">> = iolist_to_binary(join(re:split("<abc <123> hij>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended, - trim]))), + trim]))), <<":<abc <123> hij>:<abc <123> hij>:">> = iolist_to_binary(join(re:split("<abc <123> hij>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended, {parts, - 2}]))), - <<":<abc <123> hij>:<abc <123> hij>:">> = iolist_to_binary(join(re:split("<abc <123> hij>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended]))), + 2}]))), + <<":<abc <123> hij>:<abc <123> hij>:">> = iolist_to_binary(join(re:split("<abc <123> hij>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended]))), <<"<abc :<def>:<def>: hij>">> = iolist_to_binary(join(re:split("<abc <def> hij>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended, - trim]))), + trim]))), <<"<abc :<def>:<def>: hij>">> = iolist_to_binary(join(re:split("<abc <def> hij>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended, {parts, - 2}]))), - <<"<abc :<def>:<def>: hij>">> = iolist_to_binary(join(re:split("<abc <def> hij>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended]))), + 2}]))), + <<"<abc :<def>:<def>: hij>">> = iolist_to_binary(join(re:split("<abc <def> hij>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended]))), <<":<abc<>def>:<abc<>def>">> = iolist_to_binary(join(re:split("<abc<>def>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended, - trim]))), + trim]))), <<":<abc<>def>:<abc<>def>:">> = iolist_to_binary(join(re:split("<abc<>def>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended, {parts, - 2}]))), - <<":<abc<>def>:<abc<>def>:">> = iolist_to_binary(join(re:split("<abc<>def>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended]))), + 2}]))), + <<":<abc<>def>:<abc<>def>:">> = iolist_to_binary(join(re:split("<abc<>def>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended]))), <<"<abc:<>:<>">> = iolist_to_binary(join(re:split("<abc<>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended, - trim]))), + trim]))), <<"<abc:<>:<>:">> = iolist_to_binary(join(re:split("<abc<>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended, {parts, - 2}]))), - <<"<abc:<>:<>:">> = iolist_to_binary(join(re:split("<abc<>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended]))), + 2}]))), + <<"<abc:<>:<>:">> = iolist_to_binary(join(re:split("<abc<>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended]))), <<"<abc">> = iolist_to_binary(join(re:split("<abc","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended, - trim]))), + trim]))), <<"<abc">> = iolist_to_binary(join(re:split("<abc","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended, {parts, - 2}]))), - <<"<abc">> = iolist_to_binary(join(re:split("<abc","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended]))), - <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^a+(*FAIL)",[trim]))), + 2}]))), + <<"<abc">> = iolist_to_binary(join(re:split("<abc","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended]))), + <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^a+(*FAIL)",[trim]))), <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^a+(*FAIL)",[{parts, - 2}]))), - <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^a+(*FAIL)",[]))), - <<"aaabccc">> = iolist_to_binary(join(re:split("aaabccc","a+b?c+(*FAIL)",[trim]))), + 2}]))), + <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^a+(*FAIL)",[]))), + <<"aaabccc">> = iolist_to_binary(join(re:split("aaabccc","a+b?c+(*FAIL)",[trim]))), <<"aaabccc">> = iolist_to_binary(join(re:split("aaabccc","a+b?c+(*FAIL)",[{parts, - 2}]))), - <<"aaabccc">> = iolist_to_binary(join(re:split("aaabccc","a+b?c+(*FAIL)",[]))), - <<"aaabccc">> = iolist_to_binary(join(re:split("aaabccc","a+b?(*PRUNE)c+(*FAIL)",[trim]))), + 2}]))), + <<"aaabccc">> = iolist_to_binary(join(re:split("aaabccc","a+b?c+(*FAIL)",[]))), + <<"aaabccc">> = iolist_to_binary(join(re:split("aaabccc","a+b?(*PRUNE)c+(*FAIL)",[trim]))), <<"aaabccc">> = iolist_to_binary(join(re:split("aaabccc","a+b?(*PRUNE)c+(*FAIL)",[{parts, - 2}]))), - <<"aaabccc">> = iolist_to_binary(join(re:split("aaabccc","a+b?(*PRUNE)c+(*FAIL)",[]))), - <<"aaabccc">> = iolist_to_binary(join(re:split("aaabccc","a+b?(*COMMIT)c+(*FAIL)",[trim]))), + 2}]))), + <<"aaabccc">> = iolist_to_binary(join(re:split("aaabccc","a+b?(*PRUNE)c+(*FAIL)",[]))), + <<"aaabccc">> = iolist_to_binary(join(re:split("aaabccc","a+b?(*COMMIT)c+(*FAIL)",[trim]))), <<"aaabccc">> = iolist_to_binary(join(re:split("aaabccc","a+b?(*COMMIT)c+(*FAIL)",[{parts, - 2}]))), - <<"aaabccc">> = iolist_to_binary(join(re:split("aaabccc","a+b?(*COMMIT)c+(*FAIL)",[]))), - <<"aaabcccaaabccc">> = iolist_to_binary(join(re:split("aaabcccaaabccc","a+b?(*SKIP)c+(*FAIL)",[trim]))), + 2}]))), + <<"aaabccc">> = iolist_to_binary(join(re:split("aaabccc","a+b?(*COMMIT)c+(*FAIL)",[]))), + <<"aaabcccaaabccc">> = iolist_to_binary(join(re:split("aaabcccaaabccc","a+b?(*SKIP)c+(*FAIL)",[trim]))), <<"aaabcccaaabccc">> = iolist_to_binary(join(re:split("aaabcccaaabccc","a+b?(*SKIP)c+(*FAIL)",[{parts, - 2}]))), - <<"aaabcccaaabccc">> = iolist_to_binary(join(re:split("aaabcccaaabccc","a+b?(*SKIP)c+(*FAIL)",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaxxxxxx","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), + 2}]))), + <<"aaabcccaaabccc">> = iolist_to_binary(join(re:split("aaabcccaaabccc","a+b?(*SKIP)c+(*FAIL)",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaxxxxxx","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaxxxxxx","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaxxxxxx","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), - <<":++++++">> = iolist_to_binary(join(re:split("aaa++++++","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaxxxxxx","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), + <<":++++++">> = iolist_to_binary(join(re:split("aaa++++++","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), <<":++++++">> = iolist_to_binary(join(re:split("aaa++++++","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[{parts, - 2}]))), - <<":++++++">> = iolist_to_binary(join(re:split("aaa++++++","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), - <<"">> = iolist_to_binary(join(re:split("bbbxxxxx","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), + 2}]))), + <<":++++++">> = iolist_to_binary(join(re:split("aaa++++++","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), + <<"">> = iolist_to_binary(join(re:split("bbbxxxxx","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), <<":">> = iolist_to_binary(join(re:split("bbbxxxxx","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("bbbxxxxx","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), - <<":+++++">> = iolist_to_binary(join(re:split("bbb+++++","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("bbbxxxxx","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), + <<":+++++">> = iolist_to_binary(join(re:split("bbb+++++","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), <<":+++++">> = iolist_to_binary(join(re:split("bbb+++++","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[{parts, - 2}]))), - <<":+++++">> = iolist_to_binary(join(re:split("bbb+++++","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), - <<"">> = iolist_to_binary(join(re:split("cccxxxx","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), + 2}]))), + <<":+++++">> = iolist_to_binary(join(re:split("bbb+++++","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), + <<"">> = iolist_to_binary(join(re:split("cccxxxx","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), <<":">> = iolist_to_binary(join(re:split("cccxxxx","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("cccxxxx","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), - <<":++++">> = iolist_to_binary(join(re:split("ccc++++","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("cccxxxx","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), + <<":++++">> = iolist_to_binary(join(re:split("ccc++++","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), <<":++++">> = iolist_to_binary(join(re:split("ccc++++","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[{parts, - 2}]))), - <<":++++">> = iolist_to_binary(join(re:split("ccc++++","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), - <<":ddddd">> = iolist_to_binary(join(re:split("dddddddd","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), + 2}]))), + <<":++++">> = iolist_to_binary(join(re:split("ccc++++","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), + <<":ddddd">> = iolist_to_binary(join(re:split("dddddddd","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), <<":ddddd">> = iolist_to_binary(join(re:split("dddddddd","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[{parts, - 2}]))), - <<":ddddd">> = iolist_to_binary(join(re:split("dddddddd","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), - <<":aaaxxxxxx">> = iolist_to_binary(join(re:split("aaaxxxxxx","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), + 2}]))), + <<":ddddd">> = iolist_to_binary(join(re:split("dddddddd","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), + <<":aaaxxxxxx">> = iolist_to_binary(join(re:split("aaaxxxxxx","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), <<":aaaxxxxxx:">> = iolist_to_binary(join(re:split("aaaxxxxxx","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[{parts, - 2}]))), - <<":aaaxxxxxx:">> = iolist_to_binary(join(re:split("aaaxxxxxx","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), - <<":aaa:++++++">> = iolist_to_binary(join(re:split("aaa++++++","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), + 2}]))), + <<":aaaxxxxxx:">> = iolist_to_binary(join(re:split("aaaxxxxxx","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), + <<":aaa:++++++">> = iolist_to_binary(join(re:split("aaa++++++","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), <<":aaa:++++++">> = iolist_to_binary(join(re:split("aaa++++++","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[{parts, - 2}]))), - <<":aaa:++++++">> = iolist_to_binary(join(re:split("aaa++++++","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), - <<":bbbxxxxx">> = iolist_to_binary(join(re:split("bbbxxxxx","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), + 2}]))), + <<":aaa:++++++">> = iolist_to_binary(join(re:split("aaa++++++","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), + <<":bbbxxxxx">> = iolist_to_binary(join(re:split("bbbxxxxx","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), <<":bbbxxxxx:">> = iolist_to_binary(join(re:split("bbbxxxxx","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[{parts, - 2}]))), - <<":bbbxxxxx:">> = iolist_to_binary(join(re:split("bbbxxxxx","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), - <<":bbb:+++++">> = iolist_to_binary(join(re:split("bbb+++++","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), + 2}]))), + <<":bbbxxxxx:">> = iolist_to_binary(join(re:split("bbbxxxxx","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), + <<":bbb:+++++">> = iolist_to_binary(join(re:split("bbb+++++","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), <<":bbb:+++++">> = iolist_to_binary(join(re:split("bbb+++++","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[{parts, - 2}]))), - <<":bbb:+++++">> = iolist_to_binary(join(re:split("bbb+++++","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), - <<":cccxxxx">> = iolist_to_binary(join(re:split("cccxxxx","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), + 2}]))), + <<":bbb:+++++">> = iolist_to_binary(join(re:split("bbb+++++","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), + <<":cccxxxx">> = iolist_to_binary(join(re:split("cccxxxx","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), <<":cccxxxx:">> = iolist_to_binary(join(re:split("cccxxxx","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[{parts, - 2}]))), - <<":cccxxxx:">> = iolist_to_binary(join(re:split("cccxxxx","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), - <<":ccc:++++">> = iolist_to_binary(join(re:split("ccc++++","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), + 2}]))), + <<":cccxxxx:">> = iolist_to_binary(join(re:split("cccxxxx","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), + <<":ccc:++++">> = iolist_to_binary(join(re:split("ccc++++","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), <<":ccc:++++">> = iolist_to_binary(join(re:split("ccc++++","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[{parts, - 2}]))), - <<":ccc:++++">> = iolist_to_binary(join(re:split("ccc++++","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), - <<":ddd:ddddd">> = iolist_to_binary(join(re:split("dddddddd","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), + 2}]))), + <<":ccc:++++">> = iolist_to_binary(join(re:split("ccc++++","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), + <<":ddd:ddddd">> = iolist_to_binary(join(re:split("dddddddd","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), <<":ddd:ddddd">> = iolist_to_binary(join(re:split("dddddddd","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[{parts, - 2}]))), - <<":ddd:ddddd">> = iolist_to_binary(join(re:split("dddddddd","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), - <<"aaabccc">> = iolist_to_binary(join(re:split("aaabccc","a+b?(*THEN)c+(*FAIL)",[trim]))), + 2}]))), + <<":ddd:ddddd">> = iolist_to_binary(join(re:split("dddddddd","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), + <<"aaabccc">> = iolist_to_binary(join(re:split("aaabccc","a+b?(*THEN)c+(*FAIL)",[trim]))), <<"aaabccc">> = iolist_to_binary(join(re:split("aaabccc","a+b?(*THEN)c+(*FAIL)",[{parts, - 2}]))), - <<"aaabccc">> = iolist_to_binary(join(re:split("aaabccc","a+b?(*THEN)c+(*FAIL)",[]))), + 2}]))), + <<"aaabccc">> = iolist_to_binary(join(re:split("aaabccc","a+b?(*THEN)c+(*FAIL)",[]))), <<":AB:B">> = iolist_to_binary(join(re:split("AB","(A (A|B(*ACCEPT)|C) D)(E)",[extended, - trim]))), + trim]))), <<":AB:B::">> = iolist_to_binary(join(re:split("AB","(A (A|B(*ACCEPT)|C) D)(E)",[extended, {parts, - 2}]))), - <<":AB:B::">> = iolist_to_binary(join(re:split("AB","(A (A|B(*ACCEPT)|C) D)(E)",[extended]))), + 2}]))), + <<":AB:B::">> = iolist_to_binary(join(re:split("AB","(A (A|B(*ACCEPT)|C) D)(E)",[extended]))), <<":AB:B::X">> = iolist_to_binary(join(re:split("ABX","(A (A|B(*ACCEPT)|C) D)(E)",[extended, - trim]))), + trim]))), <<":AB:B::X">> = iolist_to_binary(join(re:split("ABX","(A (A|B(*ACCEPT)|C) D)(E)",[extended, {parts, - 2}]))), - <<":AB:B::X">> = iolist_to_binary(join(re:split("ABX","(A (A|B(*ACCEPT)|C) D)(E)",[extended]))), + 2}]))), + <<":AB:B::X">> = iolist_to_binary(join(re:split("ABX","(A (A|B(*ACCEPT)|C) D)(E)",[extended]))), <<":AAD:A:E">> = iolist_to_binary(join(re:split("AADE","(A (A|B(*ACCEPT)|C) D)(E)",[extended, - trim]))), + trim]))), <<":AAD:A:E:">> = iolist_to_binary(join(re:split("AADE","(A (A|B(*ACCEPT)|C) D)(E)",[extended, {parts, - 2}]))), - <<":AAD:A:E:">> = iolist_to_binary(join(re:split("AADE","(A (A|B(*ACCEPT)|C) D)(E)",[extended]))), + 2}]))), + <<":AAD:A:E:">> = iolist_to_binary(join(re:split("AADE","(A (A|B(*ACCEPT)|C) D)(E)",[extended]))), <<":ACD:C:E">> = iolist_to_binary(join(re:split("ACDE","(A (A|B(*ACCEPT)|C) D)(E)",[extended, - trim]))), + trim]))), <<":ACD:C:E:">> = iolist_to_binary(join(re:split("ACDE","(A (A|B(*ACCEPT)|C) D)(E)",[extended, {parts, - 2}]))), - <<":ACD:C:E:">> = iolist_to_binary(join(re:split("ACDE","(A (A|B(*ACCEPT)|C) D)(E)",[extended]))), + 2}]))), + <<":ACD:C:E:">> = iolist_to_binary(join(re:split("ACDE","(A (A|B(*ACCEPT)|C) D)(E)",[extended]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(A (A|B(*ACCEPT)|C) D)(E)",[extended, - trim]))), + trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(A (A|B(*ACCEPT)|C) D)(E)",[extended, {parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(A (A|B(*ACCEPT)|C) D)(E)",[extended]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(A (A|B(*ACCEPT)|C) D)(E)",[extended]))), <<"AD">> = iolist_to_binary(join(re:split("AD","(A (A|B(*ACCEPT)|C) D)(E)",[extended, - trim]))), + trim]))), <<"AD">> = iolist_to_binary(join(re:split("AD","(A (A|B(*ACCEPT)|C) D)(E)",[extended, {parts, - 2}]))), - <<"AD">> = iolist_to_binary(join(re:split("AD","(A (A|B(*ACCEPT)|C) D)(E)",[extended]))), + 2}]))), + <<"AD">> = iolist_to_binary(join(re:split("AD","(A (A|B(*ACCEPT)|C) D)(E)",[extended]))), <<":1221:1">> = iolist_to_binary(join(re:split("1221","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless, - trim]))), + trim]))), <<":1221:1:::">> = iolist_to_binary(join(re:split("1221","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless, {parts, - 2}]))), - <<":1221:1:::">> = iolist_to_binary(join(re:split("1221","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless]))), + 2}]))), + <<":1221:1:::">> = iolist_to_binary(join(re:split("1221","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless]))), <<":::Satan, oscillate my metallic sonatas:S">> = iolist_to_binary(join(re:split("Satan, oscillate my metallic sonatas!","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless, - trim]))), + trim]))), <<":::Satan, oscillate my metallic sonatas:S:">> = iolist_to_binary(join(re:split("Satan, oscillate my metallic sonatas!","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless, {parts, - 2}]))), - <<":::Satan, oscillate my metallic sonatas:S:">> = iolist_to_binary(join(re:split("Satan, oscillate my metallic sonatas!","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless]))), + 2}]))), + <<":::Satan, oscillate my metallic sonatas:S:">> = iolist_to_binary(join(re:split("Satan, oscillate my metallic sonatas!","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless]))), <<":::A man, a plan, a canal: Panama:A">> = iolist_to_binary(join(re:split("A man, a plan, a canal: Panama!","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless, - trim]))), + trim]))), <<":::A man, a plan, a canal: Panama:A:">> = iolist_to_binary(join(re:split("A man, a plan, a canal: Panama!","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless, {parts, - 2}]))), - <<":::A man, a plan, a canal: Panama:A:">> = iolist_to_binary(join(re:split("A man, a plan, a canal: Panama!","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless]))), + 2}]))), + <<":::A man, a plan, a canal: Panama:A:">> = iolist_to_binary(join(re:split("A man, a plan, a canal: Panama!","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless]))), <<":::Able was I ere I saw Elba:A">> = iolist_to_binary(join(re:split("Able was I ere I saw Elba.","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless, - trim]))), + trim]))), <<":::Able was I ere I saw Elba:A:">> = iolist_to_binary(join(re:split("Able was I ere I saw Elba.","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless, {parts, - 2}]))), - <<":::Able was I ere I saw Elba:A:">> = iolist_to_binary(join(re:split("Able was I ere I saw Elba.","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless]))), + 2}]))), + <<":::Able was I ere I saw Elba:A:">> = iolist_to_binary(join(re:split("Able was I ere I saw Elba.","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless]))), <<"The quick brown fox">> = iolist_to_binary(join(re:split("The quick brown fox","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless, - trim]))), + trim]))), <<"The quick brown fox">> = iolist_to_binary(join(re:split("The quick brown fox","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless, {parts, - 2}]))), - <<"The quick brown fox">> = iolist_to_binary(join(re:split("The quick brown fox","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless]))), - <<":a">> = iolist_to_binary(join(re:split("a","^((.)(?1)\\2|.)$",[trim]))), + 2}]))), + <<"The quick brown fox">> = iolist_to_binary(join(re:split("The quick brown fox","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless]))), + <<":a">> = iolist_to_binary(join(re:split("a","^((.)(?1)\\2|.)$",[trim]))), <<":a::">> = iolist_to_binary(join(re:split("a","^((.)(?1)\\2|.)$",[{parts, - 2}]))), - <<":a::">> = iolist_to_binary(join(re:split("a","^((.)(?1)\\2|.)$",[]))), - <<":aba:a">> = iolist_to_binary(join(re:split("aba","^((.)(?1)\\2|.)$",[trim]))), + 2}]))), + <<":a::">> = iolist_to_binary(join(re:split("a","^((.)(?1)\\2|.)$",[]))), + <<":aba:a">> = iolist_to_binary(join(re:split("aba","^((.)(?1)\\2|.)$",[trim]))), <<":aba:a:">> = iolist_to_binary(join(re:split("aba","^((.)(?1)\\2|.)$",[{parts, - 2}]))), - <<":aba:a:">> = iolist_to_binary(join(re:split("aba","^((.)(?1)\\2|.)$",[]))), - <<":aabaa:a">> = iolist_to_binary(join(re:split("aabaa","^((.)(?1)\\2|.)$",[trim]))), + 2}]))), + <<":aba:a:">> = iolist_to_binary(join(re:split("aba","^((.)(?1)\\2|.)$",[]))), + <<":aabaa:a">> = iolist_to_binary(join(re:split("aabaa","^((.)(?1)\\2|.)$",[trim]))), <<":aabaa:a:">> = iolist_to_binary(join(re:split("aabaa","^((.)(?1)\\2|.)$",[{parts, - 2}]))), - <<":aabaa:a:">> = iolist_to_binary(join(re:split("aabaa","^((.)(?1)\\2|.)$",[]))), - <<":abcdcba:a">> = iolist_to_binary(join(re:split("abcdcba","^((.)(?1)\\2|.)$",[trim]))), + 2}]))), + <<":aabaa:a:">> = iolist_to_binary(join(re:split("aabaa","^((.)(?1)\\2|.)$",[]))), + <<":abcdcba:a">> = iolist_to_binary(join(re:split("abcdcba","^((.)(?1)\\2|.)$",[trim]))), <<":abcdcba:a:">> = iolist_to_binary(join(re:split("abcdcba","^((.)(?1)\\2|.)$",[{parts, - 2}]))), - <<":abcdcba:a:">> = iolist_to_binary(join(re:split("abcdcba","^((.)(?1)\\2|.)$",[]))), - <<":pqaabaaqp:p">> = iolist_to_binary(join(re:split("pqaabaaqp","^((.)(?1)\\2|.)$",[trim]))), + 2}]))), + <<":abcdcba:a:">> = iolist_to_binary(join(re:split("abcdcba","^((.)(?1)\\2|.)$",[]))), + <<":pqaabaaqp:p">> = iolist_to_binary(join(re:split("pqaabaaqp","^((.)(?1)\\2|.)$",[trim]))), <<":pqaabaaqp:p:">> = iolist_to_binary(join(re:split("pqaabaaqp","^((.)(?1)\\2|.)$",[{parts, - 2}]))), - <<":pqaabaaqp:p:">> = iolist_to_binary(join(re:split("pqaabaaqp","^((.)(?1)\\2|.)$",[]))), - <<":ablewasiereisawelba:a">> = iolist_to_binary(join(re:split("ablewasiereisawelba","^((.)(?1)\\2|.)$",[trim]))), + 2}]))), + <<":pqaabaaqp:p:">> = iolist_to_binary(join(re:split("pqaabaaqp","^((.)(?1)\\2|.)$",[]))), + <<":ablewasiereisawelba:a">> = iolist_to_binary(join(re:split("ablewasiereisawelba","^((.)(?1)\\2|.)$",[trim]))), <<":ablewasiereisawelba:a:">> = iolist_to_binary(join(re:split("ablewasiereisawelba","^((.)(?1)\\2|.)$",[{parts, - 2}]))), - <<":ablewasiereisawelba:a:">> = iolist_to_binary(join(re:split("ablewasiereisawelba","^((.)(?1)\\2|.)$",[]))), - <<"rhubarb">> = iolist_to_binary(join(re:split("rhubarb","^((.)(?1)\\2|.)$",[trim]))), + 2}]))), + <<":ablewasiereisawelba:a:">> = iolist_to_binary(join(re:split("ablewasiereisawelba","^((.)(?1)\\2|.)$",[]))), + <<"rhubarb">> = iolist_to_binary(join(re:split("rhubarb","^((.)(?1)\\2|.)$",[trim]))), <<"rhubarb">> = iolist_to_binary(join(re:split("rhubarb","^((.)(?1)\\2|.)$",[{parts, - 2}]))), - <<"rhubarb">> = iolist_to_binary(join(re:split("rhubarb","^((.)(?1)\\2|.)$",[]))), - <<"the quick brown fox">> = iolist_to_binary(join(re:split("the quick brown fox","^((.)(?1)\\2|.)$",[trim]))), + 2}]))), + <<"rhubarb">> = iolist_to_binary(join(re:split("rhubarb","^((.)(?1)\\2|.)$",[]))), + <<"the quick brown fox">> = iolist_to_binary(join(re:split("the quick brown fox","^((.)(?1)\\2|.)$",[trim]))), <<"the quick brown fox">> = iolist_to_binary(join(re:split("the quick brown fox","^((.)(?1)\\2|.)$",[{parts, - 2}]))), - <<"the quick brown fox">> = iolist_to_binary(join(re:split("the quick brown fox","^((.)(?1)\\2|.)$",[]))), - <<"b:a:z">> = iolist_to_binary(join(re:split("baz","(a)(?<=b(?1))",[trim]))), + 2}]))), + <<"the quick brown fox">> = iolist_to_binary(join(re:split("the quick brown fox","^((.)(?1)\\2|.)$",[]))), + <<"b:a:z">> = iolist_to_binary(join(re:split("baz","(a)(?<=b(?1))",[trim]))), <<"b:a:z">> = iolist_to_binary(join(re:split("baz","(a)(?<=b(?1))",[{parts, - 2}]))), - <<"b:a:z">> = iolist_to_binary(join(re:split("baz","(a)(?<=b(?1))",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(a)(?<=b(?1))",[trim]))), + 2}]))), + <<"b:a:z">> = iolist_to_binary(join(re:split("baz","(a)(?<=b(?1))",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(a)(?<=b(?1))",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(a)(?<=b(?1))",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(a)(?<=b(?1))",[]))), - <<"caz">> = iolist_to_binary(join(re:split("caz","(a)(?<=b(?1))",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(a)(?<=b(?1))",[]))), + <<"caz">> = iolist_to_binary(join(re:split("caz","(a)(?<=b(?1))",[trim]))), <<"caz">> = iolist_to_binary(join(re:split("caz","(a)(?<=b(?1))",[{parts, - 2}]))), - <<"caz">> = iolist_to_binary(join(re:split("caz","(a)(?<=b(?1))",[]))), - <<"zba:a:z">> = iolist_to_binary(join(re:split("zbaaz","(?<=b(?1))(a)",[trim]))), + 2}]))), + <<"caz">> = iolist_to_binary(join(re:split("caz","(a)(?<=b(?1))",[]))), + <<"zba:a:z">> = iolist_to_binary(join(re:split("zbaaz","(?<=b(?1))(a)",[trim]))), <<"zba:a:z">> = iolist_to_binary(join(re:split("zbaaz","(?<=b(?1))(a)",[{parts, - 2}]))), - <<"zba:a:z">> = iolist_to_binary(join(re:split("zbaaz","(?<=b(?1))(a)",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=b(?1))(a)",[trim]))), + 2}]))), + <<"zba:a:z">> = iolist_to_binary(join(re:split("zbaaz","(?<=b(?1))(a)",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=b(?1))(a)",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=b(?1))(a)",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=b(?1))(a)",[]))), - <<"aaa">> = iolist_to_binary(join(re:split("aaa","(?<=b(?1))(a)",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=b(?1))(a)",[]))), + <<"aaa">> = iolist_to_binary(join(re:split("aaa","(?<=b(?1))(a)",[trim]))), <<"aaa">> = iolist_to_binary(join(re:split("aaa","(?<=b(?1))(a)",[{parts, - 2}]))), - <<"aaa">> = iolist_to_binary(join(re:split("aaa","(?<=b(?1))(a)",[]))), - <<"b:a:z">> = iolist_to_binary(join(re:split("baz","(?<X>a)(?<=b(?&X))",[trim]))), + 2}]))), + <<"aaa">> = iolist_to_binary(join(re:split("aaa","(?<=b(?1))(a)",[]))), + <<"b:a:z">> = iolist_to_binary(join(re:split("baz","(?<X>a)(?<=b(?&X))",[trim]))), <<"b:a:z">> = iolist_to_binary(join(re:split("baz","(?<X>a)(?<=b(?&X))",[{parts, - 2}]))), - <<"b:a:z">> = iolist_to_binary(join(re:split("baz","(?<X>a)(?<=b(?&X))",[]))), - <<":abc">> = iolist_to_binary(join(re:split("abcabc","^(?|(abc)|(def))\\1",[trim]))), + 2}]))), + <<"b:a:z">> = iolist_to_binary(join(re:split("baz","(?<X>a)(?<=b(?&X))",[]))), + <<":abc">> = iolist_to_binary(join(re:split("abcabc","^(?|(abc)|(def))\\1",[trim]))), <<":abc:">> = iolist_to_binary(join(re:split("abcabc","^(?|(abc)|(def))\\1",[{parts, - 2}]))), - <<":abc:">> = iolist_to_binary(join(re:split("abcabc","^(?|(abc)|(def))\\1",[]))), - <<":def">> = iolist_to_binary(join(re:split("defdef","^(?|(abc)|(def))\\1",[trim]))), + 2}]))), + <<":abc:">> = iolist_to_binary(join(re:split("abcabc","^(?|(abc)|(def))\\1",[]))), + <<":def">> = iolist_to_binary(join(re:split("defdef","^(?|(abc)|(def))\\1",[trim]))), <<":def:">> = iolist_to_binary(join(re:split("defdef","^(?|(abc)|(def))\\1",[{parts, - 2}]))), - <<":def:">> = iolist_to_binary(join(re:split("defdef","^(?|(abc)|(def))\\1",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?|(abc)|(def))\\1",[trim]))), + 2}]))), + <<":def:">> = iolist_to_binary(join(re:split("defdef","^(?|(abc)|(def))\\1",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?|(abc)|(def))\\1",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?|(abc)|(def))\\1",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?|(abc)|(def))\\1",[]))), - <<"abcdef">> = iolist_to_binary(join(re:split("abcdef","^(?|(abc)|(def))\\1",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?|(abc)|(def))\\1",[]))), + <<"abcdef">> = iolist_to_binary(join(re:split("abcdef","^(?|(abc)|(def))\\1",[trim]))), <<"abcdef">> = iolist_to_binary(join(re:split("abcdef","^(?|(abc)|(def))\\1",[{parts, - 2}]))), - <<"abcdef">> = iolist_to_binary(join(re:split("abcdef","^(?|(abc)|(def))\\1",[]))), - <<"defabc">> = iolist_to_binary(join(re:split("defabc","^(?|(abc)|(def))\\1",[trim]))), + 2}]))), + <<"abcdef">> = iolist_to_binary(join(re:split("abcdef","^(?|(abc)|(def))\\1",[]))), + <<"defabc">> = iolist_to_binary(join(re:split("defabc","^(?|(abc)|(def))\\1",[trim]))), <<"defabc">> = iolist_to_binary(join(re:split("defabc","^(?|(abc)|(def))\\1",[{parts, - 2}]))), - <<"defabc">> = iolist_to_binary(join(re:split("defabc","^(?|(abc)|(def))\\1",[]))), + 2}]))), + <<"defabc">> = iolist_to_binary(join(re:split("defabc","^(?|(abc)|(def))\\1",[]))), ok. run43() -> - <<":abc">> = iolist_to_binary(join(re:split("abcabc","^(?|(abc)|(def))(?1)",[trim]))), + <<":abc">> = iolist_to_binary(join(re:split("abcabc","^(?|(abc)|(def))(?1)",[trim]))), <<":abc:">> = iolist_to_binary(join(re:split("abcabc","^(?|(abc)|(def))(?1)",[{parts, - 2}]))), - <<":abc:">> = iolist_to_binary(join(re:split("abcabc","^(?|(abc)|(def))(?1)",[]))), - <<":def">> = iolist_to_binary(join(re:split("defabc","^(?|(abc)|(def))(?1)",[trim]))), + 2}]))), + <<":abc:">> = iolist_to_binary(join(re:split("abcabc","^(?|(abc)|(def))(?1)",[]))), + <<":def">> = iolist_to_binary(join(re:split("defabc","^(?|(abc)|(def))(?1)",[trim]))), <<":def:">> = iolist_to_binary(join(re:split("defabc","^(?|(abc)|(def))(?1)",[{parts, - 2}]))), - <<":def:">> = iolist_to_binary(join(re:split("defabc","^(?|(abc)|(def))(?1)",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?|(abc)|(def))(?1)",[trim]))), + 2}]))), + <<":def:">> = iolist_to_binary(join(re:split("defabc","^(?|(abc)|(def))(?1)",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?|(abc)|(def))(?1)",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?|(abc)|(def))(?1)",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?|(abc)|(def))(?1)",[]))), - <<"defdef">> = iolist_to_binary(join(re:split("defdef","^(?|(abc)|(def))(?1)",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?|(abc)|(def))(?1)",[]))), + <<"defdef">> = iolist_to_binary(join(re:split("defdef","^(?|(abc)|(def))(?1)",[trim]))), <<"defdef">> = iolist_to_binary(join(re:split("defdef","^(?|(abc)|(def))(?1)",[{parts, - 2}]))), - <<"defdef">> = iolist_to_binary(join(re:split("defdef","^(?|(abc)|(def))(?1)",[]))), - <<"abcdef">> = iolist_to_binary(join(re:split("abcdef","^(?|(abc)|(def))(?1)",[trim]))), + 2}]))), + <<"defdef">> = iolist_to_binary(join(re:split("defdef","^(?|(abc)|(def))(?1)",[]))), + <<"abcdef">> = iolist_to_binary(join(re:split("abcdef","^(?|(abc)|(def))(?1)",[trim]))), <<"abcdef">> = iolist_to_binary(join(re:split("abcdef","^(?|(abc)|(def))(?1)",[{parts, - 2}]))), - <<"abcdef">> = iolist_to_binary(join(re:split("abcdef","^(?|(abc)|(def))(?1)",[]))), - <<"A:C:D">> = iolist_to_binary(join(re:split("ABCD","(?:(?1)|B)(A(*F)|C)",[trim]))), + 2}]))), + <<"abcdef">> = iolist_to_binary(join(re:split("abcdef","^(?|(abc)|(def))(?1)",[]))), + <<"A:C:D">> = iolist_to_binary(join(re:split("ABCD","(?:(?1)|B)(A(*F)|C)",[trim]))), <<"A:C:D">> = iolist_to_binary(join(re:split("ABCD","(?:(?1)|B)(A(*F)|C)",[{parts, - 2}]))), - <<"A:C:D">> = iolist_to_binary(join(re:split("ABCD","(?:(?1)|B)(A(*F)|C)",[]))), - <<":C:D">> = iolist_to_binary(join(re:split("CCD","(?:(?1)|B)(A(*F)|C)",[trim]))), + 2}]))), + <<"A:C:D">> = iolist_to_binary(join(re:split("ABCD","(?:(?1)|B)(A(*F)|C)",[]))), + <<":C:D">> = iolist_to_binary(join(re:split("CCD","(?:(?1)|B)(A(*F)|C)",[trim]))), <<":C:D">> = iolist_to_binary(join(re:split("CCD","(?:(?1)|B)(A(*F)|C)",[{parts, - 2}]))), - <<":C:D">> = iolist_to_binary(join(re:split("CCD","(?:(?1)|B)(A(*F)|C)",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?:(?1)|B)(A(*F)|C)",[trim]))), + 2}]))), + <<":C:D">> = iolist_to_binary(join(re:split("CCD","(?:(?1)|B)(A(*F)|C)",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?:(?1)|B)(A(*F)|C)",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?:(?1)|B)(A(*F)|C)",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?:(?1)|B)(A(*F)|C)",[]))), - <<"CAD">> = iolist_to_binary(join(re:split("CAD","(?:(?1)|B)(A(*F)|C)",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?:(?1)|B)(A(*F)|C)",[]))), + <<"CAD">> = iolist_to_binary(join(re:split("CAD","(?:(?1)|B)(A(*F)|C)",[trim]))), <<"CAD">> = iolist_to_binary(join(re:split("CAD","(?:(?1)|B)(A(*F)|C)",[{parts, - 2}]))), - <<"CAD">> = iolist_to_binary(join(re:split("CAD","(?:(?1)|B)(A(*F)|C)",[]))), - <<":C:D">> = iolist_to_binary(join(re:split("CCD","^(?:(?1)|B)(A(*F)|C)",[trim]))), + 2}]))), + <<"CAD">> = iolist_to_binary(join(re:split("CAD","(?:(?1)|B)(A(*F)|C)",[]))), + <<":C:D">> = iolist_to_binary(join(re:split("CCD","^(?:(?1)|B)(A(*F)|C)",[trim]))), <<":C:D">> = iolist_to_binary(join(re:split("CCD","^(?:(?1)|B)(A(*F)|C)",[{parts, - 2}]))), - <<":C:D">> = iolist_to_binary(join(re:split("CCD","^(?:(?1)|B)(A(*F)|C)",[]))), - <<":C:D">> = iolist_to_binary(join(re:split("BCD","^(?:(?1)|B)(A(*F)|C)",[trim]))), + 2}]))), + <<":C:D">> = iolist_to_binary(join(re:split("CCD","^(?:(?1)|B)(A(*F)|C)",[]))), + <<":C:D">> = iolist_to_binary(join(re:split("BCD","^(?:(?1)|B)(A(*F)|C)",[trim]))), <<":C:D">> = iolist_to_binary(join(re:split("BCD","^(?:(?1)|B)(A(*F)|C)",[{parts, - 2}]))), - <<":C:D">> = iolist_to_binary(join(re:split("BCD","^(?:(?1)|B)(A(*F)|C)",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?:(?1)|B)(A(*F)|C)",[trim]))), + 2}]))), + <<":C:D">> = iolist_to_binary(join(re:split("BCD","^(?:(?1)|B)(A(*F)|C)",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?:(?1)|B)(A(*F)|C)",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?:(?1)|B)(A(*F)|C)",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?:(?1)|B)(A(*F)|C)",[]))), - <<"ABCD">> = iolist_to_binary(join(re:split("ABCD","^(?:(?1)|B)(A(*F)|C)",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?:(?1)|B)(A(*F)|C)",[]))), + <<"ABCD">> = iolist_to_binary(join(re:split("ABCD","^(?:(?1)|B)(A(*F)|C)",[trim]))), <<"ABCD">> = iolist_to_binary(join(re:split("ABCD","^(?:(?1)|B)(A(*F)|C)",[{parts, - 2}]))), - <<"ABCD">> = iolist_to_binary(join(re:split("ABCD","^(?:(?1)|B)(A(*F)|C)",[]))), - <<"CAD">> = iolist_to_binary(join(re:split("CAD","^(?:(?1)|B)(A(*F)|C)",[trim]))), + 2}]))), + <<"ABCD">> = iolist_to_binary(join(re:split("ABCD","^(?:(?1)|B)(A(*F)|C)",[]))), + <<"CAD">> = iolist_to_binary(join(re:split("CAD","^(?:(?1)|B)(A(*F)|C)",[trim]))), <<"CAD">> = iolist_to_binary(join(re:split("CAD","^(?:(?1)|B)(A(*F)|C)",[{parts, - 2}]))), - <<"CAD">> = iolist_to_binary(join(re:split("CAD","^(?:(?1)|B)(A(*F)|C)",[]))), - <<"BAD">> = iolist_to_binary(join(re:split("BAD","^(?:(?1)|B)(A(*F)|C)",[trim]))), + 2}]))), + <<"CAD">> = iolist_to_binary(join(re:split("CAD","^(?:(?1)|B)(A(*F)|C)",[]))), + <<"BAD">> = iolist_to_binary(join(re:split("BAD","^(?:(?1)|B)(A(*F)|C)",[trim]))), <<"BAD">> = iolist_to_binary(join(re:split("BAD","^(?:(?1)|B)(A(*F)|C)",[{parts, - 2}]))), - <<"BAD">> = iolist_to_binary(join(re:split("BAD","^(?:(?1)|B)(A(*F)|C)",[]))), - <<":A:D">> = iolist_to_binary(join(re:split("AAD","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[trim]))), + 2}]))), + <<"BAD">> = iolist_to_binary(join(re:split("BAD","^(?:(?1)|B)(A(*F)|C)",[]))), + <<":A:D">> = iolist_to_binary(join(re:split("AAD","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[trim]))), <<":A:D">> = iolist_to_binary(join(re:split("AAD","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[{parts, - 2}]))), - <<":A:D">> = iolist_to_binary(join(re:split("AAD","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[]))), - <<":C">> = iolist_to_binary(join(re:split("ACD","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[trim]))), + 2}]))), + <<":A:D">> = iolist_to_binary(join(re:split("AAD","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[]))), + <<":C">> = iolist_to_binary(join(re:split("ACD","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[trim]))), <<":C:">> = iolist_to_binary(join(re:split("ACD","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[{parts, - 2}]))), - <<":C:">> = iolist_to_binary(join(re:split("ACD","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[]))), - <<":A:D">> = iolist_to_binary(join(re:split("BAD","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[trim]))), + 2}]))), + <<":C:">> = iolist_to_binary(join(re:split("ACD","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[]))), + <<":A:D">> = iolist_to_binary(join(re:split("BAD","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[trim]))), <<":A:D">> = iolist_to_binary(join(re:split("BAD","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[{parts, - 2}]))), - <<":A:D">> = iolist_to_binary(join(re:split("BAD","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[]))), - <<":C">> = iolist_to_binary(join(re:split("BCD","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[trim]))), + 2}]))), + <<":A:D">> = iolist_to_binary(join(re:split("BAD","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[]))), + <<":C">> = iolist_to_binary(join(re:split("BCD","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[trim]))), <<":C:">> = iolist_to_binary(join(re:split("BCD","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[{parts, - 2}]))), - <<":C:">> = iolist_to_binary(join(re:split("BCD","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[]))), - <<":A:X">> = iolist_to_binary(join(re:split("BAX","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[trim]))), + 2}]))), + <<":C:">> = iolist_to_binary(join(re:split("BCD","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[]))), + <<":A:X">> = iolist_to_binary(join(re:split("BAX","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[trim]))), <<":A:X">> = iolist_to_binary(join(re:split("BAX","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[{parts, - 2}]))), - <<":A:X">> = iolist_to_binary(join(re:split("BAX","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[trim]))), + 2}]))), + <<":A:X">> = iolist_to_binary(join(re:split("BAX","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[]))), - <<"ACX">> = iolist_to_binary(join(re:split("ACX","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[]))), + <<"ACX">> = iolist_to_binary(join(re:split("ACX","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[trim]))), <<"ACX">> = iolist_to_binary(join(re:split("ACX","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[{parts, - 2}]))), - <<"ACX">> = iolist_to_binary(join(re:split("ACX","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[]))), - <<"ABC">> = iolist_to_binary(join(re:split("ABC","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[trim]))), + 2}]))), + <<"ACX">> = iolist_to_binary(join(re:split("ACX","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[]))), + <<"ABC">> = iolist_to_binary(join(re:split("ABC","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[trim]))), <<"ABC">> = iolist_to_binary(join(re:split("ABC","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[{parts, - 2}]))), - <<"ABC">> = iolist_to_binary(join(re:split("ABC","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[]))), - <<"">> = iolist_to_binary(join(re:split("BAC","(?(DEFINE)(A))B(?1)C",[trim]))), + 2}]))), + <<"ABC">> = iolist_to_binary(join(re:split("ABC","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[]))), + <<"">> = iolist_to_binary(join(re:split("BAC","(?(DEFINE)(A))B(?1)C",[trim]))), <<"::">> = iolist_to_binary(join(re:split("BAC","(?(DEFINE)(A))B(?1)C",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("BAC","(?(DEFINE)(A))B(?1)C",[]))), - <<"">> = iolist_to_binary(join(re:split("BAAC","(?(DEFINE)((A)\\2))B(?1)C",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("BAC","(?(DEFINE)(A))B(?1)C",[]))), + <<"">> = iolist_to_binary(join(re:split("BAAC","(?(DEFINE)((A)\\2))B(?1)C",[trim]))), <<":::">> = iolist_to_binary(join(re:split("BAAC","(?(DEFINE)((A)\\2))B(?1)C",[{parts, - 2}]))), - <<":::">> = iolist_to_binary(join(re:split("BAAC","(?(DEFINE)((A)\\2))B(?1)C",[]))), + 2}]))), + <<":::">> = iolist_to_binary(join(re:split("BAAC","(?(DEFINE)((A)\\2))B(?1)C",[]))), <<":(ab(cd)ef):ef">> = iolist_to_binary(join(re:split("(ab(cd)ef)","(?<pn> \\( ( [^()]++ | (?&pn) )* \\) )",[extended, - trim]))), + trim]))), <<":(ab(cd)ef):ef:">> = iolist_to_binary(join(re:split("(ab(cd)ef)","(?<pn> \\( ( [^()]++ | (?&pn) )* \\) )",[extended, {parts, - 2}]))), - <<":(ab(cd)ef):ef:">> = iolist_to_binary(join(re:split("(ab(cd)ef)","(?<pn> \\( ( [^()]++ | (?&pn) )* \\) )",[extended]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?=a(*SKIP)b|ac)",[trim]))), + 2}]))), + <<":(ab(cd)ef):ef:">> = iolist_to_binary(join(re:split("(ab(cd)ef)","(?<pn> \\( ( [^()]++ | (?&pn) )* \\) )",[extended]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?=a(*SKIP)b|ac)",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?=a(*SKIP)b|ac)",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?=a(*SKIP)b|ac)",[]))), - <<"ac">> = iolist_to_binary(join(re:split("ac","^(?=a(*SKIP)b|ac)",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?=a(*SKIP)b|ac)",[]))), + <<"ac">> = iolist_to_binary(join(re:split("ac","^(?=a(*SKIP)b|ac)",[trim]))), <<"ac">> = iolist_to_binary(join(re:split("ac","^(?=a(*SKIP)b|ac)",[{parts, - 2}]))), - <<"ac">> = iolist_to_binary(join(re:split("ac","^(?=a(*SKIP)b|ac)",[]))), - <<"ab">> = iolist_to_binary(join(re:split("ab","^(?=a(*PRUNE)b)",[trim]))), + 2}]))), + <<"ac">> = iolist_to_binary(join(re:split("ac","^(?=a(*SKIP)b|ac)",[]))), + <<"ab">> = iolist_to_binary(join(re:split("ab","^(?=a(*PRUNE)b)",[trim]))), <<"ab">> = iolist_to_binary(join(re:split("ab","^(?=a(*PRUNE)b)",[{parts, - 2}]))), - <<"ab">> = iolist_to_binary(join(re:split("ab","^(?=a(*PRUNE)b)",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?=a(*PRUNE)b)",[trim]))), + 2}]))), + <<"ab">> = iolist_to_binary(join(re:split("ab","^(?=a(*PRUNE)b)",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?=a(*PRUNE)b)",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?=a(*PRUNE)b)",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?=a(*PRUNE)b)",[]))), - <<"ac">> = iolist_to_binary(join(re:split("ac","^(?=a(*PRUNE)b)",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?=a(*PRUNE)b)",[]))), + <<"ac">> = iolist_to_binary(join(re:split("ac","^(?=a(*PRUNE)b)",[trim]))), <<"ac">> = iolist_to_binary(join(re:split("ac","^(?=a(*PRUNE)b)",[{parts, - 2}]))), - <<"ac">> = iolist_to_binary(join(re:split("ac","^(?=a(*PRUNE)b)",[]))), - <<"ac">> = iolist_to_binary(join(re:split("ac","^(?=a(*ACCEPT)b)",[trim]))), + 2}]))), + <<"ac">> = iolist_to_binary(join(re:split("ac","^(?=a(*PRUNE)b)",[]))), + <<"ac">> = iolist_to_binary(join(re:split("ac","^(?=a(*ACCEPT)b)",[trim]))), <<"ac">> = iolist_to_binary(join(re:split("ac","^(?=a(*ACCEPT)b)",[{parts, - 2}]))), - <<"ac">> = iolist_to_binary(join(re:split("ac","^(?=a(*ACCEPT)b)",[]))), - <<"a">> = iolist_to_binary(join(re:split("ab","(?>a\\Kb)",[trim]))), + 2}]))), + <<"ac">> = iolist_to_binary(join(re:split("ac","^(?=a(*ACCEPT)b)",[]))), + <<"a">> = iolist_to_binary(join(re:split("ab","(?>a\\Kb)",[trim]))), <<"a:">> = iolist_to_binary(join(re:split("ab","(?>a\\Kb)",[{parts, - 2}]))), - <<"a:">> = iolist_to_binary(join(re:split("ab","(?>a\\Kb)",[]))), - <<"a:ab">> = iolist_to_binary(join(re:split("ab","((?>a\\Kb))",[trim]))), + 2}]))), + <<"a:">> = iolist_to_binary(join(re:split("ab","(?>a\\Kb)",[]))), + <<"a:ab">> = iolist_to_binary(join(re:split("ab","((?>a\\Kb))",[trim]))), <<"a:ab:">> = iolist_to_binary(join(re:split("ab","((?>a\\Kb))",[{parts, - 2}]))), - <<"a:ab:">> = iolist_to_binary(join(re:split("ab","((?>a\\Kb))",[]))), - <<"a:ab">> = iolist_to_binary(join(re:split("ab","(a\\Kb)",[trim]))), + 2}]))), + <<"a:ab:">> = iolist_to_binary(join(re:split("ab","((?>a\\Kb))",[]))), + <<"a:ab">> = iolist_to_binary(join(re:split("ab","(a\\Kb)",[trim]))), <<"a:ab:">> = iolist_to_binary(join(re:split("ab","(a\\Kb)",[{parts, - 2}]))), - <<"a:ab:">> = iolist_to_binary(join(re:split("ab","(a\\Kb)",[]))), - <<"">> = iolist_to_binary(join(re:split("ac","^a\\Kcz|ac",[trim]))), + 2}]))), + <<"a:ab:">> = iolist_to_binary(join(re:split("ab","(a\\Kb)",[]))), + <<"">> = iolist_to_binary(join(re:split("ac","^a\\Kcz|ac",[trim]))), <<":">> = iolist_to_binary(join(re:split("ac","^a\\Kcz|ac",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ac","^a\\Kcz|ac",[]))), - <<"">> = iolist_to_binary(join(re:split("ab","(?>a\\Kbz|ab)",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ac","^a\\Kcz|ac",[]))), + <<"">> = iolist_to_binary(join(re:split("ab","(?>a\\Kbz|ab)",[trim]))), <<":">> = iolist_to_binary(join(re:split("ab","(?>a\\Kbz|ab)",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ab","(?>a\\Kbz|ab)",[]))), - <<"a">> = iolist_to_binary(join(re:split("ab","^(?&t)(?(DEFINE)(?<t>a\\Kb))$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ab","(?>a\\Kbz|ab)",[]))), + <<"a">> = iolist_to_binary(join(re:split("ab","^(?&t)(?(DEFINE)(?<t>a\\Kb))$",[trim]))), <<"a::">> = iolist_to_binary(join(re:split("ab","^(?&t)(?(DEFINE)(?<t>a\\Kb))$",[{parts, - 2}]))), - <<"a::">> = iolist_to_binary(join(re:split("ab","^(?&t)(?(DEFINE)(?<t>a\\Kb))$",[]))), - <<":c">> = iolist_to_binary(join(re:split("a(b)c","^([^()]|\\((?1)*\\))*$",[trim]))), + 2}]))), + <<"a::">> = iolist_to_binary(join(re:split("ab","^(?&t)(?(DEFINE)(?<t>a\\Kb))$",[]))), + <<":c">> = iolist_to_binary(join(re:split("a(b)c","^([^()]|\\((?1)*\\))*$",[trim]))), <<":c:">> = iolist_to_binary(join(re:split("a(b)c","^([^()]|\\((?1)*\\))*$",[{parts, - 2}]))), - <<":c:">> = iolist_to_binary(join(re:split("a(b)c","^([^()]|\\((?1)*\\))*$",[]))), - <<":e">> = iolist_to_binary(join(re:split("a(b(c)d)e","^([^()]|\\((?1)*\\))*$",[trim]))), + 2}]))), + <<":c:">> = iolist_to_binary(join(re:split("a(b)c","^([^()]|\\((?1)*\\))*$",[]))), + <<":e">> = iolist_to_binary(join(re:split("a(b(c)d)e","^([^()]|\\((?1)*\\))*$",[trim]))), <<":e:">> = iolist_to_binary(join(re:split("a(b(c)d)e","^([^()]|\\((?1)*\\))*$",[{parts, - 2}]))), - <<":e:">> = iolist_to_binary(join(re:split("a(b(c)d)e","^([^()]|\\((?1)*\\))*$",[]))), - <<":0">> = iolist_to_binary(join(re:split("0","(?P<L1>(?P<L2>0)(?P>L1)|(?P>L2))",[trim]))), + 2}]))), + <<":e:">> = iolist_to_binary(join(re:split("a(b(c)d)e","^([^()]|\\((?1)*\\))*$",[]))), + <<":0">> = iolist_to_binary(join(re:split("0","(?P<L1>(?P<L2>0)(?P>L1)|(?P>L2))",[trim]))), <<":0::">> = iolist_to_binary(join(re:split("0","(?P<L1>(?P<L2>0)(?P>L1)|(?P>L2))",[{parts, - 2}]))), - <<":0::">> = iolist_to_binary(join(re:split("0","(?P<L1>(?P<L2>0)(?P>L1)|(?P>L2))",[]))), - <<":00:0">> = iolist_to_binary(join(re:split("00","(?P<L1>(?P<L2>0)(?P>L1)|(?P>L2))",[trim]))), + 2}]))), + <<":0::">> = iolist_to_binary(join(re:split("0","(?P<L1>(?P<L2>0)(?P>L1)|(?P>L2))",[]))), + <<":00:0">> = iolist_to_binary(join(re:split("00","(?P<L1>(?P<L2>0)(?P>L1)|(?P>L2))",[trim]))), <<":00:0:">> = iolist_to_binary(join(re:split("00","(?P<L1>(?P<L2>0)(?P>L1)|(?P>L2))",[{parts, - 2}]))), - <<":00:0:">> = iolist_to_binary(join(re:split("00","(?P<L1>(?P<L2>0)(?P>L1)|(?P>L2))",[]))), - <<":0000:0">> = iolist_to_binary(join(re:split("0000","(?P<L1>(?P<L2>0)(?P>L1)|(?P>L2))",[trim]))), + 2}]))), + <<":00:0:">> = iolist_to_binary(join(re:split("00","(?P<L1>(?P<L2>0)(?P>L1)|(?P>L2))",[]))), + <<":0000:0">> = iolist_to_binary(join(re:split("0000","(?P<L1>(?P<L2>0)(?P>L1)|(?P>L2))",[trim]))), <<":0000:0:">> = iolist_to_binary(join(re:split("0000","(?P<L1>(?P<L2>0)(?P>L1)|(?P>L2))",[{parts, - 2}]))), - <<":0000:0:">> = iolist_to_binary(join(re:split("0000","(?P<L1>(?P<L2>0)(?P>L1)|(?P>L2))",[]))), - <<":0:0">> = iolist_to_binary(join(re:split("0","(?P<L1>(?P<L2>0)|(?P>L2)(?P>L1))",[trim]))), + 2}]))), + <<":0000:0:">> = iolist_to_binary(join(re:split("0000","(?P<L1>(?P<L2>0)(?P>L1)|(?P>L2))",[]))), + <<":0:0">> = iolist_to_binary(join(re:split("0","(?P<L1>(?P<L2>0)|(?P>L2)(?P>L1))",[trim]))), <<":0:0:">> = iolist_to_binary(join(re:split("0","(?P<L1>(?P<L2>0)|(?P>L2)(?P>L1))",[{parts, - 2}]))), - <<":0:0:">> = iolist_to_binary(join(re:split("0","(?P<L1>(?P<L2>0)|(?P>L2)(?P>L1))",[]))), - <<":0:0::0:0">> = iolist_to_binary(join(re:split("00","(?P<L1>(?P<L2>0)|(?P>L2)(?P>L1))",[trim]))), + 2}]))), + <<":0:0:">> = iolist_to_binary(join(re:split("0","(?P<L1>(?P<L2>0)|(?P>L2)(?P>L1))",[]))), + <<":0:0::0:0">> = iolist_to_binary(join(re:split("00","(?P<L1>(?P<L2>0)|(?P>L2)(?P>L1))",[trim]))), <<":0:0:0">> = iolist_to_binary(join(re:split("00","(?P<L1>(?P<L2>0)|(?P>L2)(?P>L1))",[{parts, - 2}]))), - <<":0:0::0:0:">> = iolist_to_binary(join(re:split("00","(?P<L1>(?P<L2>0)|(?P>L2)(?P>L1))",[]))), - <<":0:0::0:0::0:0::0:0">> = iolist_to_binary(join(re:split("0000","(?P<L1>(?P<L2>0)|(?P>L2)(?P>L1))",[trim]))), + 2}]))), + <<":0:0::0:0:">> = iolist_to_binary(join(re:split("00","(?P<L1>(?P<L2>0)|(?P>L2)(?P>L1))",[]))), + <<":0:0::0:0::0:0::0:0">> = iolist_to_binary(join(re:split("0000","(?P<L1>(?P<L2>0)|(?P>L2)(?P>L1))",[trim]))), <<":0:0:000">> = iolist_to_binary(join(re:split("0000","(?P<L1>(?P<L2>0)|(?P>L2)(?P>L1))",[{parts, - 2}]))), - <<":0:0::0:0::0:0::0:0:">> = iolist_to_binary(join(re:split("0000","(?P<L1>(?P<L2>0)|(?P>L2)(?P>L1))",[]))), + 2}]))), + <<":0:0::0:0::0:0::0:0:">> = iolist_to_binary(join(re:split("0000","(?P<L1>(?P<L2>0)|(?P>L2)(?P>L1))",[]))), ok. run44() -> - <<"ACABX">> = iolist_to_binary(join(re:split("ACABX","A(*COMMIT)(B|D)",[trim]))), + <<"ACABX">> = iolist_to_binary(join(re:split("ACABX","A(*COMMIT)(B|D)",[trim]))), <<"ACABX">> = iolist_to_binary(join(re:split("ACABX","A(*COMMIT)(B|D)",[{parts, - 2}]))), - <<"ACABX">> = iolist_to_binary(join(re:split("ACABX","A(*COMMIT)(B|D)",[]))), - <<":A:B:C:DEFG">> = iolist_to_binary(join(re:split("ABCDEFG","(*COMMIT)(A|P)(B|P)(C|P)",[trim]))), + 2}]))), + <<"ACABX">> = iolist_to_binary(join(re:split("ACABX","A(*COMMIT)(B|D)",[]))), + <<":A:B:C:DEFG">> = iolist_to_binary(join(re:split("ABCDEFG","(*COMMIT)(A|P)(B|P)(C|P)",[trim]))), <<":A:B:C:DEFG">> = iolist_to_binary(join(re:split("ABCDEFG","(*COMMIT)(A|P)(B|P)(C|P)",[{parts, - 2}]))), - <<":A:B:C:DEFG">> = iolist_to_binary(join(re:split("ABCDEFG","(*COMMIT)(A|P)(B|P)(C|P)",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(*COMMIT)(A|P)(B|P)(C|P)",[trim]))), + 2}]))), + <<":A:B:C:DEFG">> = iolist_to_binary(join(re:split("ABCDEFG","(*COMMIT)(A|P)(B|P)(C|P)",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(*COMMIT)(A|P)(B|P)(C|P)",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(*COMMIT)(A|P)(B|P)(C|P)",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(*COMMIT)(A|P)(B|P)(C|P)",[]))), - <<"DEFGABC">> = iolist_to_binary(join(re:split("DEFGABC","(*COMMIT)(A|P)(B|P)(C|P)",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(*COMMIT)(A|P)(B|P)(C|P)",[]))), + <<"DEFGABC">> = iolist_to_binary(join(re:split("DEFGABC","(*COMMIT)(A|P)(B|P)(C|P)",[trim]))), <<"DEFGABC">> = iolist_to_binary(join(re:split("DEFGABC","(*COMMIT)(A|P)(B|P)(C|P)",[{parts, - 2}]))), - <<"DEFGABC">> = iolist_to_binary(join(re:split("DEFGABC","(*COMMIT)(A|P)(B|P)(C|P)",[]))), - <<":a">> = iolist_to_binary(join(re:split("abbb","(\\w+)(?>b(*COMMIT))\\w{2}",[trim]))), + 2}]))), + <<"DEFGABC">> = iolist_to_binary(join(re:split("DEFGABC","(*COMMIT)(A|P)(B|P)(C|P)",[]))), + <<":a">> = iolist_to_binary(join(re:split("abbb","(\\w+)(?>b(*COMMIT))\\w{2}",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("abbb","(\\w+)(?>b(*COMMIT))\\w{2}",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("abbb","(\\w+)(?>b(*COMMIT))\\w{2}",[]))), - <<"abbb">> = iolist_to_binary(join(re:split("abbb","(\\w+)b(*COMMIT)\\w{2}",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("abbb","(\\w+)(?>b(*COMMIT))\\w{2}",[]))), + <<"abbb">> = iolist_to_binary(join(re:split("abbb","(\\w+)b(*COMMIT)\\w{2}",[trim]))), <<"abbb">> = iolist_to_binary(join(re:split("abbb","(\\w+)b(*COMMIT)\\w{2}",[{parts, - 2}]))), - <<"abbb">> = iolist_to_binary(join(re:split("abbb","(\\w+)b(*COMMIT)\\w{2}",[]))), - <<"b::c">> = iolist_to_binary(join(re:split("bac","(?&t)(?#()(?(DEFINE)(?<t>a))",[trim]))), + 2}]))), + <<"abbb">> = iolist_to_binary(join(re:split("abbb","(\\w+)b(*COMMIT)\\w{2}",[]))), + <<"b::c">> = iolist_to_binary(join(re:split("bac","(?&t)(?#()(?(DEFINE)(?<t>a))",[trim]))), <<"b::c">> = iolist_to_binary(join(re:split("bac","(?&t)(?#()(?(DEFINE)(?<t>a))",[{parts, - 2}]))), - <<"b::c">> = iolist_to_binary(join(re:split("bac","(?&t)(?#()(?(DEFINE)(?<t>a))",[]))), - <<"yes">> = iolist_to_binary(join(re:split("yes","(?>(*COMMIT)(?>yes|no)(*THEN)(*F))?",[trim]))), + 2}]))), + <<"b::c">> = iolist_to_binary(join(re:split("bac","(?&t)(?#()(?(DEFINE)(?<t>a))",[]))), + <<"yes">> = iolist_to_binary(join(re:split("yes","(?>(*COMMIT)(?>yes|no)(*THEN)(*F))?",[trim]))), <<"yes">> = iolist_to_binary(join(re:split("yes","(?>(*COMMIT)(?>yes|no)(*THEN)(*F))?",[{parts, - 2}]))), - <<"yes">> = iolist_to_binary(join(re:split("yes","(?>(*COMMIT)(?>yes|no)(*THEN)(*F))?",[]))), - <<"yes">> = iolist_to_binary(join(re:split("yes","(?>(*COMMIT)(yes|no)(*THEN)(*F))?",[trim]))), + 2}]))), + <<"yes">> = iolist_to_binary(join(re:split("yes","(?>(*COMMIT)(?>yes|no)(*THEN)(*F))?",[]))), + <<"yes">> = iolist_to_binary(join(re:split("yes","(?>(*COMMIT)(yes|no)(*THEN)(*F))?",[trim]))), <<"yes">> = iolist_to_binary(join(re:split("yes","(?>(*COMMIT)(yes|no)(*THEN)(*F))?",[{parts, - 2}]))), - <<"yes">> = iolist_to_binary(join(re:split("yes","(?>(*COMMIT)(yes|no)(*THEN)(*F))?",[]))), - <<"">> = iolist_to_binary(join(re:split("bc","b?(*SKIP)c",[trim]))), + 2}]))), + <<"yes">> = iolist_to_binary(join(re:split("yes","(?>(*COMMIT)(yes|no)(*THEN)(*F))?",[]))), + <<"">> = iolist_to_binary(join(re:split("bc","b?(*SKIP)c",[trim]))), <<":">> = iolist_to_binary(join(re:split("bc","b?(*SKIP)c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("bc","b?(*SKIP)c",[]))), - <<"a">> = iolist_to_binary(join(re:split("abc","b?(*SKIP)c",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("bc","b?(*SKIP)c",[]))), + <<"a">> = iolist_to_binary(join(re:split("abc","b?(*SKIP)c",[trim]))), <<"a:">> = iolist_to_binary(join(re:split("abc","b?(*SKIP)c",[{parts, - 2}]))), - <<"a:">> = iolist_to_binary(join(re:split("abc","b?(*SKIP)c",[]))), + 2}]))), + <<"a:">> = iolist_to_binary(join(re:split("abc","b?(*SKIP)c",[]))), ok. run45() -> - <<"a">> = iolist_to_binary(join(re:split("a","(*SKIP)bc",[trim]))), + <<"a">> = iolist_to_binary(join(re:split("a","(*SKIP)bc",[trim]))), <<"a">> = iolist_to_binary(join(re:split("a","(*SKIP)bc",[{parts, - 2}]))), - <<"a">> = iolist_to_binary(join(re:split("a","(*SKIP)bc",[]))), - <<"a">> = iolist_to_binary(join(re:split("a","(*SKIP)b",[trim]))), + 2}]))), + <<"a">> = iolist_to_binary(join(re:split("a","(*SKIP)bc",[]))), + <<"a">> = iolist_to_binary(join(re:split("a","(*SKIP)b",[trim]))), <<"a">> = iolist_to_binary(join(re:split("a","(*SKIP)b",[{parts, - 2}]))), - <<"a">> = iolist_to_binary(join(re:split("a","(*SKIP)b",[]))), - <<"x::x::x">> = iolist_to_binary(join(re:split("xxx","(?P<abn>(?P=abn)xxx|)+",[trim]))), + 2}]))), + <<"a">> = iolist_to_binary(join(re:split("a","(*SKIP)b",[]))), + <<"x::x::x">> = iolist_to_binary(join(re:split("xxx","(?P<abn>(?P=abn)xxx|)+",[trim]))), <<"x::xx">> = iolist_to_binary(join(re:split("xxx","(?P<abn>(?P=abn)xxx|)+",[{parts, - 2}]))), - <<"x::x::x::">> = iolist_to_binary(join(re:split("xxx","(?P<abn>(?P=abn)xxx|)+",[]))), - <<":a">> = iolist_to_binary(join(re:split("aa","(?i:([^b]))(?1)",[trim]))), + 2}]))), + <<"x::x::x::">> = iolist_to_binary(join(re:split("xxx","(?P<abn>(?P=abn)xxx|)+",[]))), + <<":a">> = iolist_to_binary(join(re:split("aa","(?i:([^b]))(?1)",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aa","(?i:([^b]))(?1)",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aa","(?i:([^b]))(?1)",[]))), - <<":a">> = iolist_to_binary(join(re:split("aA","(?i:([^b]))(?1)",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aa","(?i:([^b]))(?1)",[]))), + <<":a">> = iolist_to_binary(join(re:split("aA","(?i:([^b]))(?1)",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aA","(?i:([^b]))(?1)",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aA","(?i:([^b]))(?1)",[]))), - <<":*:: ::a::l::r">> = iolist_to_binary(join(re:split("** Failers","(?i:([^b]))(?1)",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aA","(?i:([^b]))(?1)",[]))), + <<":*:: ::a::l::r">> = iolist_to_binary(join(re:split("** Failers","(?i:([^b]))(?1)",[trim]))), <<":*: Failers">> = iolist_to_binary(join(re:split("** Failers","(?i:([^b]))(?1)",[{parts, - 2}]))), - <<":*:: ::a::l::r:">> = iolist_to_binary(join(re:split("** Failers","(?i:([^b]))(?1)",[]))), - <<"ab">> = iolist_to_binary(join(re:split("ab","(?i:([^b]))(?1)",[trim]))), + 2}]))), + <<":*:: ::a::l::r:">> = iolist_to_binary(join(re:split("** Failers","(?i:([^b]))(?1)",[]))), + <<"ab">> = iolist_to_binary(join(re:split("ab","(?i:([^b]))(?1)",[trim]))), <<"ab">> = iolist_to_binary(join(re:split("ab","(?i:([^b]))(?1)",[{parts, - 2}]))), - <<"ab">> = iolist_to_binary(join(re:split("ab","(?i:([^b]))(?1)",[]))), - <<"aB">> = iolist_to_binary(join(re:split("aB","(?i:([^b]))(?1)",[trim]))), + 2}]))), + <<"ab">> = iolist_to_binary(join(re:split("ab","(?i:([^b]))(?1)",[]))), + <<"aB">> = iolist_to_binary(join(re:split("aB","(?i:([^b]))(?1)",[trim]))), <<"aB">> = iolist_to_binary(join(re:split("aB","(?i:([^b]))(?1)",[{parts, - 2}]))), - <<"aB">> = iolist_to_binary(join(re:split("aB","(?i:([^b]))(?1)",[]))), - <<"Ba">> = iolist_to_binary(join(re:split("Ba","(?i:([^b]))(?1)",[trim]))), + 2}]))), + <<"aB">> = iolist_to_binary(join(re:split("aB","(?i:([^b]))(?1)",[]))), + <<"Ba">> = iolist_to_binary(join(re:split("Ba","(?i:([^b]))(?1)",[trim]))), <<"Ba">> = iolist_to_binary(join(re:split("Ba","(?i:([^b]))(?1)",[{parts, - 2}]))), - <<"Ba">> = iolist_to_binary(join(re:split("Ba","(?i:([^b]))(?1)",[]))), - <<"ba">> = iolist_to_binary(join(re:split("ba","(?i:([^b]))(?1)",[trim]))), + 2}]))), + <<"Ba">> = iolist_to_binary(join(re:split("Ba","(?i:([^b]))(?1)",[]))), + <<"ba">> = iolist_to_binary(join(re:split("ba","(?i:([^b]))(?1)",[trim]))), <<"ba">> = iolist_to_binary(join(re:split("ba","(?i:([^b]))(?1)",[{parts, - 2}]))), - <<"ba">> = iolist_to_binary(join(re:split("ba","(?i:([^b]))(?1)",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaaaaX","^(?&t)*+(?(DEFINE)(?<t>a))\\w$",[trim]))), + 2}]))), + <<"ba">> = iolist_to_binary(join(re:split("ba","(?i:([^b]))(?1)",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaaaaX","^(?&t)*+(?(DEFINE)(?<t>a))\\w$",[trim]))), <<"::">> = iolist_to_binary(join(re:split("aaaaaaX","^(?&t)*+(?(DEFINE)(?<t>a))\\w$",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("aaaaaaX","^(?&t)*+(?(DEFINE)(?<t>a))\\w$",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?&t)*+(?(DEFINE)(?<t>a))\\w$",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("aaaaaaX","^(?&t)*+(?(DEFINE)(?<t>a))\\w$",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?&t)*+(?(DEFINE)(?<t>a))\\w$",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?&t)*+(?(DEFINE)(?<t>a))\\w$",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?&t)*+(?(DEFINE)(?<t>a))\\w$",[]))), - <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^(?&t)*+(?(DEFINE)(?<t>a))\\w$",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?&t)*+(?(DEFINE)(?<t>a))\\w$",[]))), + <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^(?&t)*+(?(DEFINE)(?<t>a))\\w$",[trim]))), <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^(?&t)*+(?(DEFINE)(?<t>a))\\w$",[{parts, - 2}]))), - <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^(?&t)*+(?(DEFINE)(?<t>a))\\w$",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaaaaX","^(?&t)*(?(DEFINE)(?<t>a))\\w$",[trim]))), + 2}]))), + <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^(?&t)*+(?(DEFINE)(?<t>a))\\w$",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaaaaX","^(?&t)*(?(DEFINE)(?<t>a))\\w$",[trim]))), <<"::">> = iolist_to_binary(join(re:split("aaaaaaX","^(?&t)*(?(DEFINE)(?<t>a))\\w$",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("aaaaaaX","^(?&t)*(?(DEFINE)(?<t>a))\\w$",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaaaa","^(?&t)*(?(DEFINE)(?<t>a))\\w$",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("aaaaaaX","^(?&t)*(?(DEFINE)(?<t>a))\\w$",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaaaa","^(?&t)*(?(DEFINE)(?<t>a))\\w$",[trim]))), <<"::">> = iolist_to_binary(join(re:split("aaaaaa","^(?&t)*(?(DEFINE)(?<t>a))\\w$",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("aaaaaa","^(?&t)*(?(DEFINE)(?<t>a))\\w$",[]))), - <<":a:X">> = iolist_to_binary(join(re:split("aaaaX","^(a)*+(\\w)",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("aaaaaa","^(?&t)*(?(DEFINE)(?<t>a))\\w$",[]))), + <<":a:X">> = iolist_to_binary(join(re:split("aaaaX","^(a)*+(\\w)",[trim]))), <<":a:X:">> = iolist_to_binary(join(re:split("aaaaX","^(a)*+(\\w)",[{parts, - 2}]))), - <<":a:X:">> = iolist_to_binary(join(re:split("aaaaX","^(a)*+(\\w)",[]))), - <<"::Y:Z">> = iolist_to_binary(join(re:split("YZ","^(a)*+(\\w)",[trim]))), + 2}]))), + <<":a:X:">> = iolist_to_binary(join(re:split("aaaaX","^(a)*+(\\w)",[]))), + <<"::Y:Z">> = iolist_to_binary(join(re:split("YZ","^(a)*+(\\w)",[trim]))), <<"::Y:Z">> = iolist_to_binary(join(re:split("YZ","^(a)*+(\\w)",[{parts, - 2}]))), - <<"::Y:Z">> = iolist_to_binary(join(re:split("YZ","^(a)*+(\\w)",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a)*+(\\w)",[trim]))), + 2}]))), + <<"::Y:Z">> = iolist_to_binary(join(re:split("YZ","^(a)*+(\\w)",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a)*+(\\w)",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a)*+(\\w)",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a)*+(\\w)",[]))), - <<"aaaa">> = iolist_to_binary(join(re:split("aaaa","^(a)*+(\\w)",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a)*+(\\w)",[]))), + <<"aaaa">> = iolist_to_binary(join(re:split("aaaa","^(a)*+(\\w)",[trim]))), <<"aaaa">> = iolist_to_binary(join(re:split("aaaa","^(a)*+(\\w)",[{parts, - 2}]))), - <<"aaaa">> = iolist_to_binary(join(re:split("aaaa","^(a)*+(\\w)",[]))), - <<":X">> = iolist_to_binary(join(re:split("aaaaX","^(?:a)*+(\\w)",[trim]))), + 2}]))), + <<"aaaa">> = iolist_to_binary(join(re:split("aaaa","^(a)*+(\\w)",[]))), + <<":X">> = iolist_to_binary(join(re:split("aaaaX","^(?:a)*+(\\w)",[trim]))), <<":X:">> = iolist_to_binary(join(re:split("aaaaX","^(?:a)*+(\\w)",[{parts, - 2}]))), - <<":X:">> = iolist_to_binary(join(re:split("aaaaX","^(?:a)*+(\\w)",[]))), - <<":Y:Z">> = iolist_to_binary(join(re:split("YZ","^(?:a)*+(\\w)",[trim]))), + 2}]))), + <<":X:">> = iolist_to_binary(join(re:split("aaaaX","^(?:a)*+(\\w)",[]))), + <<":Y:Z">> = iolist_to_binary(join(re:split("YZ","^(?:a)*+(\\w)",[trim]))), <<":Y:Z">> = iolist_to_binary(join(re:split("YZ","^(?:a)*+(\\w)",[{parts, - 2}]))), - <<":Y:Z">> = iolist_to_binary(join(re:split("YZ","^(?:a)*+(\\w)",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?:a)*+(\\w)",[trim]))), + 2}]))), + <<":Y:Z">> = iolist_to_binary(join(re:split("YZ","^(?:a)*+(\\w)",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?:a)*+(\\w)",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?:a)*+(\\w)",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?:a)*+(\\w)",[]))), - <<"aaaa">> = iolist_to_binary(join(re:split("aaaa","^(?:a)*+(\\w)",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?:a)*+(\\w)",[]))), + <<"aaaa">> = iolist_to_binary(join(re:split("aaaa","^(?:a)*+(\\w)",[trim]))), <<"aaaa">> = iolist_to_binary(join(re:split("aaaa","^(?:a)*+(\\w)",[{parts, - 2}]))), - <<"aaaa">> = iolist_to_binary(join(re:split("aaaa","^(?:a)*+(\\w)",[]))), - <<":a:X">> = iolist_to_binary(join(re:split("aaaaX","^(a)++(\\w)",[trim]))), + 2}]))), + <<"aaaa">> = iolist_to_binary(join(re:split("aaaa","^(?:a)*+(\\w)",[]))), + <<":a:X">> = iolist_to_binary(join(re:split("aaaaX","^(a)++(\\w)",[trim]))), <<":a:X:">> = iolist_to_binary(join(re:split("aaaaX","^(a)++(\\w)",[{parts, - 2}]))), - <<":a:X:">> = iolist_to_binary(join(re:split("aaaaX","^(a)++(\\w)",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a)++(\\w)",[trim]))), + 2}]))), + <<":a:X:">> = iolist_to_binary(join(re:split("aaaaX","^(a)++(\\w)",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a)++(\\w)",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a)++(\\w)",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a)++(\\w)",[]))), - <<"aaaa">> = iolist_to_binary(join(re:split("aaaa","^(a)++(\\w)",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a)++(\\w)",[]))), + <<"aaaa">> = iolist_to_binary(join(re:split("aaaa","^(a)++(\\w)",[trim]))), <<"aaaa">> = iolist_to_binary(join(re:split("aaaa","^(a)++(\\w)",[{parts, - 2}]))), - <<"aaaa">> = iolist_to_binary(join(re:split("aaaa","^(a)++(\\w)",[]))), - <<"YZ">> = iolist_to_binary(join(re:split("YZ","^(a)++(\\w)",[trim]))), + 2}]))), + <<"aaaa">> = iolist_to_binary(join(re:split("aaaa","^(a)++(\\w)",[]))), + <<"YZ">> = iolist_to_binary(join(re:split("YZ","^(a)++(\\w)",[trim]))), <<"YZ">> = iolist_to_binary(join(re:split("YZ","^(a)++(\\w)",[{parts, - 2}]))), - <<"YZ">> = iolist_to_binary(join(re:split("YZ","^(a)++(\\w)",[]))), - <<":X">> = iolist_to_binary(join(re:split("aaaaX","^(?:a)++(\\w)",[trim]))), + 2}]))), + <<"YZ">> = iolist_to_binary(join(re:split("YZ","^(a)++(\\w)",[]))), + <<":X">> = iolist_to_binary(join(re:split("aaaaX","^(?:a)++(\\w)",[trim]))), <<":X:">> = iolist_to_binary(join(re:split("aaaaX","^(?:a)++(\\w)",[{parts, - 2}]))), - <<":X:">> = iolist_to_binary(join(re:split("aaaaX","^(?:a)++(\\w)",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?:a)++(\\w)",[trim]))), + 2}]))), + <<":X:">> = iolist_to_binary(join(re:split("aaaaX","^(?:a)++(\\w)",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?:a)++(\\w)",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?:a)++(\\w)",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?:a)++(\\w)",[]))), - <<"aaaa">> = iolist_to_binary(join(re:split("aaaa","^(?:a)++(\\w)",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?:a)++(\\w)",[]))), + <<"aaaa">> = iolist_to_binary(join(re:split("aaaa","^(?:a)++(\\w)",[trim]))), <<"aaaa">> = iolist_to_binary(join(re:split("aaaa","^(?:a)++(\\w)",[{parts, - 2}]))), - <<"aaaa">> = iolist_to_binary(join(re:split("aaaa","^(?:a)++(\\w)",[]))), - <<"YZ">> = iolist_to_binary(join(re:split("YZ","^(?:a)++(\\w)",[trim]))), + 2}]))), + <<"aaaa">> = iolist_to_binary(join(re:split("aaaa","^(?:a)++(\\w)",[]))), + <<"YZ">> = iolist_to_binary(join(re:split("YZ","^(?:a)++(\\w)",[trim]))), <<"YZ">> = iolist_to_binary(join(re:split("YZ","^(?:a)++(\\w)",[{parts, - 2}]))), - <<"YZ">> = iolist_to_binary(join(re:split("YZ","^(?:a)++(\\w)",[]))), - <<":a:a:aaX">> = iolist_to_binary(join(re:split("aaaaX","^(a)?+(\\w)",[trim]))), + 2}]))), + <<"YZ">> = iolist_to_binary(join(re:split("YZ","^(?:a)++(\\w)",[]))), + <<":a:a:aaX">> = iolist_to_binary(join(re:split("aaaaX","^(a)?+(\\w)",[trim]))), <<":a:a:aaX">> = iolist_to_binary(join(re:split("aaaaX","^(a)?+(\\w)",[{parts, - 2}]))), - <<":a:a:aaX">> = iolist_to_binary(join(re:split("aaaaX","^(a)?+(\\w)",[]))), - <<"::Y:Z">> = iolist_to_binary(join(re:split("YZ","^(a)?+(\\w)",[trim]))), + 2}]))), + <<":a:a:aaX">> = iolist_to_binary(join(re:split("aaaaX","^(a)?+(\\w)",[]))), + <<"::Y:Z">> = iolist_to_binary(join(re:split("YZ","^(a)?+(\\w)",[trim]))), <<"::Y:Z">> = iolist_to_binary(join(re:split("YZ","^(a)?+(\\w)",[{parts, - 2}]))), - <<"::Y:Z">> = iolist_to_binary(join(re:split("YZ","^(a)?+(\\w)",[]))), - <<":a:aaX">> = iolist_to_binary(join(re:split("aaaaX","^(?:a)?+(\\w)",[trim]))), + 2}]))), + <<"::Y:Z">> = iolist_to_binary(join(re:split("YZ","^(a)?+(\\w)",[]))), + <<":a:aaX">> = iolist_to_binary(join(re:split("aaaaX","^(?:a)?+(\\w)",[trim]))), <<":a:aaX">> = iolist_to_binary(join(re:split("aaaaX","^(?:a)?+(\\w)",[{parts, - 2}]))), - <<":a:aaX">> = iolist_to_binary(join(re:split("aaaaX","^(?:a)?+(\\w)",[]))), - <<":Y:Z">> = iolist_to_binary(join(re:split("YZ","^(?:a)?+(\\w)",[trim]))), + 2}]))), + <<":a:aaX">> = iolist_to_binary(join(re:split("aaaaX","^(?:a)?+(\\w)",[]))), + <<":Y:Z">> = iolist_to_binary(join(re:split("YZ","^(?:a)?+(\\w)",[trim]))), <<":Y:Z">> = iolist_to_binary(join(re:split("YZ","^(?:a)?+(\\w)",[{parts, - 2}]))), - <<":Y:Z">> = iolist_to_binary(join(re:split("YZ","^(?:a)?+(\\w)",[]))), - <<":a:X">> = iolist_to_binary(join(re:split("aaaaX","^(a){2,}+(\\w)",[trim]))), + 2}]))), + <<":Y:Z">> = iolist_to_binary(join(re:split("YZ","^(?:a)?+(\\w)",[]))), + <<":a:X">> = iolist_to_binary(join(re:split("aaaaX","^(a){2,}+(\\w)",[trim]))), <<":a:X:">> = iolist_to_binary(join(re:split("aaaaX","^(a){2,}+(\\w)",[{parts, - 2}]))), - <<":a:X:">> = iolist_to_binary(join(re:split("aaaaX","^(a){2,}+(\\w)",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a){2,}+(\\w)",[trim]))), + 2}]))), + <<":a:X:">> = iolist_to_binary(join(re:split("aaaaX","^(a){2,}+(\\w)",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a){2,}+(\\w)",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a){2,}+(\\w)",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a){2,}+(\\w)",[]))), - <<"aaa">> = iolist_to_binary(join(re:split("aaa","^(a){2,}+(\\w)",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a){2,}+(\\w)",[]))), + <<"aaa">> = iolist_to_binary(join(re:split("aaa","^(a){2,}+(\\w)",[trim]))), <<"aaa">> = iolist_to_binary(join(re:split("aaa","^(a){2,}+(\\w)",[{parts, - 2}]))), - <<"aaa">> = iolist_to_binary(join(re:split("aaa","^(a){2,}+(\\w)",[]))), - <<"YZ">> = iolist_to_binary(join(re:split("YZ","^(a){2,}+(\\w)",[trim]))), + 2}]))), + <<"aaa">> = iolist_to_binary(join(re:split("aaa","^(a){2,}+(\\w)",[]))), + <<"YZ">> = iolist_to_binary(join(re:split("YZ","^(a){2,}+(\\w)",[trim]))), <<"YZ">> = iolist_to_binary(join(re:split("YZ","^(a){2,}+(\\w)",[{parts, - 2}]))), - <<"YZ">> = iolist_to_binary(join(re:split("YZ","^(a){2,}+(\\w)",[]))), - <<":X">> = iolist_to_binary(join(re:split("aaaaX","^(?:a){2,}+(\\w)",[trim]))), + 2}]))), + <<"YZ">> = iolist_to_binary(join(re:split("YZ","^(a){2,}+(\\w)",[]))), + <<":X">> = iolist_to_binary(join(re:split("aaaaX","^(?:a){2,}+(\\w)",[trim]))), <<":X:">> = iolist_to_binary(join(re:split("aaaaX","^(?:a){2,}+(\\w)",[{parts, - 2}]))), - <<":X:">> = iolist_to_binary(join(re:split("aaaaX","^(?:a){2,}+(\\w)",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?:a){2,}+(\\w)",[trim]))), + 2}]))), + <<":X:">> = iolist_to_binary(join(re:split("aaaaX","^(?:a){2,}+(\\w)",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?:a){2,}+(\\w)",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?:a){2,}+(\\w)",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?:a){2,}+(\\w)",[]))), - <<"aaa">> = iolist_to_binary(join(re:split("aaa","^(?:a){2,}+(\\w)",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?:a){2,}+(\\w)",[]))), + <<"aaa">> = iolist_to_binary(join(re:split("aaa","^(?:a){2,}+(\\w)",[trim]))), <<"aaa">> = iolist_to_binary(join(re:split("aaa","^(?:a){2,}+(\\w)",[{parts, - 2}]))), - <<"aaa">> = iolist_to_binary(join(re:split("aaa","^(?:a){2,}+(\\w)",[]))), - <<"YZ">> = iolist_to_binary(join(re:split("YZ","^(?:a){2,}+(\\w)",[trim]))), + 2}]))), + <<"aaa">> = iolist_to_binary(join(re:split("aaa","^(?:a){2,}+(\\w)",[]))), + <<"YZ">> = iolist_to_binary(join(re:split("YZ","^(?:a){2,}+(\\w)",[trim]))), <<"YZ">> = iolist_to_binary(join(re:split("YZ","^(?:a){2,}+(\\w)",[{parts, - 2}]))), - <<"YZ">> = iolist_to_binary(join(re:split("YZ","^(?:a){2,}+(\\w)",[]))), - <<"">> = iolist_to_binary(join(re:split("b","(a|)*(?1)b",[trim]))), + 2}]))), + <<"YZ">> = iolist_to_binary(join(re:split("YZ","^(?:a){2,}+(\\w)",[]))), + <<"">> = iolist_to_binary(join(re:split("b","(a|)*(?1)b",[trim]))), <<"::">> = iolist_to_binary(join(re:split("b","(a|)*(?1)b",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("b","(a|)*(?1)b",[]))), - <<"">> = iolist_to_binary(join(re:split("ab","(a|)*(?1)b",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("b","(a|)*(?1)b",[]))), + <<"">> = iolist_to_binary(join(re:split("ab","(a|)*(?1)b",[trim]))), <<"::">> = iolist_to_binary(join(re:split("ab","(a|)*(?1)b",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("ab","(a|)*(?1)b",[]))), - <<"">> = iolist_to_binary(join(re:split("aab","(a|)*(?1)b",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("ab","(a|)*(?1)b",[]))), + <<"">> = iolist_to_binary(join(re:split("aab","(a|)*(?1)b",[trim]))), <<"::">> = iolist_to_binary(join(re:split("aab","(a|)*(?1)b",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("aab","(a|)*(?1)b",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(a)++(?1)b",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("aab","(a|)*(?1)b",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(a)++(?1)b",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(a)++(?1)b",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(a)++(?1)b",[]))), - <<"ab">> = iolist_to_binary(join(re:split("ab","(a)++(?1)b",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(a)++(?1)b",[]))), + <<"ab">> = iolist_to_binary(join(re:split("ab","(a)++(?1)b",[trim]))), <<"ab">> = iolist_to_binary(join(re:split("ab","(a)++(?1)b",[{parts, - 2}]))), - <<"ab">> = iolist_to_binary(join(re:split("ab","(a)++(?1)b",[]))), - <<"aab">> = iolist_to_binary(join(re:split("aab","(a)++(?1)b",[trim]))), + 2}]))), + <<"ab">> = iolist_to_binary(join(re:split("ab","(a)++(?1)b",[]))), + <<"aab">> = iolist_to_binary(join(re:split("aab","(a)++(?1)b",[trim]))), <<"aab">> = iolist_to_binary(join(re:split("aab","(a)++(?1)b",[{parts, - 2}]))), - <<"aab">> = iolist_to_binary(join(re:split("aab","(a)++(?1)b",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(a)*+(?1)b",[trim]))), + 2}]))), + <<"aab">> = iolist_to_binary(join(re:split("aab","(a)++(?1)b",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(a)*+(?1)b",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(a)*+(?1)b",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(a)*+(?1)b",[]))), - <<"ab">> = iolist_to_binary(join(re:split("ab","(a)*+(?1)b",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(a)*+(?1)b",[]))), + <<"ab">> = iolist_to_binary(join(re:split("ab","(a)*+(?1)b",[trim]))), <<"ab">> = iolist_to_binary(join(re:split("ab","(a)*+(?1)b",[{parts, - 2}]))), - <<"ab">> = iolist_to_binary(join(re:split("ab","(a)*+(?1)b",[]))), - <<"aab">> = iolist_to_binary(join(re:split("aab","(a)*+(?1)b",[trim]))), + 2}]))), + <<"ab">> = iolist_to_binary(join(re:split("ab","(a)*+(?1)b",[]))), + <<"aab">> = iolist_to_binary(join(re:split("aab","(a)*+(?1)b",[trim]))), <<"aab">> = iolist_to_binary(join(re:split("aab","(a)*+(?1)b",[{parts, - 2}]))), - <<"aab">> = iolist_to_binary(join(re:split("aab","(a)*+(?1)b",[]))), - <<"">> = iolist_to_binary(join(re:split("b","(?1)(?:(b)){0}",[trim]))), + 2}]))), + <<"aab">> = iolist_to_binary(join(re:split("aab","(a)*+(?1)b",[]))), + <<"">> = iolist_to_binary(join(re:split("b","(?1)(?:(b)){0}",[trim]))), <<"::">> = iolist_to_binary(join(re:split("b","(?1)(?:(b)){0}",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("b","(?1)(?:(b)){0}",[]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("b","(?1)(?:(b)){0}",[]))), <<":foo(bar(baz)+baz(bop)):(bar(baz)+baz(bop)):bar(baz)+baz(bop)">> = iolist_to_binary(join(re:split("foo(bar(baz)+baz(bop))","(foo ( \\( ((?:(?> [^()]+ )|(?2))*) \\) ) )",[extended, - trim]))), + trim]))), <<":foo(bar(baz)+baz(bop)):(bar(baz)+baz(bop)):bar(baz)+baz(bop):">> = iolist_to_binary(join(re:split("foo(bar(baz)+baz(bop))","(foo ( \\( ((?:(?> [^()]+ )|(?2))*) \\) ) )",[extended, {parts, - 2}]))), - <<":foo(bar(baz)+baz(bop)):(bar(baz)+baz(bop)):bar(baz)+baz(bop):">> = iolist_to_binary(join(re:split("foo(bar(baz)+baz(bop))","(foo ( \\( ((?:(?> [^()]+ )|(?2))*) \\) ) )",[extended]))), + 2}]))), + <<":foo(bar(baz)+baz(bop)):(bar(baz)+baz(bop)):bar(baz)+baz(bop):">> = iolist_to_binary(join(re:split("foo(bar(baz)+baz(bop))","(foo ( \\( ((?:(?> [^()]+ )|(?2))*) \\) ) )",[extended]))), <<":AB:B">> = iolist_to_binary(join(re:split("AB","(A (A|B(*ACCEPT)|C) D)(E)",[extended, - trim]))), + trim]))), <<":AB:B::">> = iolist_to_binary(join(re:split("AB","(A (A|B(*ACCEPT)|C) D)(E)",[extended, {parts, - 2}]))), - <<":AB:B::">> = iolist_to_binary(join(re:split("AB","(A (A|B(*ACCEPT)|C) D)(E)",[extended]))), + 2}]))), + <<":AB:B::">> = iolist_to_binary(join(re:split("AB","(A (A|B(*ACCEPT)|C) D)(E)",[extended]))), ok. run46() -> - <<":a">> = iolist_to_binary(join(re:split("ba","\\A.*?(a|bc)",[trim]))), + <<":a">> = iolist_to_binary(join(re:split("ba","\\A.*?(a|bc)",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("ba","\\A.*?(a|bc)",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("ba","\\A.*?(a|bc)",[]))), - <<"">> = iolist_to_binary(join(re:split("ba","\\A.*?(?:a|bc)++",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("ba","\\A.*?(a|bc)",[]))), + <<"">> = iolist_to_binary(join(re:split("ba","\\A.*?(?:a|bc)++",[trim]))), <<":">> = iolist_to_binary(join(re:split("ba","\\A.*?(?:a|bc)++",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ba","\\A.*?(?:a|bc)++",[]))), - <<":a">> = iolist_to_binary(join(re:split("ba","\\A.*?(a|bc)++",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ba","\\A.*?(?:a|bc)++",[]))), + <<":a">> = iolist_to_binary(join(re:split("ba","\\A.*?(a|bc)++",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("ba","\\A.*?(a|bc)++",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("ba","\\A.*?(a|bc)++",[]))), - <<"">> = iolist_to_binary(join(re:split("ba","\\A.*?(?:a|bc|d)",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("ba","\\A.*?(a|bc)++",[]))), + <<"">> = iolist_to_binary(join(re:split("ba","\\A.*?(?:a|bc|d)",[trim]))), <<":">> = iolist_to_binary(join(re:split("ba","\\A.*?(?:a|bc|d)",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ba","\\A.*?(?:a|bc|d)",[]))), - <<":b:eetle">> = iolist_to_binary(join(re:split("beetle","(?:(b))++",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ba","\\A.*?(?:a|bc|d)",[]))), + <<":b:eetle">> = iolist_to_binary(join(re:split("beetle","(?:(b))++",[trim]))), <<":b:eetle">> = iolist_to_binary(join(re:split("beetle","(?:(b))++",[{parts, - 2}]))), - <<":b:eetle">> = iolist_to_binary(join(re:split("beetle","(?:(b))++",[]))), - <<":a">> = iolist_to_binary(join(re:split("a","(?(?=(a(*ACCEPT)z))a)",[trim]))), + 2}]))), + <<":b:eetle">> = iolist_to_binary(join(re:split("beetle","(?:(b))++",[]))), + <<":a">> = iolist_to_binary(join(re:split("a","(?(?=(a(*ACCEPT)z))a)",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("a","(?(?=(a(*ACCEPT)z))a)",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("a","(?(?=(a(*ACCEPT)z))a)",[]))), - <<":a">> = iolist_to_binary(join(re:split("aaaab","^(a)(?1)+ab",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("a","(?(?=(a(*ACCEPT)z))a)",[]))), + <<":a">> = iolist_to_binary(join(re:split("aaaab","^(a)(?1)+ab",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aaaab","^(a)(?1)+ab",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aaaab","^(a)(?1)+ab",[]))), - <<"aaaab">> = iolist_to_binary(join(re:split("aaaab","^(a)(?1)++ab",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aaaab","^(a)(?1)+ab",[]))), + <<"aaaab">> = iolist_to_binary(join(re:split("aaaab","^(a)(?1)++ab",[trim]))), <<"aaaab">> = iolist_to_binary(join(re:split("aaaab","^(a)(?1)++ab",[{parts, - 2}]))), - <<"aaaab">> = iolist_to_binary(join(re:split("aaaab","^(a)(?1)++ab",[]))), - <<"::ckgammon">> = iolist_to_binary(join(re:split("backgammon","(?(DEFINE)(a))?b(?1)",[trim]))), + 2}]))), + <<"aaaab">> = iolist_to_binary(join(re:split("aaaab","^(a)(?1)++ab",[]))), + <<"::ckgammon">> = iolist_to_binary(join(re:split("backgammon","(?(DEFINE)(a))?b(?1)",[trim]))), <<"::ckgammon">> = iolist_to_binary(join(re:split("backgammon","(?(DEFINE)(a))?b(?1)",[{parts, - 2}]))), - <<"::ckgammon">> = iolist_to_binary(join(re:split("backgammon","(?(DEFINE)(a))?b(?1)",[]))), + 2}]))), + <<"::ckgammon">> = iolist_to_binary(join(re:split("backgammon","(?(DEFINE)(a))?b(?1)",[]))), <<": def">> = iolist_to_binary(join(re:split("abc -def","^\\N+",[trim]))), +def","^\\N+",[trim]))), <<": def">> = iolist_to_binary(join(re:split("abc -def","^\\N+",[{parts,2}]))), +def","^\\N+",[{parts,2}]))), <<": def">> = iolist_to_binary(join(re:split("abc -def","^\\N+",[]))), +def","^\\N+",[]))), <<": def">> = iolist_to_binary(join(re:split("abc -def","^\\N{1,}",[trim]))), +def","^\\N{1,}",[trim]))), <<": def">> = iolist_to_binary(join(re:split("abc -def","^\\N{1,}",[{parts,2}]))), +def","^\\N{1,}",[{parts,2}]))), <<": def">> = iolist_to_binary(join(re:split("abc -def","^\\N{1,}",[]))), - <<":cde">> = iolist_to_binary(join(re:split("aaaabcde","(?(R)a+|(?R)b)",[trim]))), +def","^\\N{1,}",[]))), + <<":cde">> = iolist_to_binary(join(re:split("aaaabcde","(?(R)a+|(?R)b)",[trim]))), <<":cde">> = iolist_to_binary(join(re:split("aaaabcde","(?(R)a+|(?R)b)",[{parts, - 2}]))), - <<":cde">> = iolist_to_binary(join(re:split("aaaabcde","(?(R)a+|(?R)b)",[]))), - <<":aaaa:cde">> = iolist_to_binary(join(re:split("aaaabcde","(?(R)a+|((?R))b)",[trim]))), + 2}]))), + <<":cde">> = iolist_to_binary(join(re:split("aaaabcde","(?(R)a+|(?R)b)",[]))), + <<":aaaa:cde">> = iolist_to_binary(join(re:split("aaaabcde","(?(R)a+|((?R))b)",[trim]))), <<":aaaa:cde">> = iolist_to_binary(join(re:split("aaaabcde","(?(R)a+|((?R))b)",[{parts, - 2}]))), - <<":aaaa:cde">> = iolist_to_binary(join(re:split("aaaabcde","(?(R)a+|((?R))b)",[]))), - <<":aaaab:cde">> = iolist_to_binary(join(re:split("aaaabcde","((?(R)a+|(?1)b))",[trim]))), + 2}]))), + <<":aaaa:cde">> = iolist_to_binary(join(re:split("aaaabcde","(?(R)a+|((?R))b)",[]))), + <<":aaaab:cde">> = iolist_to_binary(join(re:split("aaaabcde","((?(R)a+|(?1)b))",[trim]))), <<":aaaab:cde">> = iolist_to_binary(join(re:split("aaaabcde","((?(R)a+|(?1)b))",[{parts, - 2}]))), - <<":aaaab:cde">> = iolist_to_binary(join(re:split("aaaabcde","((?(R)a+|(?1)b))",[]))), - <<":aaaab:cde">> = iolist_to_binary(join(re:split("aaaabcde","((?(R1)a+|(?1)b))",[trim]))), + 2}]))), + <<":aaaab:cde">> = iolist_to_binary(join(re:split("aaaabcde","((?(R)a+|(?1)b))",[]))), + <<":aaaab:cde">> = iolist_to_binary(join(re:split("aaaabcde","((?(R1)a+|(?1)b))",[trim]))), <<":aaaab:cde">> = iolist_to_binary(join(re:split("aaaabcde","((?(R1)a+|(?1)b))",[{parts, - 2}]))), - <<":aaaab:cde">> = iolist_to_binary(join(re:split("aaaabcde","((?(R1)a+|(?1)b))",[]))), - <<":a">> = iolist_to_binary(join(re:split("aaa","((?(R)a|(?1)))*",[trim]))), + 2}]))), + <<":aaaab:cde">> = iolist_to_binary(join(re:split("aaaabcde","((?(R1)a+|(?1)b))",[]))), + <<":a">> = iolist_to_binary(join(re:split("aaa","((?(R)a|(?1)))*",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aaa","((?(R)a|(?1)))*",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aaa","((?(R)a|(?1)))*",[]))), - <<":a">> = iolist_to_binary(join(re:split("aaa","((?(R)a|(?1)))+",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aaa","((?(R)a|(?1)))*",[]))), + <<":a">> = iolist_to_binary(join(re:split("aaa","((?(R)a|(?1)))+",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aaa","((?(R)a|(?1)))+",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aaa","((?(R)a|(?1)))+",[]))), - <<"">> = iolist_to_binary(join(re:split("a","(?>(?&t)c|(?&t))(?(DEFINE)(?<t>a|b(*PRUNE)c))",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aaa","((?(R)a|(?1)))+",[]))), + <<"">> = iolist_to_binary(join(re:split("a","(?>(?&t)c|(?&t))(?(DEFINE)(?<t>a|b(*PRUNE)c))",[trim]))), <<"::">> = iolist_to_binary(join(re:split("a","(?>(?&t)c|(?&t))(?(DEFINE)(?<t>a|b(*PRUNE)c))",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("a","(?>(?&t)c|(?&t))(?(DEFINE)(?<t>a|b(*PRUNE)c))",[]))), - <<"b">> = iolist_to_binary(join(re:split("ba","(?>(?&t)c|(?&t))(?(DEFINE)(?<t>a|b(*PRUNE)c))",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("a","(?>(?&t)c|(?&t))(?(DEFINE)(?<t>a|b(*PRUNE)c))",[]))), + <<"b">> = iolist_to_binary(join(re:split("ba","(?>(?&t)c|(?&t))(?(DEFINE)(?<t>a|b(*PRUNE)c))",[trim]))), <<"b::">> = iolist_to_binary(join(re:split("ba","(?>(?&t)c|(?&t))(?(DEFINE)(?<t>a|b(*PRUNE)c))",[{parts, - 2}]))), - <<"b::">> = iolist_to_binary(join(re:split("ba","(?>(?&t)c|(?&t))(?(DEFINE)(?<t>a|b(*PRUNE)c))",[]))), - <<"bb">> = iolist_to_binary(join(re:split("bba","(?>(?&t)c|(?&t))(?(DEFINE)(?<t>a|b(*PRUNE)c))",[trim]))), + 2}]))), + <<"b::">> = iolist_to_binary(join(re:split("ba","(?>(?&t)c|(?&t))(?(DEFINE)(?<t>a|b(*PRUNE)c))",[]))), + <<"bb">> = iolist_to_binary(join(re:split("bba","(?>(?&t)c|(?&t))(?(DEFINE)(?<t>a|b(*PRUNE)c))",[trim]))), <<"bb::">> = iolist_to_binary(join(re:split("bba","(?>(?&t)c|(?&t))(?(DEFINE)(?<t>a|b(*PRUNE)c))",[{parts, - 2}]))), - <<"bb::">> = iolist_to_binary(join(re:split("bba","(?>(?&t)c|(?&t))(?(DEFINE)(?<t>a|b(*PRUNE)c))",[]))), + 2}]))), + <<"bb::">> = iolist_to_binary(join(re:split("bba","(?>(?&t)c|(?&t))(?(DEFINE)(?<t>a|b(*PRUNE)c))",[]))), ok. run47() -> <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (a(*THEN)b) c",[extended, - trim]))), + trim]))), <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (a(*THEN)b) c",[extended, {parts, - 2}]))), - <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (a(*THEN)b) c",[extended]))), + 2}]))), + <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (a(*THEN)b) c",[extended]))), <<":ab">> = iolist_to_binary(join(re:split("aabc","^.*? (a(*THEN)b|(*F)) c",[extended, - trim]))), + trim]))), <<":ab:">> = iolist_to_binary(join(re:split("aabc","^.*? (a(*THEN)b|(*F)) c",[extended, {parts, - 2}]))), - <<":ab:">> = iolist_to_binary(join(re:split("aabc","^.*? (a(*THEN)b|(*F)) c",[extended]))), + 2}]))), + <<":ab:">> = iolist_to_binary(join(re:split("aabc","^.*? (a(*THEN)b|(*F)) c",[extended]))), <<":ab:ab">> = iolist_to_binary(join(re:split("aabc","^.*? ( (a(*THEN)b) | (*F) ) c",[extended, - trim]))), + trim]))), <<":ab:ab:">> = iolist_to_binary(join(re:split("aabc","^.*? ( (a(*THEN)b) | (*F) ) c",[extended, {parts, - 2}]))), - <<":ab:ab:">> = iolist_to_binary(join(re:split("aabc","^.*? ( (a(*THEN)b) | (*F) ) c",[extended]))), + 2}]))), + <<":ab:ab:">> = iolist_to_binary(join(re:split("aabc","^.*? ( (a(*THEN)b) | (*F) ) c",[extended]))), <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? ( (a(*THEN)b) ) c",[extended, - trim]))), + trim]))), <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? ( (a(*THEN)b) ) c",[extended, {parts, - 2}]))), - <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? ( (a(*THEN)b) ) c",[extended]))), + 2}]))), + <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? ( (a(*THEN)b) ) c",[extended]))), <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?:a(*THEN)b) c",[extended, - trim]))), + trim]))), <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?:a(*THEN)b) c",[extended, {parts, - 2}]))), - <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?:a(*THEN)b) c",[extended]))), + 2}]))), + <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?:a(*THEN)b) c",[extended]))), <<"">> = iolist_to_binary(join(re:split("aabc","^.*? (?:a(*THEN)b|(*F)) c",[extended, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("aabc","^.*? (?:a(*THEN)b|(*F)) c",[extended, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aabc","^.*? (?:a(*THEN)b|(*F)) c",[extended]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aabc","^.*? (?:a(*THEN)b|(*F)) c",[extended]))), <<"">> = iolist_to_binary(join(re:split("aabc","^.*? (?: (?:a(*THEN)b) | (*F) ) c",[extended, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("aabc","^.*? (?: (?:a(*THEN)b) | (*F) ) c",[extended, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aabc","^.*? (?: (?:a(*THEN)b) | (*F) ) c",[extended]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aabc","^.*? (?: (?:a(*THEN)b) | (*F) ) c",[extended]))), <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?: (?:a(*THEN)b) ) c",[extended, - trim]))), + trim]))), <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?: (?:a(*THEN)b) ) c",[extended, {parts, - 2}]))), - <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?: (?:a(*THEN)b) ) c",[extended]))), + 2}]))), + <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?: (?:a(*THEN)b) ) c",[extended]))), <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?>a(*THEN)b) c",[extended, - trim]))), + trim]))), <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?>a(*THEN)b) c",[extended, {parts, - 2}]))), - <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?>a(*THEN)b) c",[extended]))), + 2}]))), + <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?>a(*THEN)b) c",[extended]))), <<"">> = iolist_to_binary(join(re:split("aabc","^.*? (?>a(*THEN)b|(*F)) c",[extended, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("aabc","^.*? (?>a(*THEN)b|(*F)) c",[extended, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aabc","^.*? (?>a(*THEN)b|(*F)) c",[extended]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aabc","^.*? (?>a(*THEN)b|(*F)) c",[extended]))), <<"">> = iolist_to_binary(join(re:split("aabc","^.*? (?> (?>a(*THEN)b) | (*F) ) c",[extended, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("aabc","^.*? (?> (?>a(*THEN)b) | (*F) ) c",[extended, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aabc","^.*? (?> (?>a(*THEN)b) | (*F) ) c",[extended]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aabc","^.*? (?> (?>a(*THEN)b) | (*F) ) c",[extended]))), <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?> (?>a(*THEN)b) ) c",[extended, - trim]))), + trim]))), <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?> (?>a(*THEN)b) ) c",[extended, {parts, - 2}]))), - <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?> (?>a(*THEN)b) ) c",[extended]))), + 2}]))), + <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?> (?>a(*THEN)b) ) c",[extended]))), <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (a(*THEN)b)++ c",[extended, - trim]))), + trim]))), <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (a(*THEN)b)++ c",[extended, {parts, - 2}]))), - <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (a(*THEN)b)++ c",[extended]))), + 2}]))), + <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (a(*THEN)b)++ c",[extended]))), <<":ab">> = iolist_to_binary(join(re:split("aabc","^.*? (a(*THEN)b|(*F))++ c",[extended, - trim]))), + trim]))), <<":ab:">> = iolist_to_binary(join(re:split("aabc","^.*? (a(*THEN)b|(*F))++ c",[extended, {parts, - 2}]))), - <<":ab:">> = iolist_to_binary(join(re:split("aabc","^.*? (a(*THEN)b|(*F))++ c",[extended]))), + 2}]))), + <<":ab:">> = iolist_to_binary(join(re:split("aabc","^.*? (a(*THEN)b|(*F))++ c",[extended]))), <<":ab:ab">> = iolist_to_binary(join(re:split("aabc","^.*? ( (a(*THEN)b)++ | (*F) )++ c",[extended, - trim]))), + trim]))), <<":ab:ab:">> = iolist_to_binary(join(re:split("aabc","^.*? ( (a(*THEN)b)++ | (*F) )++ c",[extended, {parts, - 2}]))), - <<":ab:ab:">> = iolist_to_binary(join(re:split("aabc","^.*? ( (a(*THEN)b)++ | (*F) )++ c",[extended]))), + 2}]))), + <<":ab:ab:">> = iolist_to_binary(join(re:split("aabc","^.*? ( (a(*THEN)b)++ | (*F) )++ c",[extended]))), <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? ( (a(*THEN)b)++ )++ c",[extended, - trim]))), + trim]))), <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? ( (a(*THEN)b)++ )++ c",[extended, {parts, - 2}]))), - <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? ( (a(*THEN)b)++ )++ c",[extended]))), + 2}]))), + <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? ( (a(*THEN)b)++ )++ c",[extended]))), ok. run48() -> <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?:a(*THEN)b)++ c",[extended, - trim]))), + trim]))), <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?:a(*THEN)b)++ c",[extended, {parts, - 2}]))), - <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?:a(*THEN)b)++ c",[extended]))), + 2}]))), + <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?:a(*THEN)b)++ c",[extended]))), <<"">> = iolist_to_binary(join(re:split("aabc","^.*? (?:a(*THEN)b|(*F))++ c",[extended, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("aabc","^.*? (?:a(*THEN)b|(*F))++ c",[extended, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aabc","^.*? (?:a(*THEN)b|(*F))++ c",[extended]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aabc","^.*? (?:a(*THEN)b|(*F))++ c",[extended]))), <<"">> = iolist_to_binary(join(re:split("aabc","^.*? (?: (?:a(*THEN)b)++ | (*F) )++ c",[extended, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("aabc","^.*? (?: (?:a(*THEN)b)++ | (*F) )++ c",[extended, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aabc","^.*? (?: (?:a(*THEN)b)++ | (*F) )++ c",[extended]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aabc","^.*? (?: (?:a(*THEN)b)++ | (*F) )++ c",[extended]))), <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?: (?:a(*THEN)b)++ )++ c",[extended, - trim]))), + trim]))), <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?: (?:a(*THEN)b)++ )++ c",[extended, {parts, - 2}]))), - <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?: (?:a(*THEN)b)++ )++ c",[extended]))), - <<"">> = iolist_to_binary(join(re:split("ac","^(?(?=a(*THEN)b)ab|ac)",[trim]))), + 2}]))), + <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?: (?:a(*THEN)b)++ )++ c",[extended]))), + <<"">> = iolist_to_binary(join(re:split("ac","^(?(?=a(*THEN)b)ab|ac)",[trim]))), <<":">> = iolist_to_binary(join(re:split("ac","^(?(?=a(*THEN)b)ab|ac)",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ac","^(?(?=a(*THEN)b)ab|ac)",[]))), - <<"ba">> = iolist_to_binary(join(re:split("ba","^.*?(?(?=a)a|b(*THEN)c)",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ac","^(?(?=a(*THEN)b)ab|ac)",[]))), + <<"ba">> = iolist_to_binary(join(re:split("ba","^.*?(?(?=a)a|b(*THEN)c)",[trim]))), <<"ba">> = iolist_to_binary(join(re:split("ba","^.*?(?(?=a)a|b(*THEN)c)",[{parts, - 2}]))), - <<"ba">> = iolist_to_binary(join(re:split("ba","^.*?(?(?=a)a|b(*THEN)c)",[]))), - <<"">> = iolist_to_binary(join(re:split("ba","^.*?(?:(?(?=a)a|b(*THEN)c)|d)",[trim]))), + 2}]))), + <<"ba">> = iolist_to_binary(join(re:split("ba","^.*?(?(?=a)a|b(*THEN)c)",[]))), + <<"">> = iolist_to_binary(join(re:split("ba","^.*?(?:(?(?=a)a|b(*THEN)c)|d)",[trim]))), <<":">> = iolist_to_binary(join(re:split("ba","^.*?(?:(?(?=a)a|b(*THEN)c)|d)",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ba","^.*?(?:(?(?=a)a|b(*THEN)c)|d)",[]))), - <<"ac">> = iolist_to_binary(join(re:split("ac","^.*?(?(?=a)a(*THEN)b|c)",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ba","^.*?(?:(?(?=a)a|b(*THEN)c)|d)",[]))), + <<"ac">> = iolist_to_binary(join(re:split("ac","^.*?(?(?=a)a(*THEN)b|c)",[trim]))), <<"ac">> = iolist_to_binary(join(re:split("ac","^.*?(?(?=a)a(*THEN)b|c)",[{parts, - 2}]))), - <<"ac">> = iolist_to_binary(join(re:split("ac","^.*?(?(?=a)a(*THEN)b|c)",[]))), - <<":abc">> = iolist_to_binary(join(re:split("aabc","^.*(?=a(*THEN)b)",[trim]))), + 2}]))), + <<"ac">> = iolist_to_binary(join(re:split("ac","^.*?(?(?=a)a(*THEN)b|c)",[]))), + <<":abc">> = iolist_to_binary(join(re:split("aabc","^.*(?=a(*THEN)b)",[trim]))), <<":abc">> = iolist_to_binary(join(re:split("aabc","^.*(?=a(*THEN)b)",[{parts, - 2}]))), - <<":abc">> = iolist_to_binary(join(re:split("aabc","^.*(?=a(*THEN)b)",[]))), - <<"xa:d">> = iolist_to_binary(join(re:split("xacd","(?<=a(*ACCEPT)b)c",[trim]))), + 2}]))), + <<":abc">> = iolist_to_binary(join(re:split("aabc","^.*(?=a(*THEN)b)",[]))), + <<"xa:d">> = iolist_to_binary(join(re:split("xacd","(?<=a(*ACCEPT)b)c",[trim]))), <<"xa:d">> = iolist_to_binary(join(re:split("xacd","(?<=a(*ACCEPT)b)c",[{parts, - 2}]))), - <<"xa:d">> = iolist_to_binary(join(re:split("xacd","(?<=a(*ACCEPT)b)c",[]))), - <<"xa:a:d">> = iolist_to_binary(join(re:split("xacd","(?<=(a(*ACCEPT)b))c",[trim]))), + 2}]))), + <<"xa:d">> = iolist_to_binary(join(re:split("xacd","(?<=a(*ACCEPT)b)c",[]))), + <<"xa:a:d">> = iolist_to_binary(join(re:split("xacd","(?<=(a(*ACCEPT)b))c",[trim]))), <<"xa:a:d">> = iolist_to_binary(join(re:split("xacd","(?<=(a(*ACCEPT)b))c",[{parts, - 2}]))), - <<"xa:a:d">> = iolist_to_binary(join(re:split("xacd","(?<=(a(*ACCEPT)b))c",[]))), - <<"xab:ab:d">> = iolist_to_binary(join(re:split("xabcd","(?<=(a(*COMMIT)b))c",[trim]))), + 2}]))), + <<"xa:a:d">> = iolist_to_binary(join(re:split("xacd","(?<=(a(*ACCEPT)b))c",[]))), + <<"xab:ab:d">> = iolist_to_binary(join(re:split("xabcd","(?<=(a(*COMMIT)b))c",[trim]))), <<"xab:ab:d">> = iolist_to_binary(join(re:split("xabcd","(?<=(a(*COMMIT)b))c",[{parts, - 2}]))), - <<"xab:ab:d">> = iolist_to_binary(join(re:split("xabcd","(?<=(a(*COMMIT)b))c",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=(a(*COMMIT)b))c",[trim]))), + 2}]))), + <<"xab:ab:d">> = iolist_to_binary(join(re:split("xabcd","(?<=(a(*COMMIT)b))c",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=(a(*COMMIT)b))c",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=(a(*COMMIT)b))c",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=(a(*COMMIT)b))c",[]))), - <<"xacd">> = iolist_to_binary(join(re:split("xacd","(?<=(a(*COMMIT)b))c",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=(a(*COMMIT)b))c",[]))), + <<"xacd">> = iolist_to_binary(join(re:split("xacd","(?<=(a(*COMMIT)b))c",[trim]))), <<"xacd">> = iolist_to_binary(join(re:split("xacd","(?<=(a(*COMMIT)b))c",[{parts, - 2}]))), - <<"xacd">> = iolist_to_binary(join(re:split("xacd","(?<=(a(*COMMIT)b))c",[]))), - <<"x:d">> = iolist_to_binary(join(re:split("xcd","(?<!a(*FAIL)b)c",[trim]))), + 2}]))), + <<"xacd">> = iolist_to_binary(join(re:split("xacd","(?<=(a(*COMMIT)b))c",[]))), + <<"x:d">> = iolist_to_binary(join(re:split("xcd","(?<!a(*FAIL)b)c",[trim]))), <<"x:d">> = iolist_to_binary(join(re:split("xcd","(?<!a(*FAIL)b)c",[{parts, - 2}]))), - <<"x:d">> = iolist_to_binary(join(re:split("xcd","(?<!a(*FAIL)b)c",[]))), - <<"a:d">> = iolist_to_binary(join(re:split("acd","(?<!a(*FAIL)b)c",[trim]))), + 2}]))), + <<"x:d">> = iolist_to_binary(join(re:split("xcd","(?<!a(*FAIL)b)c",[]))), + <<"a:d">> = iolist_to_binary(join(re:split("acd","(?<!a(*FAIL)b)c",[trim]))), <<"a:d">> = iolist_to_binary(join(re:split("acd","(?<!a(*FAIL)b)c",[{parts, - 2}]))), - <<"a:d">> = iolist_to_binary(join(re:split("acd","(?<!a(*FAIL)b)c",[]))), - <<"xab:d">> = iolist_to_binary(join(re:split("xabcd","(?<=a(*PRUNE)b)c",[trim]))), + 2}]))), + <<"a:d">> = iolist_to_binary(join(re:split("acd","(?<!a(*FAIL)b)c",[]))), + <<"xab:d">> = iolist_to_binary(join(re:split("xabcd","(?<=a(*PRUNE)b)c",[trim]))), <<"xab:d">> = iolist_to_binary(join(re:split("xabcd","(?<=a(*PRUNE)b)c",[{parts, - 2}]))), - <<"xab:d">> = iolist_to_binary(join(re:split("xabcd","(?<=a(*PRUNE)b)c",[]))), - <<"xab:d">> = iolist_to_binary(join(re:split("xabcd","(?<=a(*SKIP)b)c",[trim]))), + 2}]))), + <<"xab:d">> = iolist_to_binary(join(re:split("xabcd","(?<=a(*PRUNE)b)c",[]))), + <<"xab:d">> = iolist_to_binary(join(re:split("xabcd","(?<=a(*SKIP)b)c",[trim]))), <<"xab:d">> = iolist_to_binary(join(re:split("xabcd","(?<=a(*SKIP)b)c",[{parts, - 2}]))), - <<"xab:d">> = iolist_to_binary(join(re:split("xabcd","(?<=a(*SKIP)b)c",[]))), - <<"xab:d">> = iolist_to_binary(join(re:split("xabcd","(?<=a(*THEN)b)c",[trim]))), + 2}]))), + <<"xab:d">> = iolist_to_binary(join(re:split("xabcd","(?<=a(*SKIP)b)c",[]))), + <<"xab:d">> = iolist_to_binary(join(re:split("xabcd","(?<=a(*THEN)b)c",[trim]))), <<"xab:d">> = iolist_to_binary(join(re:split("xabcd","(?<=a(*THEN)b)c",[{parts, - 2}]))), - <<"xab:d">> = iolist_to_binary(join(re:split("xabcd","(?<=a(*THEN)b)c",[]))), + 2}]))), + <<"xab:d">> = iolist_to_binary(join(re:split("xabcd","(?<=a(*THEN)b)c",[]))), ok. run49() -> - <<":a:d">> = iolist_to_binary(join(re:split("abcd","(a)(?2){2}(.)",[trim]))), + <<":a:d">> = iolist_to_binary(join(re:split("abcd","(a)(?2){2}(.)",[trim]))), <<":a:d:">> = iolist_to_binary(join(re:split("abcd","(a)(?2){2}(.)",[{parts, - 2}]))), - <<":a:d:">> = iolist_to_binary(join(re:split("abcd","(a)(?2){2}(.)",[]))), - <<"hello world ">> = iolist_to_binary(join(re:split("hello world test","(another)?(\\1?)test",[trim]))), + 2}]))), + <<":a:d:">> = iolist_to_binary(join(re:split("abcd","(a)(?2){2}(.)",[]))), + <<"hello world ">> = iolist_to_binary(join(re:split("hello world test","(another)?(\\1?)test",[trim]))), <<"hello world :::">> = iolist_to_binary(join(re:split("hello world test","(another)?(\\1?)test",[{parts, - 2}]))), - <<"hello world :::">> = iolist_to_binary(join(re:split("hello world test","(another)?(\\1?)test",[]))), - <<"hello world test">> = iolist_to_binary(join(re:split("hello world test","(another)?(\\1+)test",[trim]))), + 2}]))), + <<"hello world :::">> = iolist_to_binary(join(re:split("hello world test","(another)?(\\1?)test",[]))), + <<"hello world test">> = iolist_to_binary(join(re:split("hello world test","(another)?(\\1+)test",[trim]))), <<"hello world test">> = iolist_to_binary(join(re:split("hello world test","(another)?(\\1+)test",[{parts, - 2}]))), - <<"hello world test">> = iolist_to_binary(join(re:split("hello world test","(another)?(\\1+)test",[]))), - <<"">> = iolist_to_binary(join(re:split("aac","(a(*COMMIT)b){0}a(?1)|aac",[trim]))), + 2}]))), + <<"hello world test">> = iolist_to_binary(join(re:split("hello world test","(another)?(\\1+)test",[]))), + <<"">> = iolist_to_binary(join(re:split("aac","(a(*COMMIT)b){0}a(?1)|aac",[trim]))), <<"::">> = iolist_to_binary(join(re:split("aac","(a(*COMMIT)b){0}a(?1)|aac",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("aac","(a(*COMMIT)b){0}a(?1)|aac",[]))), - <<"">> = iolist_to_binary(join(re:split("aac","((?:a?)*)*c",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("aac","(a(*COMMIT)b){0}a(?1)|aac",[]))), + <<"">> = iolist_to_binary(join(re:split("aac","((?:a?)*)*c",[trim]))), <<"::">> = iolist_to_binary(join(re:split("aac","((?:a?)*)*c",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("aac","((?:a?)*)*c",[]))), - <<"">> = iolist_to_binary(join(re:split("aac","((?>a?)*)*c",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("aac","((?:a?)*)*c",[]))), + <<"">> = iolist_to_binary(join(re:split("aac","((?>a?)*)*c",[trim]))), <<"::">> = iolist_to_binary(join(re:split("aac","((?>a?)*)*c",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("aac","((?>a?)*)*c",[]))), - <<"a">> = iolist_to_binary(join(re:split("aba","(?>.*?a)(?<=ba)",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("aac","((?>a?)*)*c",[]))), + <<"a">> = iolist_to_binary(join(re:split("aba","(?>.*?a)(?<=ba)",[trim]))), <<"a:">> = iolist_to_binary(join(re:split("aba","(?>.*?a)(?<=ba)",[{parts, - 2}]))), - <<"a:">> = iolist_to_binary(join(re:split("aba","(?>.*?a)(?<=ba)",[]))), - <<"">> = iolist_to_binary(join(re:split("aba","(?:.*?a)(?<=ba)",[trim]))), + 2}]))), + <<"a:">> = iolist_to_binary(join(re:split("aba","(?>.*?a)(?<=ba)",[]))), + <<"">> = iolist_to_binary(join(re:split("aba","(?:.*?a)(?<=ba)",[trim]))), <<":">> = iolist_to_binary(join(re:split("aba","(?:.*?a)(?<=ba)",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aba","(?:.*?a)(?<=ba)",[]))), - <<"a">> = iolist_to_binary(join(re:split("aab",".*?a(*PRUNE)b",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aba","(?:.*?a)(?<=ba)",[]))), + <<"a">> = iolist_to_binary(join(re:split("aab",".*?a(*PRUNE)b",[trim]))), <<"a:">> = iolist_to_binary(join(re:split("aab",".*?a(*PRUNE)b",[{parts, - 2}]))), - <<"a:">> = iolist_to_binary(join(re:split("aab",".*?a(*PRUNE)b",[]))), + 2}]))), + <<"a:">> = iolist_to_binary(join(re:split("aab",".*?a(*PRUNE)b",[]))), <<"a">> = iolist_to_binary(join(re:split("aab",".*?a(*PRUNE)b",[dotall, - trim]))), + trim]))), <<"a:">> = iolist_to_binary(join(re:split("aab",".*?a(*PRUNE)b",[dotall, {parts, - 2}]))), - <<"a:">> = iolist_to_binary(join(re:split("aab",".*?a(*PRUNE)b",[dotall]))), + 2}]))), + <<"a:">> = iolist_to_binary(join(re:split("aab",".*?a(*PRUNE)b",[dotall]))), <<"aab">> = iolist_to_binary(join(re:split("aab","^a(*PRUNE)b",[dotall, - trim]))), + trim]))), <<"aab">> = iolist_to_binary(join(re:split("aab","^a(*PRUNE)b",[dotall, {parts, - 2}]))), - <<"aab">> = iolist_to_binary(join(re:split("aab","^a(*PRUNE)b",[dotall]))), - <<"a">> = iolist_to_binary(join(re:split("aab",".*?a(*SKIP)b",[trim]))), + 2}]))), + <<"aab">> = iolist_to_binary(join(re:split("aab","^a(*PRUNE)b",[dotall]))), + <<"a">> = iolist_to_binary(join(re:split("aab",".*?a(*SKIP)b",[trim]))), <<"a:">> = iolist_to_binary(join(re:split("aab",".*?a(*SKIP)b",[{parts, - 2}]))), - <<"a:">> = iolist_to_binary(join(re:split("aab",".*?a(*SKIP)b",[]))), + 2}]))), + <<"a:">> = iolist_to_binary(join(re:split("aab",".*?a(*SKIP)b",[]))), <<"a">> = iolist_to_binary(join(re:split("aab","(?>.*?a)b",[dotall, - trim]))), + trim]))), <<"a:">> = iolist_to_binary(join(re:split("aab","(?>.*?a)b",[dotall, {parts, - 2}]))), - <<"a:">> = iolist_to_binary(join(re:split("aab","(?>.*?a)b",[dotall]))), + 2}]))), + <<"a:">> = iolist_to_binary(join(re:split("aab","(?>.*?a)b",[dotall]))), ok. run50() -> - <<"a">> = iolist_to_binary(join(re:split("aab","(?>.*?a)b",[trim]))), + <<"a">> = iolist_to_binary(join(re:split("aab","(?>.*?a)b",[trim]))), <<"a:">> = iolist_to_binary(join(re:split("aab","(?>.*?a)b",[{parts, - 2}]))), - <<"a:">> = iolist_to_binary(join(re:split("aab","(?>.*?a)b",[]))), + 2}]))), + <<"a:">> = iolist_to_binary(join(re:split("aab","(?>.*?a)b",[]))), <<"aab">> = iolist_to_binary(join(re:split("aab","(?>^a)b",[dotall, - trim]))), + trim]))), <<"aab">> = iolist_to_binary(join(re:split("aab","(?>^a)b",[dotall, {parts, - 2}]))), - <<"aab">> = iolist_to_binary(join(re:split("aab","(?>^a)b",[dotall]))), - <<"alphabetabcd:abcd">> = iolist_to_binary(join(re:split("alphabetabcd","(?>.*?)(?<=(abcd)|(wxyz))",[trim]))), + 2}]))), + <<"aab">> = iolist_to_binary(join(re:split("aab","(?>^a)b",[dotall]))), + <<"alphabetabcd:abcd">> = iolist_to_binary(join(re:split("alphabetabcd","(?>.*?)(?<=(abcd)|(wxyz))",[trim]))), <<"alphabetabcd:abcd::">> = iolist_to_binary(join(re:split("alphabetabcd","(?>.*?)(?<=(abcd)|(wxyz))",[{parts, - 2}]))), - <<"alphabetabcd:abcd::">> = iolist_to_binary(join(re:split("alphabetabcd","(?>.*?)(?<=(abcd)|(wxyz))",[]))), - <<"endingwxyz::wxyz">> = iolist_to_binary(join(re:split("endingwxyz","(?>.*?)(?<=(abcd)|(wxyz))",[trim]))), + 2}]))), + <<"alphabetabcd:abcd::">> = iolist_to_binary(join(re:split("alphabetabcd","(?>.*?)(?<=(abcd)|(wxyz))",[]))), + <<"endingwxyz::wxyz">> = iolist_to_binary(join(re:split("endingwxyz","(?>.*?)(?<=(abcd)|(wxyz))",[trim]))), <<"endingwxyz::wxyz:">> = iolist_to_binary(join(re:split("endingwxyz","(?>.*?)(?<=(abcd)|(wxyz))",[{parts, - 2}]))), - <<"endingwxyz::wxyz:">> = iolist_to_binary(join(re:split("endingwxyz","(?>.*?)(?<=(abcd)|(wxyz))",[]))), - <<":abcd">> = iolist_to_binary(join(re:split("alphabetabcd","(?>.*)(?<=(abcd)|(wxyz))",[trim]))), + 2}]))), + <<"endingwxyz::wxyz:">> = iolist_to_binary(join(re:split("endingwxyz","(?>.*?)(?<=(abcd)|(wxyz))",[]))), + <<":abcd">> = iolist_to_binary(join(re:split("alphabetabcd","(?>.*)(?<=(abcd)|(wxyz))",[trim]))), <<":abcd::">> = iolist_to_binary(join(re:split("alphabetabcd","(?>.*)(?<=(abcd)|(wxyz))",[{parts, - 2}]))), - <<":abcd::">> = iolist_to_binary(join(re:split("alphabetabcd","(?>.*)(?<=(abcd)|(wxyz))",[]))), - <<"::wxyz">> = iolist_to_binary(join(re:split("endingwxyz","(?>.*)(?<=(abcd)|(wxyz))",[trim]))), + 2}]))), + <<":abcd::">> = iolist_to_binary(join(re:split("alphabetabcd","(?>.*)(?<=(abcd)|(wxyz))",[]))), + <<"::wxyz">> = iolist_to_binary(join(re:split("endingwxyz","(?>.*)(?<=(abcd)|(wxyz))",[trim]))), <<"::wxyz:">> = iolist_to_binary(join(re:split("endingwxyz","(?>.*)(?<=(abcd)|(wxyz))",[{parts, - 2}]))), - <<"::wxyz:">> = iolist_to_binary(join(re:split("endingwxyz","(?>.*)(?<=(abcd)|(wxyz))",[]))), - <<"abcdfooxyz">> = iolist_to_binary(join(re:split("abcdfooxyz","(?>.*)foo",[trim]))), + 2}]))), + <<"::wxyz:">> = iolist_to_binary(join(re:split("endingwxyz","(?>.*)(?<=(abcd)|(wxyz))",[]))), + <<"abcdfooxyz">> = iolist_to_binary(join(re:split("abcdfooxyz","(?>.*)foo",[trim]))), <<"abcdfooxyz">> = iolist_to_binary(join(re:split("abcdfooxyz","(?>.*)foo",[{parts, - 2}]))), - <<"abcdfooxyz">> = iolist_to_binary(join(re:split("abcdfooxyz","(?>.*)foo",[]))), - <<"abcd:xyz">> = iolist_to_binary(join(re:split("abcdfooxyz","(?>.*?)foo",[trim]))), + 2}]))), + <<"abcdfooxyz">> = iolist_to_binary(join(re:split("abcdfooxyz","(?>.*)foo",[]))), + <<"abcd:xyz">> = iolist_to_binary(join(re:split("abcdfooxyz","(?>.*?)foo",[trim]))), <<"abcd:xyz">> = iolist_to_binary(join(re:split("abcdfooxyz","(?>.*?)foo",[{parts, - 2}]))), - <<"abcd:xyz">> = iolist_to_binary(join(re:split("abcdfooxyz","(?>.*?)foo",[]))), - <<"">> = iolist_to_binary(join(re:split("ac","(?:(a(*PRUNE)b)){0}(?:(?1)|ac)",[trim]))), + 2}]))), + <<"abcd:xyz">> = iolist_to_binary(join(re:split("abcdfooxyz","(?>.*?)foo",[]))), + <<"">> = iolist_to_binary(join(re:split("ac","(?:(a(*PRUNE)b)){0}(?:(?1)|ac)",[trim]))), <<"::">> = iolist_to_binary(join(re:split("ac","(?:(a(*PRUNE)b)){0}(?:(?1)|ac)",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("ac","(?:(a(*PRUNE)b)){0}(?:(?1)|ac)",[]))), - <<"">> = iolist_to_binary(join(re:split("ac","(?:(a(*SKIP)b)){0}(?:(?1)|ac)",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("ac","(?:(a(*PRUNE)b)){0}(?:(?1)|ac)",[]))), + <<"">> = iolist_to_binary(join(re:split("ac","(?:(a(*SKIP)b)){0}(?:(?1)|ac)",[trim]))), <<"::">> = iolist_to_binary(join(re:split("ac","(?:(a(*SKIP)b)){0}(?:(?1)|ac)",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("ac","(?:(a(*SKIP)b)){0}(?:(?1)|ac)",[]))), - <<"aa">> = iolist_to_binary(join(re:split("aa","(?<=(*SKIP)ac)a",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("ac","(?:(a(*SKIP)b)){0}(?:(?1)|ac)",[]))), + <<"aa">> = iolist_to_binary(join(re:split("aa","(?<=(*SKIP)ac)a",[trim]))), <<"aa">> = iolist_to_binary(join(re:split("aa","(?<=(*SKIP)ac)a",[{parts, - 2}]))), - <<"aa">> = iolist_to_binary(join(re:split("aa","(?<=(*SKIP)ac)a",[]))), - <<"aa">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*PRUNE)b|a+c",[trim]))), + 2}]))), + <<"aa">> = iolist_to_binary(join(re:split("aa","(?<=(*SKIP)ac)a",[]))), + <<"aa">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*PRUNE)b|a+c",[trim]))), <<"aa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*PRUNE)b|a+c",[{parts, - 2}]))), - <<"aa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*PRUNE)b|a+c",[]))), - <<"aa">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*SKIP)(*PRUNE)b|a+c",[trim]))), + 2}]))), + <<"aa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*PRUNE)b|a+c",[]))), + <<"aa">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*SKIP)(*PRUNE)b|a+c",[trim]))), <<"aa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*SKIP)(*PRUNE)b|a+c",[{parts, - 2}]))), - <<"aa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*SKIP)(*PRUNE)b|a+c",[]))), - <<"aa">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*SKIP:N)(*PRUNE)b|a+c",[trim]))), + 2}]))), + <<"aa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*SKIP)(*PRUNE)b|a+c",[]))), + <<"aa">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*SKIP:N)(*PRUNE)b|a+c",[trim]))), <<"aa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*SKIP:N)(*PRUNE)b|a+c",[{parts, - 2}]))), - <<"aa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*SKIP:N)(*PRUNE)b|a+c",[]))), - <<"aa">> = iolist_to_binary(join(re:split("aaaaaac","aaaa(*:N)a(*SKIP:N)(*PRUNE)b|a+c",[trim]))), + 2}]))), + <<"aa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*SKIP:N)(*PRUNE)b|a+c",[]))), + <<"aa">> = iolist_to_binary(join(re:split("aaaaaac","aaaa(*:N)a(*SKIP:N)(*PRUNE)b|a+c",[trim]))), <<"aa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaa(*:N)a(*SKIP:N)(*PRUNE)b|a+c",[{parts, - 2}]))), - <<"aa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaa(*:N)a(*SKIP:N)(*PRUNE)b|a+c",[]))), - <<"aa">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*THEN)(*PRUNE)b|a+c",[trim]))), + 2}]))), + <<"aa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaa(*:N)a(*SKIP:N)(*PRUNE)b|a+c",[]))), + <<"aa">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*THEN)(*PRUNE)b|a+c",[trim]))), <<"aa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*THEN)(*PRUNE)b|a+c",[{parts, - 2}]))), - <<"aa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*THEN)(*PRUNE)b|a+c",[]))), + 2}]))), + <<"aa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*THEN)(*PRUNE)b|a+c",[]))), ok. run51() -> - <<"aaaaa">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*SKIP)b|a+c",[trim]))), + <<"aaaaa">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*SKIP)b|a+c",[trim]))), <<"aaaaa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*SKIP)b|a+c",[{parts, - 2}]))), - <<"aaaaa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*SKIP)b|a+c",[]))), - <<"aaaaa">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*PRUNE)(*SKIP)b|a+c",[trim]))), + 2}]))), + <<"aaaaa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*SKIP)b|a+c",[]))), + <<"aaaaa">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*PRUNE)(*SKIP)b|a+c",[trim]))), <<"aaaaa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*PRUNE)(*SKIP)b|a+c",[{parts, - 2}]))), - <<"aaaaa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*PRUNE)(*SKIP)b|a+c",[]))), - <<"aaaaa">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*THEN)(*SKIP)b|a+c",[trim]))), + 2}]))), + <<"aaaaa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*PRUNE)(*SKIP)b|a+c",[]))), + <<"aaaaa">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*THEN)(*SKIP)b|a+c",[trim]))), <<"aaaaa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*THEN)(*SKIP)b|a+c",[{parts, - 2}]))), - <<"aaaaa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*THEN)(*SKIP)b|a+c",[]))), - <<"aaaaa">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*COMMIT)(*SKIP)b|a+c",[trim]))), + 2}]))), + <<"aaaaa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*THEN)(*SKIP)b|a+c",[]))), + <<"aaaaa">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*COMMIT)(*SKIP)b|a+c",[trim]))), <<"aaaaa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*COMMIT)(*SKIP)b|a+c",[{parts, - 2}]))), - <<"aaaaa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*COMMIT)(*SKIP)b|a+c",[]))), - <<"aaaaaac">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*COMMIT)b|a+c",[trim]))), + 2}]))), + <<"aaaaa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*COMMIT)(*SKIP)b|a+c",[]))), + <<"aaaaaac">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*COMMIT)b|a+c",[trim]))), <<"aaaaaac">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*COMMIT)b|a+c",[{parts, - 2}]))), - <<"aaaaaac">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*COMMIT)b|a+c",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*THEN)b|a+c",[trim]))), + 2}]))), + <<"aaaaaac">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*COMMIT)b|a+c",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*THEN)b|a+c",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*THEN)b|a+c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*THEN)b|a+c",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*SKIP)(*THEN)b|a+c",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*THEN)b|a+c",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*SKIP)(*THEN)b|a+c",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*SKIP)(*THEN)b|a+c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*SKIP)(*THEN)b|a+c",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*PRUNE)(*THEN)b|a+c",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*SKIP)(*THEN)b|a+c",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*PRUNE)(*THEN)b|a+c",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*PRUNE)(*THEN)b|a+c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*PRUNE)(*THEN)b|a+c",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*COMMIT)(*THEN)b|a+c",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*PRUNE)(*THEN)b|a+c",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*COMMIT)(*THEN)b|a+c",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*COMMIT)(*THEN)b|a+c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*COMMIT)(*THEN)b|a+c",[]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*COMMIT)(*THEN)b|a+c",[]))), ok. run52() -> - <<"aaaaa">> = iolist_to_binary(join(re:split("aaaaaa","aaaaa(*:m)(*PRUNE:m)(*SKIP:m)m|a+",[trim]))), + <<"aaaaa">> = iolist_to_binary(join(re:split("aaaaaa","aaaaa(*:m)(*PRUNE:m)(*SKIP:m)m|a+",[trim]))), <<"aaaaa:">> = iolist_to_binary(join(re:split("aaaaaa","aaaaa(*:m)(*PRUNE:m)(*SKIP:m)m|a+",[{parts, - 2}]))), - <<"aaaaa:">> = iolist_to_binary(join(re:split("aaaaaa","aaaaa(*:m)(*PRUNE:m)(*SKIP:m)m|a+",[]))), - <<"aaaaa">> = iolist_to_binary(join(re:split("aaaaaa","aaaaa(*:m)(*MARK:m)(*PRUNE)(*SKIP:m)m|a+",[trim]))), + 2}]))), + <<"aaaaa:">> = iolist_to_binary(join(re:split("aaaaaa","aaaaa(*:m)(*PRUNE:m)(*SKIP:m)m|a+",[]))), + <<"aaaaa">> = iolist_to_binary(join(re:split("aaaaaa","aaaaa(*:m)(*MARK:m)(*PRUNE)(*SKIP:m)m|a+",[trim]))), <<"aaaaa:">> = iolist_to_binary(join(re:split("aaaaaa","aaaaa(*:m)(*MARK:m)(*PRUNE)(*SKIP:m)m|a+",[{parts, - 2}]))), - <<"aaaaa:">> = iolist_to_binary(join(re:split("aaaaaa","aaaaa(*:m)(*MARK:m)(*PRUNE)(*SKIP:m)m|a+",[]))), - <<"aa">> = iolist_to_binary(join(re:split("aaaaaa","aaaaa(*:n)(*PRUNE:m)(*SKIP:m)m|a+",[trim]))), + 2}]))), + <<"aaaaa:">> = iolist_to_binary(join(re:split("aaaaaa","aaaaa(*:m)(*MARK:m)(*PRUNE)(*SKIP:m)m|a+",[]))), + <<"aa">> = iolist_to_binary(join(re:split("aaaaaa","aaaaa(*:n)(*PRUNE:m)(*SKIP:m)m|a+",[trim]))), <<"aa:">> = iolist_to_binary(join(re:split("aaaaaa","aaaaa(*:n)(*PRUNE:m)(*SKIP:m)m|a+",[{parts, - 2}]))), - <<"aa:">> = iolist_to_binary(join(re:split("aaaaaa","aaaaa(*:n)(*PRUNE:m)(*SKIP:m)m|a+",[]))), - <<"aaaaa">> = iolist_to_binary(join(re:split("aaaaaa","aaaaa(*:n)(*MARK:m)(*PRUNE)(*SKIP:m)m|a+",[trim]))), + 2}]))), + <<"aa:">> = iolist_to_binary(join(re:split("aaaaaa","aaaaa(*:n)(*PRUNE:m)(*SKIP:m)m|a+",[]))), + <<"aaaaa">> = iolist_to_binary(join(re:split("aaaaaa","aaaaa(*:n)(*MARK:m)(*PRUNE)(*SKIP:m)m|a+",[trim]))), <<"aaaaa:">> = iolist_to_binary(join(re:split("aaaaaa","aaaaa(*:n)(*MARK:m)(*PRUNE)(*SKIP:m)m|a+",[{parts, - 2}]))), - <<"aaaaa:">> = iolist_to_binary(join(re:split("aaaaaa","aaaaa(*:n)(*MARK:m)(*PRUNE)(*SKIP:m)m|a+",[]))), - <<"aa">> = iolist_to_binary(join(re:split("aaaac","a(*MARK:A)aa(*PRUNE:A)a(*SKIP:A)b|a+c",[trim]))), + 2}]))), + <<"aaaaa:">> = iolist_to_binary(join(re:split("aaaaaa","aaaaa(*:n)(*MARK:m)(*PRUNE)(*SKIP:m)m|a+",[]))), + <<"aa">> = iolist_to_binary(join(re:split("aaaac","a(*MARK:A)aa(*PRUNE:A)a(*SKIP:A)b|a+c",[trim]))), <<"aa:">> = iolist_to_binary(join(re:split("aaaac","a(*MARK:A)aa(*PRUNE:A)a(*SKIP:A)b|a+c",[{parts, - 2}]))), - <<"aa:">> = iolist_to_binary(join(re:split("aaaac","a(*MARK:A)aa(*PRUNE:A)a(*SKIP:A)b|a+c",[]))), - <<"aaa">> = iolist_to_binary(join(re:split("aaaac","a(*MARK:A)aa(*MARK:A)a(*SKIP:A)b|a+c",[trim]))), + 2}]))), + <<"aa:">> = iolist_to_binary(join(re:split("aaaac","a(*MARK:A)aa(*PRUNE:A)a(*SKIP:A)b|a+c",[]))), + <<"aaa">> = iolist_to_binary(join(re:split("aaaac","a(*MARK:A)aa(*MARK:A)a(*SKIP:A)b|a+c",[trim]))), <<"aaa:">> = iolist_to_binary(join(re:split("aaaac","a(*MARK:A)aa(*MARK:A)a(*SKIP:A)b|a+c",[{parts, - 2}]))), - <<"aaa:">> = iolist_to_binary(join(re:split("aaaac","a(*MARK:A)aa(*MARK:A)a(*SKIP:A)b|a+c",[]))), - <<"aa">> = iolist_to_binary(join(re:split("aaaac","aaa(*PRUNE:A)a(*SKIP:A)b|a+c",[trim]))), + 2}]))), + <<"aaa:">> = iolist_to_binary(join(re:split("aaaac","a(*MARK:A)aa(*MARK:A)a(*SKIP:A)b|a+c",[]))), + <<"aa">> = iolist_to_binary(join(re:split("aaaac","aaa(*PRUNE:A)a(*SKIP:A)b|a+c",[trim]))), <<"aa:">> = iolist_to_binary(join(re:split("aaaac","aaa(*PRUNE:A)a(*SKIP:A)b|a+c",[{parts, - 2}]))), - <<"aa:">> = iolist_to_binary(join(re:split("aaaac","aaa(*PRUNE:A)a(*SKIP:A)b|a+c",[]))), - <<"aaa">> = iolist_to_binary(join(re:split("aaaac","aaa(*MARK:A)a(*SKIP:A)b|a+c",[trim]))), + 2}]))), + <<"aa:">> = iolist_to_binary(join(re:split("aaaac","aaa(*PRUNE:A)a(*SKIP:A)b|a+c",[]))), + <<"aaa">> = iolist_to_binary(join(re:split("aaaac","aaa(*MARK:A)a(*SKIP:A)b|a+c",[trim]))), <<"aaa:">> = iolist_to_binary(join(re:split("aaaac","aaa(*MARK:A)a(*SKIP:A)b|a+c",[{parts, - 2}]))), - <<"aaa:">> = iolist_to_binary(join(re:split("aaaac","aaa(*MARK:A)a(*SKIP:A)b|a+c",[]))), - <<":a">> = iolist_to_binary(join(re:split("ba",".?(a|b(*THEN)c)",[trim]))), + 2}]))), + <<"aaa:">> = iolist_to_binary(join(re:split("aaaac","aaa(*MARK:A)a(*SKIP:A)b|a+c",[]))), + <<":a">> = iolist_to_binary(join(re:split("ba",".?(a|b(*THEN)c)",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("ba",".?(a|b(*THEN)c)",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("ba",".?(a|b(*THEN)c)",[]))), - <<":ab">> = iolist_to_binary(join(re:split("abc","(a(*COMMIT)b)c|abd",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("ba",".?(a|b(*THEN)c)",[]))), + <<":ab">> = iolist_to_binary(join(re:split("abc","(a(*COMMIT)b)c|abd",[trim]))), <<":ab:">> = iolist_to_binary(join(re:split("abc","(a(*COMMIT)b)c|abd",[{parts, - 2}]))), - <<":ab:">> = iolist_to_binary(join(re:split("abc","(a(*COMMIT)b)c|abd",[]))), - <<"abd">> = iolist_to_binary(join(re:split("abd","(a(*COMMIT)b)c|abd",[trim]))), + 2}]))), + <<":ab:">> = iolist_to_binary(join(re:split("abc","(a(*COMMIT)b)c|abd",[]))), + <<"abd">> = iolist_to_binary(join(re:split("abd","(a(*COMMIT)b)c|abd",[trim]))), <<"abd">> = iolist_to_binary(join(re:split("abd","(a(*COMMIT)b)c|abd",[{parts, - 2}]))), - <<"abd">> = iolist_to_binary(join(re:split("abd","(a(*COMMIT)b)c|abd",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","(?=a(*COMMIT)b)abc|abd",[trim]))), + 2}]))), + <<"abd">> = iolist_to_binary(join(re:split("abd","(a(*COMMIT)b)c|abd",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","(?=a(*COMMIT)b)abc|abd",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","(?=a(*COMMIT)b)abc|abd",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","(?=a(*COMMIT)b)abc|abd",[]))), - <<"">> = iolist_to_binary(join(re:split("abd","(?=a(*COMMIT)b)abc|abd",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","(?=a(*COMMIT)b)abc|abd",[]))), + <<"">> = iolist_to_binary(join(re:split("abd","(?=a(*COMMIT)b)abc|abd",[trim]))), <<":">> = iolist_to_binary(join(re:split("abd","(?=a(*COMMIT)b)abc|abd",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abd","(?=a(*COMMIT)b)abc|abd",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","(?>a(*COMMIT)b)c|abd",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abd","(?=a(*COMMIT)b)abc|abd",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","(?>a(*COMMIT)b)c|abd",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","(?>a(*COMMIT)b)c|abd",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","(?>a(*COMMIT)b)c|abd",[]))), - <<"">> = iolist_to_binary(join(re:split("abd","(?>a(*COMMIT)b)c|abd",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","(?>a(*COMMIT)b)c|abd",[]))), + <<"">> = iolist_to_binary(join(re:split("abd","(?>a(*COMMIT)b)c|abd",[trim]))), <<":">> = iolist_to_binary(join(re:split("abd","(?>a(*COMMIT)b)c|abd",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abd","(?>a(*COMMIT)b)c|abd",[]))), - <<"abd">> = iolist_to_binary(join(re:split("abd","a(?=b(*COMMIT)c)[^d]|abd",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abd","(?>a(*COMMIT)b)c|abd",[]))), + <<"abd">> = iolist_to_binary(join(re:split("abd","a(?=b(*COMMIT)c)[^d]|abd",[trim]))), <<"abd">> = iolist_to_binary(join(re:split("abd","a(?=b(*COMMIT)c)[^d]|abd",[{parts, - 2}]))), - <<"abd">> = iolist_to_binary(join(re:split("abd","a(?=b(*COMMIT)c)[^d]|abd",[]))), - <<":c">> = iolist_to_binary(join(re:split("abc","a(?=b(*COMMIT)c)[^d]|abd",[trim]))), + 2}]))), + <<"abd">> = iolist_to_binary(join(re:split("abd","a(?=b(*COMMIT)c)[^d]|abd",[]))), + <<":c">> = iolist_to_binary(join(re:split("abc","a(?=b(*COMMIT)c)[^d]|abd",[trim]))), <<":c">> = iolist_to_binary(join(re:split("abc","a(?=b(*COMMIT)c)[^d]|abd",[{parts, - 2}]))), - <<":c">> = iolist_to_binary(join(re:split("abc","a(?=b(*COMMIT)c)[^d]|abd",[]))), - <<"">> = iolist_to_binary(join(re:split("abd","a(?=bc).|abd",[trim]))), + 2}]))), + <<":c">> = iolist_to_binary(join(re:split("abc","a(?=b(*COMMIT)c)[^d]|abd",[]))), + <<"">> = iolist_to_binary(join(re:split("abd","a(?=bc).|abd",[trim]))), <<":">> = iolist_to_binary(join(re:split("abd","a(?=bc).|abd",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abd","a(?=bc).|abd",[]))), - <<":c">> = iolist_to_binary(join(re:split("abc","a(?=bc).|abd",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abd","a(?=bc).|abd",[]))), + <<":c">> = iolist_to_binary(join(re:split("abc","a(?=bc).|abd",[trim]))), <<":c">> = iolist_to_binary(join(re:split("abc","a(?=bc).|abd",[{parts, - 2}]))), - <<":c">> = iolist_to_binary(join(re:split("abc","a(?=bc).|abd",[]))), - <<"abceabd">> = iolist_to_binary(join(re:split("abceabd","a(?>b(*COMMIT)c)d|abd",[trim]))), + 2}]))), + <<":c">> = iolist_to_binary(join(re:split("abc","a(?=bc).|abd",[]))), + <<"abceabd">> = iolist_to_binary(join(re:split("abceabd","a(?>b(*COMMIT)c)d|abd",[trim]))), <<"abceabd">> = iolist_to_binary(join(re:split("abceabd","a(?>b(*COMMIT)c)d|abd",[{parts, - 2}]))), - <<"abceabd">> = iolist_to_binary(join(re:split("abceabd","a(?>b(*COMMIT)c)d|abd",[]))), - <<"abce">> = iolist_to_binary(join(re:split("abceabd","a(?>bc)d|abd",[trim]))), + 2}]))), + <<"abceabd">> = iolist_to_binary(join(re:split("abceabd","a(?>b(*COMMIT)c)d|abd",[]))), + <<"abce">> = iolist_to_binary(join(re:split("abceabd","a(?>bc)d|abd",[trim]))), <<"abce:">> = iolist_to_binary(join(re:split("abceabd","a(?>bc)d|abd",[{parts, - 2}]))), - <<"abce:">> = iolist_to_binary(join(re:split("abceabd","a(?>bc)d|abd",[]))), - <<"">> = iolist_to_binary(join(re:split("abd","(?>a(*COMMIT)b)c|abd",[trim]))), + 2}]))), + <<"abce:">> = iolist_to_binary(join(re:split("abceabd","a(?>bc)d|abd",[]))), + <<"">> = iolist_to_binary(join(re:split("abd","(?>a(*COMMIT)b)c|abd",[trim]))), <<":">> = iolist_to_binary(join(re:split("abd","(?>a(*COMMIT)b)c|abd",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abd","(?>a(*COMMIT)b)c|abd",[]))), - <<"abd">> = iolist_to_binary(join(re:split("abd","(?>a(*COMMIT)c)d|abd",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abd","(?>a(*COMMIT)b)c|abd",[]))), + <<"abd">> = iolist_to_binary(join(re:split("abd","(?>a(*COMMIT)c)d|abd",[trim]))), <<"abd">> = iolist_to_binary(join(re:split("abd","(?>a(*COMMIT)c)d|abd",[{parts, - 2}]))), - <<"abd">> = iolist_to_binary(join(re:split("abd","(?>a(*COMMIT)c)d|abd",[]))), - <<"::c">> = iolist_to_binary(join(re:split("ac","((?=a(*COMMIT)b)ab|ac){0}(?:(?1)|a(c))",[trim]))), + 2}]))), + <<"abd">> = iolist_to_binary(join(re:split("abd","(?>a(*COMMIT)c)d|abd",[]))), + <<"::c">> = iolist_to_binary(join(re:split("ac","((?=a(*COMMIT)b)ab|ac){0}(?:(?1)|a(c))",[trim]))), <<"::c:">> = iolist_to_binary(join(re:split("ac","((?=a(*COMMIT)b)ab|ac){0}(?:(?1)|a(c))",[{parts, - 2}]))), - <<"::c:">> = iolist_to_binary(join(re:split("ac","((?=a(*COMMIT)b)ab|ac){0}(?:(?1)|a(c))",[]))), + 2}]))), + <<"::c:">> = iolist_to_binary(join(re:split("ac","((?=a(*COMMIT)b)ab|ac){0}(?:(?1)|a(c))",[]))), ok. run53() -> - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a)?(?(1)a|b)+$",[trim]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a)?(?(1)a|b)+$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a)?(?(1)a|b)+$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a)?(?(1)a|b)+$",[]))), - <<"a">> = iolist_to_binary(join(re:split("a","^(a)?(?(1)a|b)+$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a)?(?(1)a|b)+$",[]))), + <<"a">> = iolist_to_binary(join(re:split("a","^(a)?(?(1)a|b)+$",[trim]))), <<"a">> = iolist_to_binary(join(re:split("a","^(a)?(?(1)a|b)+$",[{parts, - 2}]))), - <<"a">> = iolist_to_binary(join(re:split("a","^(a)?(?(1)a|b)+$",[]))), - <<"a">> = iolist_to_binary(join(re:split("ab","(?=a\\Kb)ab",[trim]))), + 2}]))), + <<"a">> = iolist_to_binary(join(re:split("a","^(a)?(?(1)a|b)+$",[]))), + <<"a">> = iolist_to_binary(join(re:split("ab","(?=a\\Kb)ab",[trim]))), <<"a:">> = iolist_to_binary(join(re:split("ab","(?=a\\Kb)ab",[{parts, - 2}]))), - <<"a:">> = iolist_to_binary(join(re:split("ab","(?=a\\Kb)ab",[]))), - <<"">> = iolist_to_binary(join(re:split("ac","(?!a\\Kb)ac",[trim]))), + 2}]))), + <<"a:">> = iolist_to_binary(join(re:split("ab","(?=a\\Kb)ab",[]))), + <<"">> = iolist_to_binary(join(re:split("ac","(?!a\\Kb)ac",[trim]))), <<":">> = iolist_to_binary(join(re:split("ac","(?!a\\Kb)ac",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ac","(?!a\\Kb)ac",[]))), - <<"ab">> = iolist_to_binary(join(re:split("abcd","^abc(?<=b\\Kc)d",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ac","(?!a\\Kb)ac",[]))), + <<"ab">> = iolist_to_binary(join(re:split("abcd","^abc(?<=b\\Kc)d",[trim]))), <<"ab:">> = iolist_to_binary(join(re:split("abcd","^abc(?<=b\\Kc)d",[{parts, - 2}]))), - <<"ab:">> = iolist_to_binary(join(re:split("abcd","^abc(?<=b\\Kc)d",[]))), - <<"">> = iolist_to_binary(join(re:split("abcd","^abc(?<!b\\Kq)d",[trim]))), + 2}]))), + <<"ab:">> = iolist_to_binary(join(re:split("abcd","^abc(?<=b\\Kc)d",[]))), + <<"">> = iolist_to_binary(join(re:split("abcd","^abc(?<!b\\Kq)d",[trim]))), <<":">> = iolist_to_binary(join(re:split("abcd","^abc(?<!b\\Kq)d",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abcd","^abc(?<!b\\Kq)d",[]))), - <<":abcd">> = iolist_to_binary(join(re:split("abcd","^((abc|abcx)(*THEN)y|abcd)",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abcd","^abc(?<!b\\Kq)d",[]))), + <<":abcd">> = iolist_to_binary(join(re:split("abcd","^((abc|abcx)(*THEN)y|abcd)",[trim]))), <<":abcd::">> = iolist_to_binary(join(re:split("abcd","^((abc|abcx)(*THEN)y|abcd)",[{parts, - 2}]))), - <<":abcd::">> = iolist_to_binary(join(re:split("abcd","^((abc|abcx)(*THEN)y|abcd)",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^((abc|abcx)(*THEN)y|abcd)",[trim]))), + 2}]))), + <<":abcd::">> = iolist_to_binary(join(re:split("abcd","^((abc|abcx)(*THEN)y|abcd)",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^((abc|abcx)(*THEN)y|abcd)",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^((abc|abcx)(*THEN)y|abcd)",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^((abc|abcx)(*THEN)y|abcd)",[]))), - <<"abcxy">> = iolist_to_binary(join(re:split("abcxy","^((abc|abcx)(*THEN)y|abcd)",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^((abc|abcx)(*THEN)y|abcd)",[]))), + <<"abcxy">> = iolist_to_binary(join(re:split("abcxy","^((abc|abcx)(*THEN)y|abcd)",[trim]))), <<"abcxy">> = iolist_to_binary(join(re:split("abcxy","^((abc|abcx)(*THEN)y|abcd)",[{parts, - 2}]))), - <<"abcxy">> = iolist_to_binary(join(re:split("abcxy","^((abc|abcx)(*THEN)y|abcd)",[]))), - <<"yes">> = iolist_to_binary(join(re:split("yes","^((yes|no)(*THEN)(*F))?",[trim]))), + 2}]))), + <<"abcxy">> = iolist_to_binary(join(re:split("abcxy","^((abc|abcx)(*THEN)y|abcd)",[]))), + <<"yes">> = iolist_to_binary(join(re:split("yes","^((yes|no)(*THEN)(*F))?",[trim]))), <<"yes">> = iolist_to_binary(join(re:split("yes","^((yes|no)(*THEN)(*F))?",[{parts, - 2}]))), - <<"yes">> = iolist_to_binary(join(re:split("yes","^((yes|no)(*THEN)(*F))?",[]))), - <<"ac">> = iolist_to_binary(join(re:split("ac","(?=a(*COMMIT)b|ac)ac|ac",[trim]))), + 2}]))), + <<"yes">> = iolist_to_binary(join(re:split("yes","^((yes|no)(*THEN)(*F))?",[]))), + <<"ac">> = iolist_to_binary(join(re:split("ac","(?=a(*COMMIT)b|ac)ac|ac",[trim]))), <<"ac">> = iolist_to_binary(join(re:split("ac","(?=a(*COMMIT)b|ac)ac|ac",[{parts, - 2}]))), - <<"ac">> = iolist_to_binary(join(re:split("ac","(?=a(*COMMIT)b|ac)ac|ac",[]))), + 2}]))), + <<"ac">> = iolist_to_binary(join(re:split("ac","(?=a(*COMMIT)b|ac)ac|ac",[]))), <<"ac">> = iolist_to_binary(join(re:split("ac","(?=a(*COMMIT)b|(ac)) ac | (a)c",[extended, - trim]))), + trim]))), <<"ac">> = iolist_to_binary(join(re:split("ac","(?=a(*COMMIT)b|(ac)) ac | (a)c",[extended, {parts, - 2}]))), - <<"ac">> = iolist_to_binary(join(re:split("ac","(?=a(*COMMIT)b|(ac)) ac | (a)c",[extended]))), - <<":n">> = iolist_to_binary(join(re:split("bnn","(?(?!b(*THEN)a)bn|bnn)",[trim]))), + 2}]))), + <<"ac">> = iolist_to_binary(join(re:split("ac","(?=a(*COMMIT)b|(ac)) ac | (a)c",[extended]))), + <<":n">> = iolist_to_binary(join(re:split("bnn","(?(?!b(*THEN)a)bn|bnn)",[trim]))), <<":n">> = iolist_to_binary(join(re:split("bnn","(?(?!b(*THEN)a)bn|bnn)",[{parts, - 2}]))), - <<":n">> = iolist_to_binary(join(re:split("bnn","(?(?!b(*THEN)a)bn|bnn)",[]))), - <<":n">> = iolist_to_binary(join(re:split("bnn","(?!b(*SKIP)a)bn|bnn",[trim]))), + 2}]))), + <<":n">> = iolist_to_binary(join(re:split("bnn","(?(?!b(*THEN)a)bn|bnn)",[]))), + <<":n">> = iolist_to_binary(join(re:split("bnn","(?!b(*SKIP)a)bn|bnn",[trim]))), <<":n">> = iolist_to_binary(join(re:split("bnn","(?!b(*SKIP)a)bn|bnn",[{parts, - 2}]))), - <<":n">> = iolist_to_binary(join(re:split("bnn","(?!b(*SKIP)a)bn|bnn",[]))), - <<":n">> = iolist_to_binary(join(re:split("bnn","(?(?!b(*SKIP)a)bn|bnn)",[trim]))), + 2}]))), + <<":n">> = iolist_to_binary(join(re:split("bnn","(?!b(*SKIP)a)bn|bnn",[]))), + <<":n">> = iolist_to_binary(join(re:split("bnn","(?(?!b(*SKIP)a)bn|bnn)",[trim]))), <<":n">> = iolist_to_binary(join(re:split("bnn","(?(?!b(*SKIP)a)bn|bnn)",[{parts, - 2}]))), - <<":n">> = iolist_to_binary(join(re:split("bnn","(?(?!b(*SKIP)a)bn|bnn)",[]))), - <<":n">> = iolist_to_binary(join(re:split("bnn","(?!b(*PRUNE)a)bn|bnn",[trim]))), + 2}]))), + <<":n">> = iolist_to_binary(join(re:split("bnn","(?(?!b(*SKIP)a)bn|bnn)",[]))), + <<":n">> = iolist_to_binary(join(re:split("bnn","(?!b(*PRUNE)a)bn|bnn",[trim]))), <<":n">> = iolist_to_binary(join(re:split("bnn","(?!b(*PRUNE)a)bn|bnn",[{parts, - 2}]))), - <<":n">> = iolist_to_binary(join(re:split("bnn","(?!b(*PRUNE)a)bn|bnn",[]))), - <<":n">> = iolist_to_binary(join(re:split("bnn","(?(?!b(*PRUNE)a)bn|bnn)",[trim]))), + 2}]))), + <<":n">> = iolist_to_binary(join(re:split("bnn","(?!b(*PRUNE)a)bn|bnn",[]))), + <<":n">> = iolist_to_binary(join(re:split("bnn","(?(?!b(*PRUNE)a)bn|bnn)",[trim]))), <<":n">> = iolist_to_binary(join(re:split("bnn","(?(?!b(*PRUNE)a)bn|bnn)",[{parts, - 2}]))), - <<":n">> = iolist_to_binary(join(re:split("bnn","(?(?!b(*PRUNE)a)bn|bnn)",[]))), - <<":n">> = iolist_to_binary(join(re:split("bnn","(?!b(*COMMIT)a)bn|bnn",[trim]))), + 2}]))), + <<":n">> = iolist_to_binary(join(re:split("bnn","(?(?!b(*PRUNE)a)bn|bnn)",[]))), + <<":n">> = iolist_to_binary(join(re:split("bnn","(?!b(*COMMIT)a)bn|bnn",[trim]))), <<":n">> = iolist_to_binary(join(re:split("bnn","(?!b(*COMMIT)a)bn|bnn",[{parts, - 2}]))), - <<":n">> = iolist_to_binary(join(re:split("bnn","(?!b(*COMMIT)a)bn|bnn",[]))), - <<":n">> = iolist_to_binary(join(re:split("bnn","(?(?!b(*COMMIT)a)bn|bnn)",[trim]))), + 2}]))), + <<":n">> = iolist_to_binary(join(re:split("bnn","(?!b(*COMMIT)a)bn|bnn",[]))), + <<":n">> = iolist_to_binary(join(re:split("bnn","(?(?!b(*COMMIT)a)bn|bnn)",[trim]))), <<":n">> = iolist_to_binary(join(re:split("bnn","(?(?!b(*COMMIT)a)bn|bnn)",[{parts, - 2}]))), - <<":n">> = iolist_to_binary(join(re:split("bnn","(?(?!b(*COMMIT)a)bn|bnn)",[]))), - <<"bnn">> = iolist_to_binary(join(re:split("bnn","(?=b(*SKIP)a)bn|bnn",[trim]))), + 2}]))), + <<":n">> = iolist_to_binary(join(re:split("bnn","(?(?!b(*COMMIT)a)bn|bnn)",[]))), + <<"bnn">> = iolist_to_binary(join(re:split("bnn","(?=b(*SKIP)a)bn|bnn",[trim]))), <<"bnn">> = iolist_to_binary(join(re:split("bnn","(?=b(*SKIP)a)bn|bnn",[{parts, - 2}]))), - <<"bnn">> = iolist_to_binary(join(re:split("bnn","(?=b(*SKIP)a)bn|bnn",[]))), - <<"">> = iolist_to_binary(join(re:split("bnn","(?=b(*THEN)a)bn|bnn",[trim]))), + 2}]))), + <<"bnn">> = iolist_to_binary(join(re:split("bnn","(?=b(*SKIP)a)bn|bnn",[]))), + <<"">> = iolist_to_binary(join(re:split("bnn","(?=b(*THEN)a)bn|bnn",[trim]))), <<":">> = iolist_to_binary(join(re:split("bnn","(?=b(*THEN)a)bn|bnn",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("bnn","(?=b(*THEN)a)bn|bnn",[]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("bnn","(?=b(*THEN)a)bn|bnn",[]))), ok. run54() -> - <<":d">> = iolist_to_binary(join(re:split("acd","(?!a(*SKIP)b)..",[trim]))), + <<":d">> = iolist_to_binary(join(re:split("acd","(?!a(*SKIP)b)..",[trim]))), <<":d">> = iolist_to_binary(join(re:split("acd","(?!a(*SKIP)b)..",[{parts, - 2}]))), - <<":d">> = iolist_to_binary(join(re:split("acd","(?!a(*SKIP)b)..",[]))), - <<"ac">> = iolist_to_binary(join(re:split("ac","^(?(?!a(*SKIP)b))",[trim]))), + 2}]))), + <<":d">> = iolist_to_binary(join(re:split("acd","(?!a(*SKIP)b)..",[]))), + <<"ac">> = iolist_to_binary(join(re:split("ac","^(?(?!a(*SKIP)b))",[trim]))), <<"ac">> = iolist_to_binary(join(re:split("ac","^(?(?!a(*SKIP)b))",[{parts, - 2}]))), - <<"ac">> = iolist_to_binary(join(re:split("ac","^(?(?!a(*SKIP)b))",[]))), - <<":d">> = iolist_to_binary(join(re:split("acd","^(?!a(*PRUNE)b)..",[trim]))), + 2}]))), + <<"ac">> = iolist_to_binary(join(re:split("ac","^(?(?!a(*SKIP)b))",[]))), + <<":d">> = iolist_to_binary(join(re:split("acd","^(?!a(*PRUNE)b)..",[trim]))), <<":d">> = iolist_to_binary(join(re:split("acd","^(?!a(*PRUNE)b)..",[{parts, - 2}]))), - <<":d">> = iolist_to_binary(join(re:split("acd","^(?!a(*PRUNE)b)..",[]))), - <<":d">> = iolist_to_binary(join(re:split("acd","(?!a(*PRUNE)b)..",[trim]))), + 2}]))), + <<":d">> = iolist_to_binary(join(re:split("acd","^(?!a(*PRUNE)b)..",[]))), + <<":d">> = iolist_to_binary(join(re:split("acd","(?!a(*PRUNE)b)..",[trim]))), <<":d">> = iolist_to_binary(join(re:split("acd","(?!a(*PRUNE)b)..",[{parts, - 2}]))), - <<":d">> = iolist_to_binary(join(re:split("acd","(?!a(*PRUNE)b)..",[]))), - <<"">> = iolist_to_binary(join(re:split("ba","\\A.*?(?:a|bc)",[trim]))), + 2}]))), + <<":d">> = iolist_to_binary(join(re:split("acd","(?!a(*PRUNE)b)..",[]))), + <<"">> = iolist_to_binary(join(re:split("ba","\\A.*?(?:a|bc)",[trim]))), <<":">> = iolist_to_binary(join(re:split("ba","\\A.*?(?:a|bc)",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ba","\\A.*?(?:a|bc)",[]))), - <<":CD">> = iolist_to_binary(join(re:split("CD","^(A(*THEN)B|C(*THEN)D)",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ba","\\A.*?(?:a|bc)",[]))), + <<":CD">> = iolist_to_binary(join(re:split("CD","^(A(*THEN)B|C(*THEN)D)",[trim]))), <<":CD:">> = iolist_to_binary(join(re:split("CD","^(A(*THEN)B|C(*THEN)D)",[{parts, - 2}]))), - <<":CD:">> = iolist_to_binary(join(re:split("CD","^(A(*THEN)B|C(*THEN)D)",[]))), - <<"">> = iolist_to_binary(join(re:split("1234","^\\d*\\w{4}",[trim]))), + 2}]))), + <<":CD:">> = iolist_to_binary(join(re:split("CD","^(A(*THEN)B|C(*THEN)D)",[]))), + <<"">> = iolist_to_binary(join(re:split("1234","^\\d*\\w{4}",[trim]))), <<":">> = iolist_to_binary(join(re:split("1234","^\\d*\\w{4}",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("1234","^\\d*\\w{4}",[]))), - <<"123">> = iolist_to_binary(join(re:split("123","^\\d*\\w{4}",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("1234","^\\d*\\w{4}",[]))), + <<"123">> = iolist_to_binary(join(re:split("123","^\\d*\\w{4}",[trim]))), <<"123">> = iolist_to_binary(join(re:split("123","^\\d*\\w{4}",[{parts, - 2}]))), - <<"123">> = iolist_to_binary(join(re:split("123","^\\d*\\w{4}",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaa","^[^b]*\\w{4}",[trim]))), + 2}]))), + <<"123">> = iolist_to_binary(join(re:split("123","^\\d*\\w{4}",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaa","^[^b]*\\w{4}",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaa","^[^b]*\\w{4}",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaa","^[^b]*\\w{4}",[]))), - <<"aaa">> = iolist_to_binary(join(re:split("aaa","^[^b]*\\w{4}",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaa","^[^b]*\\w{4}",[]))), + <<"aaa">> = iolist_to_binary(join(re:split("aaa","^[^b]*\\w{4}",[trim]))), <<"aaa">> = iolist_to_binary(join(re:split("aaa","^[^b]*\\w{4}",[{parts, - 2}]))), - <<"aaa">> = iolist_to_binary(join(re:split("aaa","^[^b]*\\w{4}",[]))), + 2}]))), + <<"aaa">> = iolist_to_binary(join(re:split("aaa","^[^b]*\\w{4}",[]))), <<"">> = iolist_to_binary(join(re:split("aaaa","^[^b]*\\w{4}",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("aaaa","^[^b]*\\w{4}",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaa","^[^b]*\\w{4}",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaa","^[^b]*\\w{4}",[caseless]))), <<"aaa">> = iolist_to_binary(join(re:split("aaa","^[^b]*\\w{4}",[caseless, - trim]))), + trim]))), <<"aaa">> = iolist_to_binary(join(re:split("aaa","^[^b]*\\w{4}",[caseless, {parts, - 2}]))), - <<"aaa">> = iolist_to_binary(join(re:split("aaa","^[^b]*\\w{4}",[caseless]))), - <<"">> = iolist_to_binary(join(re:split("aaaa","^a*\\w{4}",[trim]))), + 2}]))), + <<"aaa">> = iolist_to_binary(join(re:split("aaa","^[^b]*\\w{4}",[caseless]))), + <<"">> = iolist_to_binary(join(re:split("aaaa","^a*\\w{4}",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaa","^a*\\w{4}",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaa","^a*\\w{4}",[]))), - <<"aaa">> = iolist_to_binary(join(re:split("aaa","^a*\\w{4}",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaa","^a*\\w{4}",[]))), + <<"aaa">> = iolist_to_binary(join(re:split("aaa","^a*\\w{4}",[trim]))), <<"aaa">> = iolist_to_binary(join(re:split("aaa","^a*\\w{4}",[{parts, - 2}]))), - <<"aaa">> = iolist_to_binary(join(re:split("aaa","^a*\\w{4}",[]))), + 2}]))), + <<"aaa">> = iolist_to_binary(join(re:split("aaa","^a*\\w{4}",[]))), <<"">> = iolist_to_binary(join(re:split("aaaa","^a*\\w{4}",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("aaaa","^a*\\w{4}",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaa","^a*\\w{4}",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaa","^a*\\w{4}",[caseless]))), <<"aaa">> = iolist_to_binary(join(re:split("aaa","^a*\\w{4}",[caseless, - trim]))), + trim]))), <<"aaa">> = iolist_to_binary(join(re:split("aaa","^a*\\w{4}",[caseless, {parts, - 2}]))), - <<"aaa">> = iolist_to_binary(join(re:split("aaa","^a*\\w{4}",[caseless]))), - <<":1:non-sp1:non-sp2">> = iolist_to_binary(join(re:split("1 IN SOA non-sp1 non-sp2(","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[trim]))), + 2}]))), + <<"aaa">> = iolist_to_binary(join(re:split("aaa","^a*\\w{4}",[caseless]))), + <<":1:non-sp1:non-sp2">> = iolist_to_binary(join(re:split("1 IN SOA non-sp1 non-sp2(","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[trim]))), <<":1:non-sp1:non-sp2:">> = iolist_to_binary(join(re:split("1 IN SOA non-sp1 non-sp2(","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[{parts, - 2}]))), - <<":1:non-sp1:non-sp2:">> = iolist_to_binary(join(re:split("1 IN SOA non-sp1 non-sp2(","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[]))), - <<"AZ">> = iolist_to_binary(join(re:split("AZ","^A\\xZ",[trim]))), + 2}]))), + <<":1:non-sp1:non-sp2:">> = iolist_to_binary(join(re:split("1 IN SOA non-sp1 non-sp2(","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[]))), + <<"AZ">> = iolist_to_binary(join(re:split("AZ","^A\\xZ",[trim]))), <<"AZ">> = iolist_to_binary(join(re:split("AZ","^A\\xZ",[{parts, - 2}]))), - <<"AZ">> = iolist_to_binary(join(re:split("AZ","^A\\xZ",[]))), - <<"">> = iolist_to_binary(join(re:split("ASB","^A\\o{123}B",[trim]))), + 2}]))), + <<"AZ">> = iolist_to_binary(join(re:split("AZ","^A\\xZ",[]))), + <<"">> = iolist_to_binary(join(re:split("ASB","^A\\o{123}B",[trim]))), <<":">> = iolist_to_binary(join(re:split("ASB","^A\\o{123}B",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ASB","^A\\o{123}B",[]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ASB","^A\\o{123}B",[]))), <<"">> = iolist_to_binary(join(re:split("aaaab"," ^ a + + b $ ",[extended, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("aaaab"," ^ a + + b $ ",[extended, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaab"," ^ a + + b $ ",[extended]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaab"," ^ a + + b $ ",[extended]))), <<"">> = iolist_to_binary(join(re:split("aaaab"," ^ a + #comment - + b $ ",[extended,trim]))), + + b $ ",[extended,trim]))), <<":">> = iolist_to_binary(join(re:split("aaaab"," ^ a + #comment - + b $ ",[extended,{parts,2}]))), + + b $ ",[extended,{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("aaaab"," ^ a + #comment - + b $ ",[extended]))), + + b $ ",[extended]))), <<"">> = iolist_to_binary(join(re:split("aaaab"," ^ a + #comment #comment - + b $ ",[extended,trim]))), + + b $ ",[extended,trim]))), <<":">> = iolist_to_binary(join(re:split("aaaab"," ^ a + #comment #comment - + b $ ",[extended,{parts,2}]))), + + b $ ",[extended,{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("aaaab"," ^ a + #comment #comment - + b $ ",[extended]))), + + b $ ",[extended]))), ok. run55() -> <<"">> = iolist_to_binary(join(re:split("aaaab"," ^ (?> a + ) b $ ",[extended, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("aaaab"," ^ (?> a + ) b $ ",[extended, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaab"," ^ (?> a + ) b $ ",[extended]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaab"," ^ (?> a + ) b $ ",[extended]))), <<":aaaa">> = iolist_to_binary(join(re:split("aaaab"," ^ ( a + ) + + \\w $ ",[extended, - trim]))), + trim]))), <<":aaaa:">> = iolist_to_binary(join(re:split("aaaab"," ^ ( a + ) + + \\w $ ",[extended, {parts, - 2}]))), - <<":aaaa:">> = iolist_to_binary(join(re:split("aaaab"," ^ ( a + ) + + \\w $ ",[extended]))), - <<"acb">> = iolist_to_binary(join(re:split("acb","(?:x|(?:(xx|yy)+|x|x|x|x|x)|a|a|a)bc",[trim]))), + 2}]))), + <<":aaaa:">> = iolist_to_binary(join(re:split("aaaab"," ^ ( a + ) + + \\w $ ",[extended]))), + <<"acb">> = iolist_to_binary(join(re:split("acb","(?:x|(?:(xx|yy)+|x|x|x|x|x)|a|a|a)bc",[trim]))), <<"acb">> = iolist_to_binary(join(re:split("acb","(?:x|(?:(xx|yy)+|x|x|x|x|x)|a|a|a)bc",[{parts, - 2}]))), - <<"acb">> = iolist_to_binary(join(re:split("acb","(?:x|(?:(xx|yy)+|x|x|x|x|x)|a|a|a)bc",[]))), - <<":\"NOT MATCHED">> = iolist_to_binary(join(re:split("NON QUOTED \"QUOT\"\"ED\" AFTER \"NOT MATCHED","\\A(?:[^\\\"]++|\\\"(?:[^\\\"]*+|\\\"\\\")*+\\\")++",[trim]))), + 2}]))), + <<"acb">> = iolist_to_binary(join(re:split("acb","(?:x|(?:(xx|yy)+|x|x|x|x|x)|a|a|a)bc",[]))), + <<":\"NOT MATCHED">> = iolist_to_binary(join(re:split("NON QUOTED \"QUOT\"\"ED\" AFTER \"NOT MATCHED","\\A(?:[^\\\"]++|\\\"(?:[^\\\"]*+|\\\"\\\")*+\\\")++",[trim]))), <<":\"NOT MATCHED">> = iolist_to_binary(join(re:split("NON QUOTED \"QUOT\"\"ED\" AFTER \"NOT MATCHED","\\A(?:[^\\\"]++|\\\"(?:[^\\\"]*+|\\\"\\\")*+\\\")++",[{parts, - 2}]))), - <<":\"NOT MATCHED">> = iolist_to_binary(join(re:split("NON QUOTED \"QUOT\"\"ED\" AFTER \"NOT MATCHED","\\A(?:[^\\\"]++|\\\"(?:[^\\\"]*+|\\\"\\\")*+\\\")++",[]))), - <<":\"NOT MATCHED">> = iolist_to_binary(join(re:split("NON QUOTED \"QUOT\"\"ED\" AFTER \"NOT MATCHED","\\A(?:[^\\\"]++|\\\"(?:[^\\\"]++|\\\"\\\")*+\\\")++",[trim]))), + 2}]))), + <<":\"NOT MATCHED">> = iolist_to_binary(join(re:split("NON QUOTED \"QUOT\"\"ED\" AFTER \"NOT MATCHED","\\A(?:[^\\\"]++|\\\"(?:[^\\\"]*+|\\\"\\\")*+\\\")++",[]))), + <<":\"NOT MATCHED">> = iolist_to_binary(join(re:split("NON QUOTED \"QUOT\"\"ED\" AFTER \"NOT MATCHED","\\A(?:[^\\\"]++|\\\"(?:[^\\\"]++|\\\"\\\")*+\\\")++",[trim]))), <<":\"NOT MATCHED">> = iolist_to_binary(join(re:split("NON QUOTED \"QUOT\"\"ED\" AFTER \"NOT MATCHED","\\A(?:[^\\\"]++|\\\"(?:[^\\\"]++|\\\"\\\")*+\\\")++",[{parts, - 2}]))), - <<":\"NOT MATCHED">> = iolist_to_binary(join(re:split("NON QUOTED \"QUOT\"\"ED\" AFTER \"NOT MATCHED","\\A(?:[^\\\"]++|\\\"(?:[^\\\"]++|\\\"\\\")*+\\\")++",[]))), - <<":\"NOT MATCHED">> = iolist_to_binary(join(re:split("NON QUOTED \"QUOT\"\"ED\" AFTER \"NOT MATCHED","\\A(?:[^\\\"]++|\\\"(?:[^\\\"]++|\\\"\\\")++\\\")++",[trim]))), + 2}]))), + <<":\"NOT MATCHED">> = iolist_to_binary(join(re:split("NON QUOTED \"QUOT\"\"ED\" AFTER \"NOT MATCHED","\\A(?:[^\\\"]++|\\\"(?:[^\\\"]++|\\\"\\\")*+\\\")++",[]))), + <<":\"NOT MATCHED">> = iolist_to_binary(join(re:split("NON QUOTED \"QUOT\"\"ED\" AFTER \"NOT MATCHED","\\A(?:[^\\\"]++|\\\"(?:[^\\\"]++|\\\"\\\")++\\\")++",[trim]))), <<":\"NOT MATCHED">> = iolist_to_binary(join(re:split("NON QUOTED \"QUOT\"\"ED\" AFTER \"NOT MATCHED","\\A(?:[^\\\"]++|\\\"(?:[^\\\"]++|\\\"\\\")++\\\")++",[{parts, - 2}]))), - <<":\"NOT MATCHED">> = iolist_to_binary(join(re:split("NON QUOTED \"QUOT\"\"ED\" AFTER \"NOT MATCHED","\\A(?:[^\\\"]++|\\\"(?:[^\\\"]++|\\\"\\\")++\\\")++",[]))), - <<": AFTER ::\"NOT MATCHED">> = iolist_to_binary(join(re:split("NON QUOTED \"QUOT\"\"ED\" AFTER \"NOT MATCHED","\\A([^\\\"1]++|[\\\"2]([^\\\"3]*+|[\\\"4][\\\"5])*+[\\\"6])++",[trim]))), + 2}]))), + <<":\"NOT MATCHED">> = iolist_to_binary(join(re:split("NON QUOTED \"QUOT\"\"ED\" AFTER \"NOT MATCHED","\\A(?:[^\\\"]++|\\\"(?:[^\\\"]++|\\\"\\\")++\\\")++",[]))), + <<": AFTER ::\"NOT MATCHED">> = iolist_to_binary(join(re:split("NON QUOTED \"QUOT\"\"ED\" AFTER \"NOT MATCHED","\\A([^\\\"1]++|[\\\"2]([^\\\"3]*+|[\\\"4][\\\"5])*+[\\\"6])++",[trim]))), <<": AFTER ::\"NOT MATCHED">> = iolist_to_binary(join(re:split("NON QUOTED \"QUOT\"\"ED\" AFTER \"NOT MATCHED","\\A([^\\\"1]++|[\\\"2]([^\\\"3]*+|[\\\"4][\\\"5])*+[\\\"6])++",[{parts, - 2}]))), - <<": AFTER ::\"NOT MATCHED">> = iolist_to_binary(join(re:split("NON QUOTED \"QUOT\"\"ED\" AFTER \"NOT MATCHED","\\A([^\\\"1]++|[\\\"2]([^\\\"3]*+|[\\\"4][\\\"5])*+[\\\"6])++",[]))), - <<":t test">> = iolist_to_binary(join(re:split("test test","^\\w+(?>\\s*)(?<=\\w)",[trim]))), + 2}]))), + <<": AFTER ::\"NOT MATCHED">> = iolist_to_binary(join(re:split("NON QUOTED \"QUOT\"\"ED\" AFTER \"NOT MATCHED","\\A([^\\\"1]++|[\\\"2]([^\\\"3]*+|[\\\"4][\\\"5])*+[\\\"6])++",[]))), + <<":t test">> = iolist_to_binary(join(re:split("test test","^\\w+(?>\\s*)(?<=\\w)",[trim]))), <<":t test">> = iolist_to_binary(join(re:split("test test","^\\w+(?>\\s*)(?<=\\w)",[{parts, - 2}]))), - <<":t test">> = iolist_to_binary(join(re:split("test test","^\\w+(?>\\s*)(?<=\\w)",[]))), - <<":a">> = iolist_to_binary(join(re:split("acl","(?P<Name>a)?(?P<Name2>b)?(?(<Name>)c|d)*l",[trim]))), + 2}]))), + <<":t test">> = iolist_to_binary(join(re:split("test test","^\\w+(?>\\s*)(?<=\\w)",[]))), + <<":a">> = iolist_to_binary(join(re:split("acl","(?P<Name>a)?(?P<Name2>b)?(?(<Name>)c|d)*l",[trim]))), <<":a::">> = iolist_to_binary(join(re:split("acl","(?P<Name>a)?(?P<Name2>b)?(?(<Name>)c|d)*l",[{parts, - 2}]))), - <<":a::">> = iolist_to_binary(join(re:split("acl","(?P<Name>a)?(?P<Name2>b)?(?(<Name>)c|d)*l",[]))), - <<"::b">> = iolist_to_binary(join(re:split("bdl","(?P<Name>a)?(?P<Name2>b)?(?(<Name>)c|d)*l",[trim]))), + 2}]))), + <<":a::">> = iolist_to_binary(join(re:split("acl","(?P<Name>a)?(?P<Name2>b)?(?(<Name>)c|d)*l",[]))), + <<"::b">> = iolist_to_binary(join(re:split("bdl","(?P<Name>a)?(?P<Name2>b)?(?(<Name>)c|d)*l",[trim]))), <<"::b:">> = iolist_to_binary(join(re:split("bdl","(?P<Name>a)?(?P<Name2>b)?(?(<Name>)c|d)*l",[{parts, - 2}]))), - <<"::b:">> = iolist_to_binary(join(re:split("bdl","(?P<Name>a)?(?P<Name2>b)?(?(<Name>)c|d)*l",[]))), - <<"a">> = iolist_to_binary(join(re:split("adl","(?P<Name>a)?(?P<Name2>b)?(?(<Name>)c|d)*l",[trim]))), + 2}]))), + <<"::b:">> = iolist_to_binary(join(re:split("bdl","(?P<Name>a)?(?P<Name2>b)?(?(<Name>)c|d)*l",[]))), + <<"a">> = iolist_to_binary(join(re:split("adl","(?P<Name>a)?(?P<Name2>b)?(?(<Name>)c|d)*l",[trim]))), <<"a:::">> = iolist_to_binary(join(re:split("adl","(?P<Name>a)?(?P<Name2>b)?(?(<Name>)c|d)*l",[{parts, - 2}]))), - <<"a:::">> = iolist_to_binary(join(re:split("adl","(?P<Name>a)?(?P<Name2>b)?(?(<Name>)c|d)*l",[]))), - <<"bc">> = iolist_to_binary(join(re:split("bcl","(?P<Name>a)?(?P<Name2>b)?(?(<Name>)c|d)*l",[trim]))), + 2}]))), + <<"a:::">> = iolist_to_binary(join(re:split("adl","(?P<Name>a)?(?P<Name2>b)?(?(<Name>)c|d)*l",[]))), + <<"bc">> = iolist_to_binary(join(re:split("bcl","(?P<Name>a)?(?P<Name2>b)?(?(<Name>)c|d)*l",[trim]))), <<"bc:::">> = iolist_to_binary(join(re:split("bcl","(?P<Name>a)?(?P<Name2>b)?(?(<Name>)c|d)*l",[{parts, - 2}]))), - <<"bc:::">> = iolist_to_binary(join(re:split("bcl","(?P<Name>a)?(?P<Name2>b)?(?(<Name>)c|d)*l",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","\\sabc",[trim]))), + 2}]))), + <<"bc:::">> = iolist_to_binary(join(re:split("bcl","(?P<Name>a)?(?P<Name2>b)?(?(<Name>)c|d)*l",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","\\sabc",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","\\sabc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","\\sabc",[]))), - <<"">> = iolist_to_binary(join(re:split("aa]]","[\\Qa]\\E]+",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","\\sabc",[]))), + <<"">> = iolist_to_binary(join(re:split("aa]]","[\\Qa]\\E]+",[trim]))), <<":">> = iolist_to_binary(join(re:split("aa]]","[\\Qa]\\E]+",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aa]]","[\\Qa]\\E]+",[]))), - <<"">> = iolist_to_binary(join(re:split("aa]]","[\\Q]a\\E]+",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aa]]","[\\Qa]\\E]+",[]))), + <<"">> = iolist_to_binary(join(re:split("aa]]","[\\Q]a\\E]+",[trim]))), <<":">> = iolist_to_binary(join(re:split("aa]]","[\\Q]a\\E]+",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aa]]","[\\Q]a\\E]+",[]))), - <<"1::::::2::::::3::::::4:abcd:abcd">> = iolist_to_binary(join(re:split("1234abcd","(?:((abcd))|(((?:(?:(?:(?:abc|(?:abcdef))))b)abcdefghi)abc)|((*ACCEPT)))",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aa]]","[\\Q]a\\E]+",[]))), + <<"1::::::2::::::3::::::4:abcd:abcd">> = iolist_to_binary(join(re:split("1234abcd","(?:((abcd))|(((?:(?:(?:(?:abc|(?:abcdef))))b)abcdefghi)abc)|((*ACCEPT)))",[trim]))), <<"1::::::234abcd">> = iolist_to_binary(join(re:split("1234abcd","(?:((abcd))|(((?:(?:(?:(?:abc|(?:abcdef))))b)abcdefghi)abc)|((*ACCEPT)))",[{parts, - 2}]))), - <<"1::::::2::::::3::::::4:abcd:abcd::::">> = iolist_to_binary(join(re:split("1234abcd","(?:((abcd))|(((?:(?:(?:(?:abc|(?:abcdef))))b)abcdefghi)abc)|((*ACCEPT)))",[]))), + 2}]))), + <<"1::::::2::::::3::::::4:abcd:abcd::::">> = iolist_to_binary(join(re:split("1234abcd","(?:((abcd))|(((?:(?:(?:(?:abc|(?:abcdef))))b)abcdefghi)abc)|((*ACCEPT)))",[]))), ok. run56() -> - <<"b:a:c">> = iolist_to_binary(join(re:split("baaaaaaaaac","(?1)(?#?'){8}(a)",[trim]))), + <<"b:a:c">> = iolist_to_binary(join(re:split("baaaaaaaaac","(?1)(?#?'){8}(a)",[trim]))), <<"b:a:c">> = iolist_to_binary(join(re:split("baaaaaaaaac","(?1)(?#?'){8}(a)",[{parts, - 2}]))), - <<"b:a:c">> = iolist_to_binary(join(re:split("baaaaaaaaac","(?1)(?#?'){8}(a)",[]))), - <<"a::b::c::d">> = iolist_to_binary(join(re:split("abcd","(?|(\\k'Pm')|(?'Pm'))",[trim]))), + 2}]))), + <<"b:a:c">> = iolist_to_binary(join(re:split("baaaaaaaaac","(?1)(?#?'){8}(a)",[]))), + <<"a::b::c::d">> = iolist_to_binary(join(re:split("abcd","(?|(\\k'Pm')|(?'Pm'))",[trim]))), <<"a::bcd">> = iolist_to_binary(join(re:split("abcd","(?|(\\k'Pm')|(?'Pm'))",[{parts, - 2}]))), - <<"a::b::c::d::">> = iolist_to_binary(join(re:split("abcd","(?|(\\k'Pm')|(?'Pm'))",[]))), - <<" :Fred:099">> = iolist_to_binary(join(re:split(" Fred:099","(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[,;:])(?=.{8,16})(?!.*[\\s])",[trim]))), + 2}]))), + <<"a::b::c::d::">> = iolist_to_binary(join(re:split("abcd","(?|(\\k'Pm')|(?'Pm'))",[]))), + <<" :Fred:099">> = iolist_to_binary(join(re:split(" Fred:099","(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[,;:])(?=.{8,16})(?!.*[\\s])",[trim]))), <<" :Fred:099">> = iolist_to_binary(join(re:split(" Fred:099","(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[,;:])(?=.{8,16})(?!.*[\\s])",[{parts, - 2}]))), - <<" :Fred:099">> = iolist_to_binary(join(re:split(" Fred:099","(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[,;:])(?=.{8,16})(?!.*[\\s])",[]))), - <<" ">> = iolist_to_binary(join(re:split(" X","(?=.*X)X$",[trim]))), + 2}]))), + <<" :Fred:099">> = iolist_to_binary(join(re:split(" Fred:099","(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[,;:])(?=.{8,16})(?!.*[\\s])",[]))), + <<" ">> = iolist_to_binary(join(re:split(" X","(?=.*X)X$",[trim]))), <<" :">> = iolist_to_binary(join(re:split(" X","(?=.*X)X$",[{parts, - 2}]))), - <<" :">> = iolist_to_binary(join(re:split(" X","(?=.*X)X$",[]))), + 2}]))), + <<" :">> = iolist_to_binary(join(re:split(" X","(?=.*X)X$",[]))), + <<">:::<">> = iolist_to_binary(join(re:split(">XXX<","X+(?#comment)?",[trim]))), + <<">:XX<">> = iolist_to_binary(join(re:split(">XXX<","X+(?#comment)?",[{parts, + 2}]))), + <<">:::<">> = iolist_to_binary(join(re:split(">XXX<","X+(?#comment)?",[]))), + <<":pokus">> = iolist_to_binary(join(re:split("pokus."," (?<word> \\w+ )* \\. ",[extended, + caseless, + trim]))), + <<":pokus:">> = iolist_to_binary(join(re:split("pokus."," (?<word> \\w+ )* \\. ",[extended, + caseless, + {parts, + 2}]))), + <<":pokus:">> = iolist_to_binary(join(re:split("pokus."," (?<word> \\w+ )* \\. ",[extended, + caseless]))), + <<"">> = iolist_to_binary(join(re:split("pokus.","(?(DEFINE) (?<word> \\w+ ) ) (?&word)* \\.",[extended, + caseless, + trim]))), + <<"::">> = iolist_to_binary(join(re:split("pokus.","(?(DEFINE) (?<word> \\w+ ) ) (?&word)* \\.",[extended, + caseless, + {parts, + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("pokus.","(?(DEFINE) (?<word> \\w+ ) ) (?&word)* \\.",[extended, + caseless]))), + <<"::pokus">> = iolist_to_binary(join(re:split("pokus.","(?(DEFINE) (?<word> \\w+ ) ) ( (?&word)* ) \\.",[extended, + caseless, + trim]))), + <<"::pokus:">> = iolist_to_binary(join(re:split("pokus.","(?(DEFINE) (?<word> \\w+ ) ) ( (?&word)* ) \\.",[extended, + caseless, + {parts, + 2}]))), + <<"::pokus:">> = iolist_to_binary(join(re:split("pokus.","(?(DEFINE) (?<word> \\w+ ) ) ( (?&word)* ) \\.",[extended, + caseless]))), + <<"">> = iolist_to_binary(join(re:split("pokus.","(?&word)* (?(DEFINE) (?<word> \\w+ ) ) \\.",[extended, + caseless, + trim]))), + <<"::">> = iolist_to_binary(join(re:split("pokus.","(?&word)* (?(DEFINE) (?<word> \\w+ ) ) \\.",[extended, + caseless, + {parts, + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("pokus.","(?&word)* (?(DEFINE) (?<word> \\w+ ) ) \\.",[extended, + caseless]))), + <<":hokus">> = iolist_to_binary(join(re:split("pokus.hokus","(?&word)* \\. (?<word> \\w+ )",[extended, + caseless, + trim]))), + <<":hokus:">> = iolist_to_binary(join(re:split("pokus.hokus","(?&word)* \\. (?<word> \\w+ )",[extended, + caseless, + {parts, + 2}]))), + <<":hokus:">> = iolist_to_binary(join(re:split("pokus.hokus","(?&word)* \\. (?<word> \\w+ )",[extended, + caseless]))), ok. diff --git a/lib/stdlib/vsn.mk b/lib/stdlib/vsn.mk index 07224afdc9..c2f586fef5 100644 --- a/lib/stdlib/vsn.mk +++ b/lib/stdlib/vsn.mk @@ -1 +1 @@ -STDLIB_VSN = 3.9 +STDLIB_VSN = 3.9.2 diff --git a/lib/tools/doc/src/notes.xml b/lib/tools/doc/src/notes.xml index fd41e2cbeb..09ae5ef04a 100644 --- a/lib/tools/doc/src/notes.xml +++ b/lib/tools/doc/src/notes.xml @@ -74,6 +74,21 @@ </section> +<section><title>Tools 3.1.0.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p><c>cover</c> would fail to start if two processes + tried to start it at the exact same time.</p> + <p> + Own Id: OTP-15813 Aux Id: ERL-943 </p> + </item> + </list> + </section> + +</section> + <section><title>Tools 3.1</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -171,6 +186,21 @@ </section> +<section><title>Tools 2.11.2.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p><c>cover</c> would fail to start if two processes + tried to start it at the exact same time.</p> + <p> + Own Id: OTP-15813 Aux Id: ERL-943 </p> + </item> + </list> + </section> + +</section> + <section><title>Tools 2.11.2.1</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/tools/src/cover.erl b/lib/tools/src/cover.erl index 8fe866cb69..2b3af417b6 100644 --- a/lib/tools/src/cover.erl +++ b/lib/tools/src/cover.erl @@ -196,6 +196,8 @@ start() -> receive {?SERVER,started} -> {ok,Pid}; + {?SERVER,{error,Error}} -> + {error,Error}; {'DOWN', Ref, _Type, _Object, Info} -> {error,Info} end, @@ -645,19 +647,31 @@ remote_reply(MainNode,Reply) -> %%%---------------------------------------------------------------------- init_main(Starter) -> - register(?SERVER,self()), - ?COVER_MAPPING_TABLE = ets:new(?COVER_MAPPING_TABLE, - [ordered_set, public, named_table]), - ?COVER_CLAUSE_TABLE = ets:new(?COVER_CLAUSE_TABLE, [set, public, - named_table]), - ?BINARY_TABLE = ets:new(?BINARY_TABLE, [set, public, named_table]), - ?COLLECTION_TABLE = ets:new(?COLLECTION_TABLE, [set, public, - named_table]), - ?COLLECTION_CLAUSE_TABLE = ets:new(?COLLECTION_CLAUSE_TABLE, [set, public, - named_table]), - ok = net_kernel:monitor_nodes(true), - Starter ! {?SERVER,started}, - main_process_loop(#main_state{}). + try register(?SERVER,self()) of + true -> + ?COVER_MAPPING_TABLE = ets:new(?COVER_MAPPING_TABLE, + [ordered_set, public, named_table]), + ?COVER_CLAUSE_TABLE = ets:new(?COVER_CLAUSE_TABLE, [set, public, + named_table]), + ?BINARY_TABLE = ets:new(?BINARY_TABLE, [set, public, named_table]), + ?COLLECTION_TABLE = ets:new(?COLLECTION_TABLE, [set, public, + named_table]), + ?COLLECTION_CLAUSE_TABLE = ets:new(?COLLECTION_CLAUSE_TABLE, + [set, public, named_table]), + ok = net_kernel:monitor_nodes(true), + Starter ! {?SERVER,started}, + main_process_loop(#main_state{}) + catch + error:badarg -> + %% The server's already registered; either report that it's already + %% started or try again if it died before we could find its pid. + case whereis(?SERVER) of + undefined -> + init_main(Starter); + Pid -> + Starter ! {?SERVER, {error, {already_started, Pid}}} + end + end. main_process_loop(State) -> receive diff --git a/lib/tools/test/cover_SUITE.erl b/lib/tools/test/cover_SUITE.erl index ee58fd7a10..462767f430 100644 --- a/lib/tools/test/cover_SUITE.erl +++ b/lib/tools/test/cover_SUITE.erl @@ -37,7 +37,7 @@ all() -> dont_reconnect_after_stop, stop_node_after_disconnect, export_import, otp_5031, otp_6115, otp_8270, otp_10979_hanging_node, otp_14817, - local_only], + local_only, startup_race], case whereis(cover_server) of undefined -> [coverage,StartStop ++ NoStartStop]; @@ -1775,7 +1775,32 @@ local_only(Config) -> {ok,Name} = test_server:start_node(?FUNCTION_NAME, slave, []), {error,local_only} = cover:start([Name]), test_server:stop_node(Name), + ok. +%% ERL-943; We should not crash on startup when multiple servers race to +%% register the server name. +startup_race(Config) when is_list(Config) -> + PidRefs = [spawn_monitor(fun() -> + case cover:start() of + {error, {already_started, _Pid}} -> + ok; + {ok, _Pid} -> + ok + end + end) || _<- lists:seq(1,8)], + startup_race_1(PidRefs). + +startup_race_1([{Pid, Ref} | PidRefs]) -> + receive + {'DOWN', Ref, process, Pid, normal} -> + startup_race_1(PidRefs); + {'DOWN', Ref, process, Pid, _Other} -> + ct:fail("Cover server crashed on startup.") + after 5000 -> + ct:fail("Timed out.") + end; +startup_race_1([]) -> + cover:stop(), ok. %%--Auxiliary------------------------------------------------------------ diff --git a/lib/wx/c_src/Makefile.in b/lib/wx/c_src/Makefile.in index 8ec64bea7e..daadf5e785 100644 --- a/lib/wx/c_src/Makefile.in +++ b/lib/wx/c_src/Makefile.in @@ -70,7 +70,7 @@ else RC_FILE = endif -WX_OBJECTS = $(GENERAL_O) $(GENERATED_O) $(RC_FILE) +WX_OBJECTS = $(GENERATED_O) $(GENERAL_O) $(RC_FILE) OBJECTS = $(WX_OBJECTS) $(GL_OBJECTS) @@ -86,7 +86,7 @@ LD = $(CXX) LDFLAGS = @LDFLAGS@ RESCOMP = @WX_RESCOMP@ -ifeq (@WX_HAVE_STATIC_LIBS@,true) +ifeq (@WX_HAVE_STATIC_LIBS@,true) OPT_WX_LIBS = @WX_LIBS_STATIC@ DEBUG_WX_LIBS = @DEBUG_WX_LIBS_STATIC@ else @@ -97,14 +97,16 @@ endif ifeq ($(TYPE),debug) WX_CFLAGS = @DEBUG_WX_CFLAGS@ CFLAGS = @DEBUG_CFLAGS@ -WX_CXX_FLAGS = @DEBUG_WX_CXXFLAGS@ +WX_CXX_FLAGS = @DEBUG_WX_CXXFLAGS@ CXX_FLAGS = @DEBUG_CXXFLAGS@ +CXX_NO_OPT_FLAGS = @DEBUG_CXXFLAGS@ WX_LIBS = $(DEBUG_WX_LIBS) else WX_CFLAGS = @WX_CFLAGS@ CFLAGS = @CFLAGS@ -WX_CXX_FLAGS = @WX_CXXFLAGS@ +WX_CXX_FLAGS = @WX_CXXFLAGS@ CXX_FLAGS = @CXXFLAGS@ +CXX_NO_OPT_FLAGS = @CXXNOOPTFLAGS@ WX_LIBS = $(OPT_WX_LIBS) endif @@ -113,6 +115,7 @@ GL_LIBS = @GL_LIBS@ CC_O = $(V_CC) -c $(CFLAGS) $(WX_CFLAGS) $(COMMON_CFLAGS) OBJC_CC_O = $(OBJC_CC) -c $(CFLAGS) $(OBJC_CFLAGS) $(WX_CFLAGS) $(COMMON_CFLAGS) CXX_O = $(V_CXX) -c $(CXX_FLAGS) $(WX_CXX_FLAGS) $(COMMON_CFLAGS) +CXX_O_NO_OPT = $(V_CXX) -c $(CXX_NO_OPT_FLAGS) $(WX_CXX_FLAGS) $(COMMON_CFLAGS) # Targets @@ -152,6 +155,10 @@ $(SYS_TYPE)/wxe_ps_init.o: wxe_ps_init.c $(V_at)mkdir -p $(SYS_TYPE) $(cc_verbose)$(OBJC_CC_O) $< -o $@ +$(SYS_TYPE)/wxe_funcs.o: gen/wxe_funcs.cpp + $(V_at)mkdir -p $(SYS_TYPE) + $(CXX_O_NO_OPT) $< -o $@ + $(SYS_TYPE)/%.o: gen/%.cpp $(V_at)mkdir -p $(SYS_TYPE) $(CXX_O) $< -o $@ diff --git a/lib/wx/c_src/wxe_driver.c b/lib/wx/c_src/wxe_driver.c index c9d299e0df..b94ec2f32d 100644 --- a/lib/wx/c_src/wxe_driver.c +++ b/lib/wx/c_src/wxe_driver.c @@ -214,7 +214,7 @@ standard_outputv(ErlDrvData drv_data, ErlIOVec* ev) if(binref == NULL) { /* realloc */ max = sd->max_bins + DEF_BINS; - driver_realloc(sd->bin, sizeof(WXEBinRef)*max); + sd->bin = driver_realloc(sd->bin, sizeof(WXEBinRef)*max); for(i=sd->max_bins; i < max; i++) { sd->bin[i].from = 0; } diff --git a/lib/wx/configure.in b/lib/wx/configure.in index dbe237cd74..f35e6cdbd0 100644 --- a/lib/wx/configure.in +++ b/lib/wx/configure.in @@ -30,17 +30,11 @@ if test -f ./CONF_INFO; then fi if test -z "$ERL_TOP" || test ! -d $ERL_TOP ; then - AC_CONFIG_AUX_DIRS(autoconf) - WX_BUILDING_INSIDE_ERLSRC=false + AC_MSG_ERROR([ERL_TOP is not set]) else erl_top=${ERL_TOP} - if test -d $erl_top/erts/autoconf; then - AC_CONFIG_AUX_DIRS($erl_top/erts/autoconf) - WX_BUILDING_INSIDE_ERLSRC=true - else - AC_CONFIG_AUX_DIRS(autoconf) - WX_BUILDING_INSIDE_ERLSRC=false - fi + AC_CONFIG_AUX_DIRS($erl_top/erts/autoconf) + WX_BUILDING_INSIDE_ERLSRC=true fi if test "X$host" != "Xfree_source" -a "X$host" != "Xwin32"; then @@ -67,6 +61,20 @@ AC_PROG_CXX AC_PROG_RANLIB AC_PROG_CPP +AC_LANG_PUSH([C++]) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM( + [[#ifndef __cplusplus + #error "broken C++" + #endif]])],, + [CXX=;]) +AC_LANG_POP([C++]) +if test "X$CXX" = X ; then + echo "Can not find C++ compiler" >> ./CONF_INFO + WXERL_CAN_BUILD_DRIVER=false + AC_MSG_WARN([Can not find C++ compiler]) +fi +WXERL_CAN_BUILD_DRIVER=false + AC_MSG_NOTICE(Building for [$host_os]) WXERL_CAN_BUILD_DRIVER=true @@ -139,13 +147,9 @@ case $host_os in if test X$APPLE_CC = X -o X$APPLE_CXX = X; then AC_MSG_RESULT([no]) dnl Complete failure, we cannot build Cocoa code - if test X"$WX_BUILDING_INSIDE_ERLSRC" != X"true" ; then - AC_MSG_ERROR([Can not find compiler to compile Cocoa applications]) - else - echo "Can not find compiler to compile Cocoa applications" > ./CONF_INFO - WXERL_CAN_BUILD_DRIVER=false - AC_MSG_WARN([Can not find compiler to compile Cocoa applications]) - fi + echo "Can not find compiler to compile Cocoa applications" >> ./CONF_INFO + WXERL_CAN_BUILD_DRIVER=false + AC_MSG_WARN([Can not find compiler to compile Cocoa applications]) WXERL_CAN_BUILD_DRIVER=false else dnl We think we found an Apple compiler and will add @@ -225,11 +229,21 @@ case $host_os in ;; *) DEBUG_CFLAGS="-g -Wall -fPIC $CFLAGS -DDEBUG" - CFLAGS="-g -Wall -O2 -fPIC $CFLAGS -fomit-frame-pointer -fno-strict-aliasing" + CFLAGS="-Wall -fPIC $CFLAGS -fomit-frame-pointer -fno-strict-aliasing" ;; esac -dnl +dnl +dnl Use -O1 -fno-move-loop-invariants for wxe_funcs.cpp to reduce +dnl compilation time +dnl + +if test "x$GCC" = xyes -a X"$host_os" != X"win32" ; then + CXXNOOPT="-O1" + LM_TRY_ENABLE_CFLAG([-fno-move-loop-invariants], [CXXNOOPT]) +fi + +dnl dnl Opengl tests dnl @@ -251,11 +265,13 @@ if test X"$host_os" != X"win32" ; then AC_CHECK_HEADERS([GL/gl.h]) if test X"$ac_cv_header_GL_gl_h" != Xyes ; then AC_MSG_WARN([No OpenGL headers found, wx will NOT be usable]) + echo "No OpenGL headers found, wx will NOT be usable" >> ./CONF_INFO + WXERL_CAN_BUILD_DRIVER=false CPPFLAGS="$saved_CPPFLAGS" - else + else GL_LIBS="-L/usr/local/lib $GL_LIBS" fi - else + else GL_LIBS="-L/usr/X11R6/lib $GL_LIBS" fi fi @@ -270,6 +286,8 @@ if test X"$host_os" != X"win32" ; then test X"$ac_cv_header_OpenGL_glu_h" != Xyes then AC_MSG_WARN([No GLU headers found, wx will NOT be usable]) + echo "No GLU headers (glu.h) found, wx will NOT be usable" >> ./CONF_INFO + WXERL_CAN_BUILD_DRIVER=false fi else AC_CHECK_HEADERS([gl/glu.h],[],[],[#include <windows.h>]) @@ -280,47 +298,17 @@ AC_SUBST(GL_LIBS) DEBUG_CXXFLAGS="$CXXFLAGS $DEBUG_CFLAGS $CPPFLAGS" DEBUG_CFLAGS="$DEBUG_CFLAGS $CPPFLAGS $C_ONLY_FLAGS" -CXXFLAGS="$CXXFLAGS $CFLAGS $CPPFLAGS" +CXXNOOPTFLAGS="$CXXFLAGS $CFLAGS $CPPFLAGS $CXXNOOPT" +CXXFLAGS="$CXXFLAGS $CFLAGS $CPPFLAGS" CFLAGS="$CFLAGS $CPPFLAGS $C_ONLY_FLAGS" AC_SUBST(DEBUG_CFLAGS) AC_SUBST(DEBUG_CXXFLAGS) - -if test X"$WX_BUILDING_INSIDE_ERLSRC" != X"true" ; then - AC_MSG_CHECKING(for erl) - if test X$ERL != X; then - AC_MSG_RESULT([yes; using $ERL]) - else - type erl >/dev/null 2>&1 - if test $? -eq 0 ; then - ERL=erl - AC_MSG_RESULT([yes]) - else - AC_MSG_ERROR([Cannot find erl in path]) - fi - fi - AC_MSG_CHECKING(for erlc) - if test X$ERLC != X; then - AC_MSG_RESULT([yes; using $ERLC]) - else - type erlc >/dev/null 2>&1 - if test $? -eq 0 ; then - ERLC=erlc - AC_MSG_RESULT([yes]) - else - AC_MSG_ERROR([Cannot find erlc in path]) - fi - fi - ERLANG_ROOT_DIR=`erl -noshell -eval 'io:format("~s~n",[[code:root_dir()]])' -s erlang halt` - AC_MSG_NOTICE(ERL ROOT DIR: [$ERLANG_ROOT_DIR]) - ERLWX_VSN=`grep WX_VSN $srcdir/vsn.mk | sed 's/^.*=[ ]*//'` -else - ERLC=erlc - ERL=erl - ERLANG_ROOT_DIR=$ERL_TOP - AC_SUBST(ERLC) -fi +ERLC=erlc +ERL=erl +ERLANG_ROOT_DIR=$ERL_TOP +AC_SUBST(ERLC) AC_SUBST(WX_BUILDING_INSIDE_ERLSRC) AC_SUBST(ERLANG_ROOT_DIR) @@ -329,7 +317,7 @@ dnl dnl Check for wxwidgets dnl if test "$cross_compiling" = "yes"; then - echo "Cross compilation of the wx driver is not supported yet, wx will NOT be usable" > ./CONF_INFO + echo "Cross compilation of the wx driver is not supported yet, wx will NOT be usable" >> ./CONF_INFO WXERL_CAN_BUILD_DRIVER=false elif test X"$MIXED_CYGWIN_VC" = X"no" -a X"$MIXED_MSYS_VC" = X"no"; then WX_VERSION=`wx-config --version` @@ -398,13 +386,9 @@ define(wx_warn_text,[ wxWidgets version is $reqwx or above.]) if test "$wxWin" != 1; then - if test X"$WX_BUILDING_INSIDE_ERLSRC" != X"true" ; then - AC_MSG_ERROR([wx_warn_text]) - else - echo "wxWidgets not found, wx will NOT be usable" > ./CONF_INFO - WXERL_CAN_BUILD_DRIVER=false - AC_MSG_WARN([wx_warn_text]) - fi + echo "wxWidgets not found, wx will NOT be usable" >> ./CONF_INFO + WXERL_CAN_BUILD_DRIVER=false + AC_MSG_WARN([wx_warn_text]) fi else AC_MSG_CHECKING(for wxWidgets in standard locations) @@ -502,13 +486,9 @@ else if test -z "$WX_LIBS_STATIC"; then AC_MSG_RESULT([failed]) - if test X"$WX_BUILDING_INSIDE_ERLSRC" != X"true" ; then - AC_MSG_ERROR([Cannot find core lib version for wxWidgets]) - else - echo "No usable wxWidgets not found, wx will not be useable" > ./CONF_INFO - WXERL_CAN_BUILD_DRIVER=false - AC_MSG_WARN([Cannot find core lib version for wxWidgets]) - fi + echo "No usable wxWidgets not found, wx will not be useable" >> ./CONF_INFO + WXERL_CAN_BUILD_DRIVER=false + AC_MSG_WARN([Cannot find core lib version for wxWidgets]) fi WX_HAVE_STATIC_LIBS=true AC_SUBST(WX_CFLAGS) @@ -550,8 +530,8 @@ AC_MSG_RESULT($HAVE_GL_SUPPORT) AC_SUBST(HAVE_GL_SUPPORT) if test X"$HAVE_GL_SUPPORT" != X"yes" ; then - echo "wxWidgets don't have gl support, wx will NOT be useable" > ./CONF_INFO - WXERL_CAN_BUILD_DRIVER=false + echo "wxWidgets don't have gl support, wx will NOT be useable" >> ./CONF_INFO + WXERL_CAN_BUILD_DRIVER=false fi dnl Check for GLintptr @@ -610,7 +590,7 @@ dnl AC_CHECK_HEADERS([wx/stc/stc.h], [], [WXERL_CAN_BUILD_DRIVER=false - echo "wxWidgets don't have wxStyledTextControl (stc.h), wx will NOT be useable" > ./CONF_INFO + echo "wxWidgets don't have wxStyledTextControl (stc.h), wx will NOT be useable" >> ./CONF_INFO AC_MSG_WARN([Can not find wx/stc/stc.h $CXXFLAGS]) ], [#ifdef WIN32 @@ -670,9 +650,9 @@ AC_LANG_POP(C++) AC_MSG_RESULT($CAN_LINK_WX) if test X"$CAN_LINK_WX" != X"yes" ; then - echo "Can not link the wx driver, wx will NOT be useable" > ./CONF_INFO - WXERL_CAN_BUILD_DRIVER=false - AC_MSG_WARN([Can not link wx program are all developer packages installed?]) + echo "Can not link the wx driver, wx will NOT be useable" >> ./CONF_INFO + WXERL_CAN_BUILD_DRIVER=false + AC_MSG_WARN([Can not link wx program are all developer packages installed?]) fi fi dnl - if test "$WXERL_CAN_BUILD_DRIVER" != "false" @@ -721,6 +701,7 @@ esac AC_SUBST(SO_EXT) AC_SUBST(RUN_ERL) +AC_SUBST(CXXNOOPTFLAGS) if test X"$WX_BUILDING_INSIDE_ERLSRC" != X"true" ; then @@ -737,12 +718,6 @@ CONFIG_STATUS=$WXERL_SYS_TYPE/config.status dnl -if test X"$WX_BUILDING_INSIDE_ERLSRC" != X"true" ; then - if test X"$WXERL_CAN_BUILD_DRIVER" != X"true" ; then - AC_MSG_ERROR([Cannot build wxErlang driver, see ./CONF_INFO for information]) - fi -fi - AC_CONFIG_FILES([ config.mk c_src/Makefile @@ -750,20 +725,9 @@ AC_CONFIG_FILES([ AC_OUTPUT -if test X"$WX_BUILDING_INSIDE_ERLSRC" != X"true" ; then - AC_MSG_NOTICE() - AC_MSG_NOTICE(--------------------------------------------------) - AC_MSG_NOTICE(Using erlang compiler: [$ERLC]) - AC_MSG_NOTICE(wxErlang Install in: [$ERLANG_ROOT_DIR/lib/wx-$ERLWX_VSN]) - AC_MSG_NOTICE(Erlang driver in priv/[$WXERL_SYS_TYPE]/wxe_driver[$SO_EXT]) - AC_MSG_NOTICE(--------------------------------------------------) -fi - -if test X"$WX_BUILDING_INSIDE_ERLSRC" = X"true" ; then - CORES=`ls core* 2>/dev/null` - if test X"$CORES" != X"" ; then - echo "Configure dumped core files" > ignore_core_files - fi +CORES=`ls core* 2>/dev/null` +if test X"$CORES" != X"" ; then + echo "Configure dumped core files" > ignore_core_files fi diff --git a/lib/xmerl/doc/src/notes.xml b/lib/xmerl/doc/src/notes.xml index 37973d0dba..9fb4a430e5 100644 --- a/lib/xmerl/doc/src/notes.xml +++ b/lib/xmerl/doc/src/notes.xml @@ -47,6 +47,23 @@ </section> +<section><title>Xmerl 1.3.20.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + <c>xmerl_sax_parser</c> crashed during charset detection + when the xml declarations attribute values was missing + the closing quotation (' or ").</p> + <p> + Own Id: OTP-15826</p> + </item> + </list> + </section> + +</section> + <section><title>Xmerl 1.3.20</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/xmerl/src/xmerl_sax_parser.erl b/lib/xmerl/src/xmerl_sax_parser.erl index fe836fd8cd..2767d02552 100644 --- a/lib/xmerl/src/xmerl_sax_parser.erl +++ b/lib/xmerl/src/xmerl_sax_parser.erl @@ -369,8 +369,8 @@ parse_eq(_, State) -> %%---------------------------------------------------------------------- parse_value(<<C, Rest/binary>>, State) when ?is_whitespace(C) -> parse_value(Rest, State); -parse_value(<<C, Rest/binary>>, _State) when C == $'; C == $" -> - parse_value_1(Rest, C, []); +parse_value(<<C, Rest/binary>>, State) when C == $'; C == $" -> + parse_value_1(Rest, C, [], State); parse_value(_, State) -> ?fatal_error(State, "\', \" or whitespace expected"). @@ -383,10 +383,12 @@ parse_value(_, State) -> %% Rest = binary() %% Description: Parsing an attribute value from the stream. %%---------------------------------------------------------------------- -parse_value_1(<<Stop, Rest/binary>>, Stop, Acc) -> +parse_value_1(<<Stop, Rest/binary>>, Stop, Acc, _State) -> {lists:reverse(Acc), Rest}; -parse_value_1(<<C, Rest/binary>>, Stop, Acc) -> - parse_value_1(Rest, Stop, [C |Acc]). +parse_value_1(<<C, Rest/binary>>, Stop, Acc, State) -> + parse_value_1(Rest, Stop, [C |Acc], State); +parse_value_1(_, _Stop, _Acc, State) -> + ?fatal_error(State, "end of input and no \' or \" found"). %%====================================================================== %% Default functions diff --git a/make/otp_patch_solve_forward_merge_version b/make/otp_patch_solve_forward_merge_version index f599e28b8a..b4de394767 100644 --- a/make/otp_patch_solve_forward_merge_version +++ b/make/otp_patch_solve_forward_merge_version @@ -1 +1 @@ -10 +11 diff --git a/make/otp_version_tickets b/make/otp_version_tickets index 12cf3070e5..b8220e1a87 100644 --- a/make/otp_version_tickets +++ b/make/otp_version_tickets @@ -1,147 +1 @@ -OTP-13397 -OTP-13726 -OTP-13819 -OTP-14282 -OTP-14461 -OTP-14625 -OTP-14703 -OTP-14724 -OTP-14731 -OTP-14732 -OTP-14737 -OTP-14792 -OTP-14831 -OTP-14865 -OTP-14894 -OTP-14984 -OTP-15011 -OTP-15055 -OTP-15063 -OTP-15075 -OTP-15095 -OTP-15111 -OTP-15128 -OTP-15129 -OTP-15135 -OTP-15195 -OTP-15200 -OTP-15211 -OTP-15220 -OTP-15238 -OTP-15248 -OTP-15261 -OTP-15262 -OTP-15273 -OTP-15310 -OTP-15323 -OTP-15325 -OTP-15326 -OTP-15336 -OTP-15340 -OTP-15349 -OTP-15351 -OTP-15362 -OTP-15372 -OTP-15373 -OTP-15374 -OTP-15381 -OTP-15383 -OTP-15385 -OTP-15386 -OTP-15427 -OTP-15429 -OTP-15439 -OTP-15440 -OTP-15451 -OTP-15452 -OTP-15456 -OTP-15483 -OTP-15491 -OTP-15496 -OTP-15513 -OTP-15534 -OTP-15535 -OTP-15543 -OTP-15550 -OTP-15563 -OTP-15564 -OTP-15565 -OTP-15571 -OTP-15575 -OTP-15581 -OTP-15588 -OTP-15590 -OTP-15591 -OTP-15596 -OTP-15607 -OTP-15610 -OTP-15611 -OTP-15612 -OTP-15613 -OTP-15615 -OTP-15617 -OTP-15620 -OTP-15621 -OTP-15622 -OTP-15623 -OTP-15626 -OTP-15631 -OTP-15632 -OTP-15633 -OTP-15635 -OTP-15640 -OTP-15641 -OTP-15644 -OTP-15649 -OTP-15655 -OTP-15658 -OTP-15668 -OTP-15672 -OTP-15673 -OTP-15674 -OTP-15677 -OTP-15678 -OTP-15680 -OTP-15683 -OTP-15687 -OTP-15689 -OTP-15691 -OTP-15692 -OTP-15693 -OTP-15694 -OTP-15697 -OTP-15698 -OTP-15699 -OTP-15703 -OTP-15705 -OTP-15707 -OTP-15708 -OTP-15710 -OTP-15712 -OTP-15715 -OTP-15740 -OTP-15746 -OTP-15749 -OTP-15751 -OTP-15752 -OTP-15754 -OTP-15755 -OTP-15756 -OTP-15759 -OTP-15760 -OTP-15761 -OTP-15770 -OTP-15771 -OTP-15773 -OTP-15774 -OTP-15775 -OTP-15776 -OTP-15777 -OTP-15779 -OTP-15782 -OTP-15786 -OTP-15788 -OTP-15791 -OTP-15794 -OTP-15804 -OTP-15806 +DEVELOPMENT diff --git a/make/otp_version_tickets_in_merge b/make/otp_version_tickets_in_merge index e69de29bb2..b741526c88 100644 --- a/make/otp_version_tickets_in_merge +++ b/make/otp_version_tickets_in_merge @@ -0,0 +1,10 @@ +OTP-15900 +OTP-15908 +OTP-15909 +OTP-15910 +OTP-15913 +OTP-15922 +OTP-15930 +OTP-15938 +OTP-15939 +OTP-15941 diff --git a/otp_versions.table b/otp_versions.table index 101915dc6e..3cc0e83167 100644 --- a/otp_versions.table +++ b/otp_versions.table @@ -1,4 +1,14 @@ +OTP-22.0.5 : dialyzer-4.0.2 erts-10.4.4 inets-7.0.9 ssl-9.3.4 # asn1-5.0.9 common_test-1.17.3 compiler-7.4.2 crypto-4.5.1 debugger-4.2.7 diameter-2.2.1 edoc-0.11 eldap-1.2.8 erl_docgen-0.9.1 erl_interface-3.12 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.19 jinterface-1.10 kernel-6.4.1 megaco-3.18.5 mnesia-4.16 observer-2.9.1 odbc-2.12.4 os_mon-2.5 parsetools-2.1.8 public_key-1.6.7 reltool-0.8 runtime_tools-1.13.3 sasl-3.4 snmp-5.3 ssh-4.7.7 stdlib-3.9.2 syntax_tools-2.2 tftp-1.0.1 tools-3.2 wx-1.8.8 xmerl-1.3.21 : +OTP-22.0.4 : erts-10.4.3 kernel-6.4.1 ssl-9.3.3 # asn1-5.0.9 common_test-1.17.3 compiler-7.4.2 crypto-4.5.1 debugger-4.2.7 dialyzer-4.0.1 diameter-2.2.1 edoc-0.11 eldap-1.2.8 erl_docgen-0.9.1 erl_interface-3.12 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.19 inets-7.0.8 jinterface-1.10 megaco-3.18.5 mnesia-4.16 observer-2.9.1 odbc-2.12.4 os_mon-2.5 parsetools-2.1.8 public_key-1.6.7 reltool-0.8 runtime_tools-1.13.3 sasl-3.4 snmp-5.3 ssh-4.7.7 stdlib-3.9.2 syntax_tools-2.2 tftp-1.0.1 tools-3.2 wx-1.8.8 xmerl-1.3.21 : +OTP-22.0.3 : compiler-7.4.2 dialyzer-4.0.1 erts-10.4.2 ssl-9.3.2 stdlib-3.9.2 # asn1-5.0.9 common_test-1.17.3 crypto-4.5.1 debugger-4.2.7 diameter-2.2.1 edoc-0.11 eldap-1.2.8 erl_docgen-0.9.1 erl_interface-3.12 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.19 inets-7.0.8 jinterface-1.10 kernel-6.4 megaco-3.18.5 mnesia-4.16 observer-2.9.1 odbc-2.12.4 os_mon-2.5 parsetools-2.1.8 public_key-1.6.7 reltool-0.8 runtime_tools-1.13.3 sasl-3.4 snmp-5.3 ssh-4.7.7 syntax_tools-2.2 tftp-1.0.1 tools-3.2 wx-1.8.8 xmerl-1.3.21 : +OTP-22.0.2 : compiler-7.4.1 crypto-4.5.1 erts-10.4.1 stdlib-3.9.1 # asn1-5.0.9 common_test-1.17.3 debugger-4.2.7 dialyzer-4.0 diameter-2.2.1 edoc-0.11 eldap-1.2.8 erl_docgen-0.9.1 erl_interface-3.12 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.19 inets-7.0.8 jinterface-1.10 kernel-6.4 megaco-3.18.5 mnesia-4.16 observer-2.9.1 odbc-2.12.4 os_mon-2.5 parsetools-2.1.8 public_key-1.6.7 reltool-0.8 runtime_tools-1.13.3 sasl-3.4 snmp-5.3 ssh-4.7.7 ssl-9.3.1 syntax_tools-2.2 tftp-1.0.1 tools-3.2 wx-1.8.8 xmerl-1.3.21 : +OTP-22.0.1 : ssl-9.3.1 # asn1-5.0.9 common_test-1.17.3 compiler-7.4 crypto-4.5 debugger-4.2.7 dialyzer-4.0 diameter-2.2.1 edoc-0.11 eldap-1.2.8 erl_docgen-0.9.1 erl_interface-3.12 erts-10.4 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.19 inets-7.0.8 jinterface-1.10 kernel-6.4 megaco-3.18.5 mnesia-4.16 observer-2.9.1 odbc-2.12.4 os_mon-2.5 parsetools-2.1.8 public_key-1.6.7 reltool-0.8 runtime_tools-1.13.3 sasl-3.4 snmp-5.3 ssh-4.7.7 stdlib-3.9 syntax_tools-2.2 tftp-1.0.1 tools-3.2 wx-1.8.8 xmerl-1.3.21 : OTP-22.0 : asn1-5.0.9 common_test-1.17.3 compiler-7.4 crypto-4.5 debugger-4.2.7 dialyzer-4.0 edoc-0.11 eldap-1.2.8 erl_docgen-0.9.1 erl_interface-3.12 erts-10.4 hipe-3.19 inets-7.0.8 jinterface-1.10 kernel-6.4 megaco-3.18.5 mnesia-4.16 observer-2.9.1 odbc-2.12.4 os_mon-2.5 public_key-1.6.7 reltool-0.8 runtime_tools-1.13.3 sasl-3.4 snmp-5.3 ssh-4.7.7 ssl-9.3 stdlib-3.9 syntax_tools-2.2 tools-3.2 wx-1.8.8 xmerl-1.3.21 # diameter-2.2.1 et-1.6.4 eunit-2.3.7 ftp-1.0.2 parsetools-2.1.8 tftp-1.0.1 : +OTP-21.3.8.5 : erts-10.3.5.4 ssl-9.2.3.4 # asn1-5.0.8 common_test-1.17.2.1 compiler-7.3.2 crypto-4.4.2 debugger-4.2.6 dialyzer-3.3.2 diameter-2.2.1 edoc-0.10 eldap-1.2.7 erl_docgen-0.9 erl_interface-3.11.3 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.18.3 inets-7.0.7 jinterface-1.9.1 kernel-6.3.1.2 megaco-3.18.4 mnesia-4.15.6 observer-2.9 odbc-2.12.3 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 public_key-1.6.6.1 reltool-0.7.8 runtime_tools-1.13.2 sasl-3.3 snmp-5.2.12 ssh-4.7.6 stdlib-3.8.2.2 syntax_tools-2.1.7 tftp-1.0.1 tools-3.1.0.1 wx-1.8.7 xmerl-1.3.20.1 : +OTP-21.3.8.4 : common_test-1.17.2.1 erts-10.3.5.3 kernel-6.3.1.2 public_key-1.6.6.1 ssl-9.2.3.3 stdlib-3.8.2.2 # asn1-5.0.8 compiler-7.3.2 crypto-4.4.2 debugger-4.2.6 dialyzer-3.3.2 diameter-2.2.1 edoc-0.10 eldap-1.2.7 erl_docgen-0.9 erl_interface-3.11.3 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.18.3 inets-7.0.7 jinterface-1.9.1 megaco-3.18.4 mnesia-4.15.6 observer-2.9 odbc-2.12.3 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 reltool-0.7.8 runtime_tools-1.13.2 sasl-3.3 snmp-5.2.12 ssh-4.7.6 syntax_tools-2.1.7 tftp-1.0.1 tools-3.1.0.1 wx-1.8.7 xmerl-1.3.20.1 : +OTP-21.3.8.3 : erts-10.3.5.2 kernel-6.3.1.1 ssl-9.2.3.2 stdlib-3.8.2.1 # asn1-5.0.8 common_test-1.17.2 compiler-7.3.2 crypto-4.4.2 debugger-4.2.6 dialyzer-3.3.2 diameter-2.2.1 edoc-0.10 eldap-1.2.7 erl_docgen-0.9 erl_interface-3.11.3 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.18.3 inets-7.0.7 jinterface-1.9.1 megaco-3.18.4 mnesia-4.15.6 observer-2.9 odbc-2.12.3 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 public_key-1.6.6 reltool-0.7.8 runtime_tools-1.13.2 sasl-3.3 snmp-5.2.12 ssh-4.7.6 syntax_tools-2.1.7 tftp-1.0.1 tools-3.1.0.1 wx-1.8.7 xmerl-1.3.20.1 : +OTP-21.3.8.2 : xmerl-1.3.20.1 # asn1-5.0.8 common_test-1.17.2 compiler-7.3.2 crypto-4.4.2 debugger-4.2.6 dialyzer-3.3.2 diameter-2.2.1 edoc-0.10 eldap-1.2.7 erl_docgen-0.9 erl_interface-3.11.3 erts-10.3.5.1 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.18.3 inets-7.0.7 jinterface-1.9.1 kernel-6.3.1 megaco-3.18.4 mnesia-4.15.6 observer-2.9 odbc-2.12.3 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 public_key-1.6.6 reltool-0.7.8 runtime_tools-1.13.2 sasl-3.3 snmp-5.2.12 ssh-4.7.6 ssl-9.2.3.1 stdlib-3.8.2 syntax_tools-2.1.7 tftp-1.0.1 tools-3.1.0.1 wx-1.8.7 : +OTP-21.3.8.1 : erts-10.3.5.1 ssl-9.2.3.1 tools-3.1.0.1 # asn1-5.0.8 common_test-1.17.2 compiler-7.3.2 crypto-4.4.2 debugger-4.2.6 dialyzer-3.3.2 diameter-2.2.1 edoc-0.10 eldap-1.2.7 erl_docgen-0.9 erl_interface-3.11.3 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.18.3 inets-7.0.7 jinterface-1.9.1 kernel-6.3.1 megaco-3.18.4 mnesia-4.15.6 observer-2.9 odbc-2.12.3 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 public_key-1.6.6 reltool-0.7.8 runtime_tools-1.13.2 sasl-3.3 snmp-5.2.12 ssh-4.7.6 stdlib-3.8.2 syntax_tools-2.1.7 tftp-1.0.1 wx-1.8.7 xmerl-1.3.20 : OTP-21.3.8 : common_test-1.17.2 eldap-1.2.7 erl_interface-3.11.3 erts-10.3.5 public_key-1.6.6 ssl-9.2.3 stdlib-3.8.2 # asn1-5.0.8 compiler-7.3.2 crypto-4.4.2 debugger-4.2.6 dialyzer-3.3.2 diameter-2.2.1 edoc-0.10 erl_docgen-0.9 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.18.3 inets-7.0.7 jinterface-1.9.1 kernel-6.3.1 megaco-3.18.4 mnesia-4.15.6 observer-2.9 odbc-2.12.3 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 reltool-0.7.8 runtime_tools-1.13.2 sasl-3.3 snmp-5.2.12 ssh-4.7.6 syntax_tools-2.1.7 tftp-1.0.1 tools-3.1 wx-1.8.7 xmerl-1.3.20 : OTP-21.3.7.1 : erl_interface-3.11.2.1 # asn1-5.0.8 common_test-1.17.1 compiler-7.3.2 crypto-4.4.2 debugger-4.2.6 dialyzer-3.3.2 diameter-2.2.1 edoc-0.10 eldap-1.2.6 erl_docgen-0.9 erts-10.3.4 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.18.3 inets-7.0.7 jinterface-1.9.1 kernel-6.3.1 megaco-3.18.4 mnesia-4.15.6 observer-2.9 odbc-2.12.3 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 public_key-1.6.5 reltool-0.7.8 runtime_tools-1.13.2 sasl-3.3 snmp-5.2.12 ssh-4.7.6 ssl-9.2.2 stdlib-3.8.1 syntax_tools-2.1.7 tftp-1.0.1 tools-3.1 wx-1.8.7 xmerl-1.3.20 : OTP-21.3.7 : ssh-4.7.6 # asn1-5.0.8 common_test-1.17.1 compiler-7.3.2 crypto-4.4.2 debugger-4.2.6 dialyzer-3.3.2 diameter-2.2.1 edoc-0.10 eldap-1.2.6 erl_docgen-0.9 erl_interface-3.11.2 erts-10.3.4 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.18.3 inets-7.0.7 jinterface-1.9.1 kernel-6.3.1 megaco-3.18.4 mnesia-4.15.6 observer-2.9 odbc-2.12.3 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 public_key-1.6.5 reltool-0.7.8 runtime_tools-1.13.2 sasl-3.3 snmp-5.2.12 ssl-9.2.2 stdlib-3.8.1 syntax_tools-2.1.7 tftp-1.0.1 tools-3.1 wx-1.8.7 xmerl-1.3.20 : @@ -32,6 +42,7 @@ OTP-21.0.3 : erts-10.0.3 # asn1-5.0.6 common_test-1.16 compiler-7.2.2 crypto-4.3 OTP-21.0.2 : compiler-7.2.2 erts-10.0.2 public_key-1.6.1 stdlib-3.5.1 # asn1-5.0.6 common_test-1.16 crypto-4.3 debugger-4.2.5 dialyzer-3.3 diameter-2.1.5 edoc-0.9.3 eldap-1.2.4 erl_docgen-0.8 erl_interface-3.10.3 et-1.6.2 eunit-2.3.6 ftp-1.0 hipe-3.18 inets-7.0 jinterface-1.9 kernel-6.0 megaco-3.18.3 mnesia-4.15.4 observer-2.8 odbc-2.12.1 os_mon-2.4.5 otp_mibs-1.2 parsetools-2.1.7 reltool-0.7.6 runtime_tools-1.13 sasl-3.2 snmp-5.2.11 ssh-4.7 ssl-9.0 syntax_tools-2.1.5 tftp-1.0 tools-3.0 wx-1.8.4 xmerl-1.3.17 : OTP-21.0.1 : compiler-7.2.1 erts-10.0.1 # asn1-5.0.6 common_test-1.16 crypto-4.3 debugger-4.2.5 dialyzer-3.3 diameter-2.1.5 edoc-0.9.3 eldap-1.2.4 erl_docgen-0.8 erl_interface-3.10.3 et-1.6.2 eunit-2.3.6 ftp-1.0 hipe-3.18 inets-7.0 jinterface-1.9 kernel-6.0 megaco-3.18.3 mnesia-4.15.4 observer-2.8 odbc-2.12.1 os_mon-2.4.5 otp_mibs-1.2 parsetools-2.1.7 public_key-1.6 reltool-0.7.6 runtime_tools-1.13 sasl-3.2 snmp-5.2.11 ssh-4.7 ssl-9.0 stdlib-3.5 syntax_tools-2.1.5 tftp-1.0 tools-3.0 wx-1.8.4 xmerl-1.3.17 : OTP-21.0 : asn1-5.0.6 common_test-1.16 compiler-7.2 crypto-4.3 debugger-4.2.5 dialyzer-3.3 diameter-2.1.5 edoc-0.9.3 eldap-1.2.4 erl_docgen-0.8 erl_interface-3.10.3 erts-10.0 et-1.6.2 eunit-2.3.6 ftp-1.0 hipe-3.18 inets-7.0 jinterface-1.9 kernel-6.0 mnesia-4.15.4 observer-2.8 os_mon-2.4.5 otp_mibs-1.2 parsetools-2.1.7 public_key-1.6 reltool-0.7.6 runtime_tools-1.13 sasl-3.2 ssh-4.7 ssl-9.0 stdlib-3.5 syntax_tools-2.1.5 tftp-1.0 tools-3.0 wx-1.8.4 xmerl-1.3.17 # megaco-3.18.3 odbc-2.12.1 snmp-5.2.11 : +OTP-20.3.8.22 : common_test-1.15.4.3 erts-9.3.3.11 tools-2.11.2.2 # asn1-5.0.5.2 compiler-7.1.5.2 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2.2.2 debugger-4.2.4 dialyzer-3.2.4 diameter-2.1.4.1 edoc-0.9.2 eldap-1.2.3.1 erl_docgen-0.7.3 erl_interface-3.10.2.2 et-1.6.1 eunit-2.3.5 hipe-3.17.1 ic-4.4.4.2 inets-6.5.2.4 jinterface-1.8.1 kernel-5.4.3.2 megaco-3.18.3 mnesia-4.15.3.2 observer-2.7 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.5 sasl-3.1.2 snmp-5.2.11.1 ssh-4.6.9.4 ssl-8.2.6.4 stdlib-3.4.5.1 syntax_tools-2.1.4.1 wx-1.8.3 xmerl-1.3.16.1 : OTP-20.3.8.21 : common_test-1.15.4.2 erl_interface-3.10.2.2 erts-9.3.3.10 snmp-5.2.11.1 ssh-4.6.9.4 tools-2.11.2.1 # asn1-5.0.5.2 compiler-7.1.5.2 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2.2.2 debugger-4.2.4 dialyzer-3.2.4 diameter-2.1.4.1 edoc-0.9.2 eldap-1.2.3.1 erl_docgen-0.7.3 et-1.6.1 eunit-2.3.5 hipe-3.17.1 ic-4.4.4.2 inets-6.5.2.4 jinterface-1.8.1 kernel-5.4.3.2 megaco-3.18.3 mnesia-4.15.3.2 observer-2.7 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.5 sasl-3.1.2 ssl-8.2.6.4 stdlib-3.4.5.1 syntax_tools-2.1.4.1 wx-1.8.3 xmerl-1.3.16.1 : OTP-20.3.8.20 : common_test-1.15.4.1 # asn1-5.0.5.2 compiler-7.1.5.2 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2.2.2 debugger-4.2.4 dialyzer-3.2.4 diameter-2.1.4.1 edoc-0.9.2 eldap-1.2.3.1 erl_docgen-0.7.3 erl_interface-3.10.2.1 erts-9.3.3.9 et-1.6.1 eunit-2.3.5 hipe-3.17.1 ic-4.4.4.2 inets-6.5.2.4 jinterface-1.8.1 kernel-5.4.3.2 megaco-3.18.3 mnesia-4.15.3.2 observer-2.7 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.5 sasl-3.1.2 snmp-5.2.11 ssh-4.6.9.3 ssl-8.2.6.4 stdlib-3.4.5.1 syntax_tools-2.1.4.1 tools-2.11.2 wx-1.8.3 xmerl-1.3.16.1 : OTP-20.3.8.19 : diameter-2.1.4.1 erts-9.3.3.9 # asn1-5.0.5.2 common_test-1.15.4 compiler-7.1.5.2 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2.2.2 debugger-4.2.4 dialyzer-3.2.4 edoc-0.9.2 eldap-1.2.3.1 erl_docgen-0.7.3 erl_interface-3.10.2.1 et-1.6.1 eunit-2.3.5 hipe-3.17.1 ic-4.4.4.2 inets-6.5.2.4 jinterface-1.8.1 kernel-5.4.3.2 megaco-3.18.3 mnesia-4.15.3.2 observer-2.7 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.5 sasl-3.1.2 snmp-5.2.11 ssh-4.6.9.3 ssl-8.2.6.4 stdlib-3.4.5.1 syntax_tools-2.1.4.1 tools-2.11.2 wx-1.8.3 xmerl-1.3.16.1 : diff --git a/system/COPYRIGHT b/system/COPYRIGHT index 91cf0bbfb3..57ea16e95c 100644 --- a/system/COPYRIGHT +++ b/system/COPYRIGHT @@ -62,7 +62,7 @@ Email domain: cam.ac.uk University of Cambridge Computing Service, Cambridge, England. -Copyright (c) 1997-2018 University of Cambridge +Copyright (c) 1997-2019 University of Cambridge All rights reserved. @@ -71,9 +71,9 @@ PCRE JUST-IN-TIME COMPILATION SUPPORT Written by: Zoltan Herczeg Email local part: hzmester -Emain domain: freemail.hu +Email domain: freemail.hu -Copyright(c) 2010-2018 Zoltan Herczeg +Copyright(c) 2010-2019 Zoltan Herczeg All rights reserved. @@ -82,9 +82,9 @@ STACK-LESS JUST-IN-TIME COMPILER Written by: Zoltan Herczeg Email local part: hzmester -Emain domain: freemail.hu +Email domain: freemail.hu -Copyright(c) 2009-2018 Zoltan Herczeg +Copyright(c) 2009-2019 Zoltan Herczeg All rights reserved. diff --git a/system/doc/efficiency_guide/commoncaveats.xml b/system/doc/efficiency_guide/commoncaveats.xml index 7b2128d888..7b00fa5d63 100644 --- a/system/doc/efficiency_guide/commoncaveats.xml +++ b/system/doc/efficiency_guide/commoncaveats.xml @@ -62,7 +62,7 @@ dangerous in a system that runs continuously. If only certain well-defined atoms are allowed as input, <seealso marker="erts:erlang#list_to_existing_atom/1">list_to_existing_atom/1</seealso> - can be used to + can be used to guard against a denial-of-service attack. (All atoms that are allowed must have been created earlier, for example, by simply using all of them in a module and loading that module.)</p> diff --git a/system/doc/reference_manual/typespec.xml b/system/doc/reference_manual/typespec.xml index 27cd0ba83d..f517259a64 100644 --- a/system/doc/reference_manual/typespec.xml +++ b/system/doc/reference_manual/typespec.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2003</year><year>2018</year> + <year>2003</year><year>2019</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -140,8 +140,7 @@ | nonempty_improper_list(Type1, Type2) %% Type1 and Type2 as above | nonempty_list(Type) %% Proper non-empty list - Map :: map() %% denotes a map of any size - | #{} %% denotes the empty map + Map :: #{} %% denotes the empty map | #{AssociationList} Tuple :: tuple() %% denotes a tuple of any size @@ -192,17 +191,16 @@ <c>AssociationList</c> are allowed to overlap, and if they do, the leftmost association takes precedence. A map association has a key in <c>AssociationList</c> if it belongs to this type. - <c>AssociationList</c> can contain both mandatory and optional - association types. + <c>AssociationList</c> can contain both mandatory <c>(:=)</c> and + optional <c>(=>)</c> association types. If an association type is mandatory, an association with that type - is to be present. + needs to be present. In the case of an optional association type it is not required for the key type to be present. </p> <p> - Notice that the syntactic representation of <c>map()</c> is - <c>#{any() => any()}</c> (or <c>#{_ => _}</c>), not <c>#{}</c>. The notation <c>#{}</c> specifies the singleton type for the empty map. + Note that this notation is not a shorthand for the <c>map()</c> type. </p> <p> For convenience, the following types are also built-in. @@ -259,6 +257,9 @@ <cell><c>iolist()</c></cell><cell><c>maybe_improper_list(byte() | binary() | iolist(), binary() | [])</c></cell> </row> <row> + <cell><c>map()</c></cell><cell><c>#{any() => any()}</c></cell> + </row> + <row> <cell><c>function()</c></cell><cell><c>fun()</c></cell> </row> <row> diff --git a/system/doc/system_principles/system_principles.xml b/system/doc/system_principles/system_principles.xml index 500522d778..6986d93ed9 100644 --- a/system/doc/system_principles/system_principles.xml +++ b/system/doc/system_principles/system_principles.xml @@ -156,7 +156,8 @@ init:stop()</pre> command-line flag <c>-mode</c>.</p> <pre> % <input>erl -mode embedded</input></pre> - <p>Default mode is <c>interactive</c>.</p> + <p>Default mode is <c>interactive</c> and extra <c>-mode</c> flags are + ignored.</p> <p>The mode properties are as follows:</p> <list type="bulleted"> <item>In embedded mode, all code is loaded during system startup |