diff options
Diffstat (limited to 'lib/stdlib')
49 files changed, 1459 insertions, 461 deletions
| diff --git a/lib/stdlib/doc/src/dict.xml b/lib/stdlib/doc/src/dict.xml index 942fd1f45e..0771682a25 100644 --- a/lib/stdlib/doc/src/dict.xml +++ b/lib/stdlib/doc/src/dict.xml @@ -121,7 +121,7 @@            <c><anno>Dict</anno></c> together with an extra argument <c>Acc</c>            (short for accumulator). <c><anno>Fun</anno></c> must return a new            accumulator which is passed to the next call. <c><anno>Acc0</anno></c> is -          returned if the list is empty. The evaluation order is +          returned if the dict is empty. The evaluation order is            undefined.</p>        </desc>      </func> diff --git a/lib/stdlib/doc/src/erl_tar.xml b/lib/stdlib/doc/src/erl_tar.xml index 7f25f5b7bc..95eefb8f9b 100644 --- a/lib/stdlib/doc/src/erl_tar.xml +++ b/lib/stdlib/doc/src/erl_tar.xml @@ -80,6 +80,12 @@    </section>    <section> +    <title>OTHER STORAGE MEDIA</title> +    <p>The <c>erl_ftp</c> module normally accesses the tar-file on disk using the <seealso marker="kernel:file">file module</seealso>.  When other needs arise, there is a way to define your own low-level Erlang functions to perform the writing and reading on the storage media. See <seealso marker="#init/3">init/3</seealso> for usage.</p> +    <p>An example of this is the sftp support in <seealso marker="ssh:ssh_sftp#open_tar/3">ssh_sftp:open_tar/3</seealso>. That function opens a tar file on a remote machine using an sftp channel.</p> +  </section> + +  <section>      <title>LIMITATIONS</title>      <p>For maximum compatibility, it is safe to archive files with names        up to 100 characters in length. Such tar files can generally be @@ -99,7 +105,8 @@          <v>TarDescriptor = term()</v>          <v>Filename = filename()</v>          <v>Options = [Option]</v> -        <v>Option = dereference|verbose</v> +        <v>Option = dereference|verbose|{chunks,ChunkSize}</v> +	<v>ChunkSize = positive_integer()</v>          <v>RetValue = ok|{error,{Filename,Reason}}</v>          <v>Reason = term()</v>        </type> @@ -119,6 +126,12 @@            <item>              <p>Print an informational message about the file being added.</p>            </item> +	  <tag><c>{chunks,ChunkSize}</c></tag> +	  <item> +	    <p>Read data in parts from the file. This is intended for memory-limited +	    machines that for example builds a tar file on a remote machine over +	    <seealso marker="ssh:ssh_sftp#open_tar/3">sftp</seealso>.</p> +	  </item>          </taglist>        </desc>      </func> @@ -389,6 +402,101 @@          </warning>        </desc>      </func> + +    <func> +      <name>init(UserPrivate, AccessMode, Fun) -> {ok,TarDescriptor} | {error,Reason} +</name> +      <fsummary>Creates a TarDescriptor used in subsequent tar operations when +      defining own low-level storage access functions +      </fsummary> +      <type> +	<v>UserPrivate = term()</v> +	<v>AccessMode = [write] | [read]</v> +	<v>Fun when AccessMode is [write] = fun(write,   {UserPrivate,DataToWrite})->...; +	                                       (position,{UserPrivate,Position})->...; +	                                       (close,   UserPrivate)->... +					    end +        </v> +	<v>Fun when AccessMode is [read] = fun(read2,   {UserPrivate,Size})->...; +	                                      (position,{UserPrivate,Position})->...; +	                                      (close,   UserPrivate)->... +					    end +        </v> +	<v>TarDescriptor = term()</v> +	<v>Reason = term()</v> +      </type> +      <desc> +	<p>The <c>Fun</c> is the definition of what to do when the different +	storage operations functions are to be called from the higher tar +	handling functions (<c>add/3</c>, <c>add/4</c>, <c>close/1</c>...). +	</p> +	<p>The <c>Fun</c> will be called when the tar function wants to do +	a low-level operation, like writing a block to a file.  The Fun is called +	as <c>Fun(Op,{UserPrivate,Parameters...})</c> where <c>Op</c> is the operation name, +	<c>UserPrivate</c> is the term passed as the first argument to <c>init/1</c> and +	<c>Parameters...</c> are the data added by the tar function to be passed down to +	the storage handling function. +	</p> +	<p>The parameter <c>UserPrivate</c> is typically the result of opening a low level +	structure like a file descriptor, a sftp channel id or such.  The different <c>Fun</c> +	clauses operates on that very term. +	</p> +	<p>The fun clauses parameter lists are: +	<taglist> +	  <tag><c>(write, {UserPrivate,DataToWrite})</c></tag> +	  <item>Write the term <c>DataToWrite</c> using <c>UserPrivate</c></item> +	  <tag><c>(close, UserPrivate)</c></tag> +	  <item>Close the access.</item> +	  <tag><c>(read2, {UserPrivate,Size})</c></tag> +	  <item>Read using <c>UserPrivate</c> but only <c>Size</c> bytes. Note that there is +	  only an arity-2 read function, not an arity-1 +	  </item> +	  <tag><c> (position,{UserPrivate,Position})</c></tag> +	  <item>Sets the position of <c>UserPrivate</c> as defined for files in <seealso marker="kernel:file#position-2">file:position/2</seealso></item> +	  <tag><c></c></tag> +	  <item></item> +	</taglist> +	</p> +	<p>A complete <c>Fun</c> parameter for reading and writing on files using the +	<seealso marker="kernel:file">file module</seealso> could be: +	</p> +	<code type="none"> +	  ExampleFun =  +	     fun(write, {Fd,Data}) ->  file:write(Fd, Data); +	        (position, {Fd,Pos}) -> file:position(Fd, Pos); +	        (read2, {Fd,Size}) -> file:read(Fd,Size); +	        (close, Fd) -> file:close(Fd) +	     end +	</code> +	<p>where <c>Fd</c> was given to the <c>init/3</c> function as:</p> +	<code> +	  {ok,Fd} = file:open(Name,...). +	  {ok,TarDesc} = erl_tar:init(Fd, [write], ExampleFun), +	</code> +	<p>The <c>TarDesc</c> is then used:</p> +	<code> +	  erl_tar:add(TarDesc, SomeValueIwantToAdd, FileNameInTarFile), +	  ...., +	  erl_tar:close(TarDesc) +	</code> +	<p>When the erl_tar core wants to e.g. write a piece of Data, it would call +	<c>ExampleFun(write,{UserPrivate,Data})</c>. +	</p> +	<note> +	  <p>The example above with <c>file</c> module operations is not necessary to +	  use directly since that is what the <seealso marker="#open">open</seealso> function +	  in principle does. +	  </p> +	</note> +        <warning> +          <p>The <c>TarDescriptor</c> term is not a file descriptor. +            You should not rely on the specific contents of the <c>TarDescriptor</c> +            term, as it may change in future versions as more features are added +            to the <c>erl_tar</c> module.</p> +        </warning> +      </desc> +    </func> +      <func>        <name>table(Name) -> RetValue</name>        <fsummary>Retrieve the name of all files in a tar file</fsummary> diff --git a/lib/stdlib/doc/src/gen_fsm.xml b/lib/stdlib/doc/src/gen_fsm.xml index 1713367bd8..848d57f3e6 100644 --- a/lib/stdlib/doc/src/gen_fsm.xml +++ b/lib/stdlib/doc/src/gen_fsm.xml @@ -115,7 +115,7 @@ gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4            If <c>FsmName={global,GlobalName}</c>, the gen_fsm is            registered globally as <c>GlobalName</c> using            <c>global:register_name/2</c>. -	  If <c>EventMgrName={via,Module,ViaName}</c>, the event manager will +	  If <c>FsmName={via,Module,ViaName}</c>, the gen_fsm will  	  register with the registry represented by <c>Module</c>.  	  The <c>Module</c> callback should export the functions  	  <c>register_name/2</c>, <c>unregister_name/1</c>, @@ -210,7 +210,7 @@ gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4  	  registered at another node, or</item>  	  <item><c>{global,GlobalName}</c>, if the gen_fsm is globally  	  registered.</item> -	  <item><c>{via,Module,ViaName}</c>, if the event manager is registered +	  <item><c>{via,Module,ViaName}</c>, if the gen_fsm is registered  	  through an alternative process registry.</item>          </list>          <p><c>Event</c> is an arbitrary term which is passed as one of diff --git a/lib/stdlib/doc/src/gen_server.xml b/lib/stdlib/doc/src/gen_server.xml index 4c83fde237..62c0394479 100644 --- a/lib/stdlib/doc/src/gen_server.xml +++ b/lib/stdlib/doc/src/gen_server.xml @@ -113,7 +113,7 @@ gen_server:abcast     -----> Module:handle_cast/2            registered globally as <c>GlobalName</c> using            <c>global:register_name/2</c>. If no name is provided,            the gen_server is not registered. -	  If <c>EventMgrName={via,Module,ViaName}</c>, the event manager will +	  If <c>ServerName={via,Module,ViaName}</c>, the gen_server will  	  register with the registry represented by <c>Module</c>.  	  The <c>Module</c> callback should export the functions  	  <c>register_name/2</c>, <c>unregister_name/1</c>, diff --git a/lib/stdlib/doc/src/io_protocol.xml b/lib/stdlib/doc/src/io_protocol.xml index 9328704e11..21da404c35 100644 --- a/lib/stdlib/doc/src/io_protocol.xml +++ b/lib/stdlib/doc/src/io_protocol.xml @@ -49,7 +49,7 @@ current I/O-protocol.</p>  <p>The original I/O-protocol was simple and flexible. Demands for spacial  and execution time efficiency has triggered extensions to the protocol  over the years, making the protocol larger and somewhat less easy to -implement than the original. It can certainly be argumented that the +implement than the original. It can certainly be argued that the  current protocol is too complex, but this text describes how it looks  today, not how it should have looked.</p> @@ -76,10 +76,11 @@ the server eventually sends a corresponding <c>io_reply</c> tuple.</p>  the I/O server sends the IO reply to.</item>  <item><c>ReplyAs</c> can be any datum and is returned in the corresponding -<c>io_reply</c>. The <seealso marker="stdlib:io">io</seealso> module simply uses the pid() -of the I/O server as the <c>ReplyAs</c> datum, but a more complicated client +<c>io_reply</c>. The <seealso marker="stdlib:io">io</seealso> module monitors +the I/O server, and uses the monitor reference as the <c>ReplyAs</c> datum. +A more complicated client  could have several outstanding I/O requests to the same I/O server and -would then use i.e. a <c>reference()</c> or something else to differentiate among +would then use different references (or something else) to differentiate among  the incoming IO replies. The <c>ReplyAs</c> element should be considered  opaque by the I/O server. Note that the <c>pid()</c> of the I/O server is not  explicitly present in the <c>io_reply</c> tuple. The reply can be sent from any diff --git a/lib/stdlib/doc/src/maps.xml b/lib/stdlib/doc/src/maps.xml index b37f7fd7fd..f766c843be 100644 --- a/lib/stdlib/doc/src/maps.xml +++ b/lib/stdlib/doc/src/maps.xml @@ -319,6 +319,23 @@ false</code>  		</func>  		<func> +			<name name="with" arity="2"/> +			<fsummary></fsummary> +			<desc> +				<p> +					Returns a new map <c><anno>Map2</anno></c> with the keys <c>K1</c> through <c>Kn</c> and their associated values from map <c><anno>Map1</anno></c>. +					Any key in <c><anno>Ks</anno></c> that does not exist in <c><anno>Map1</anno></c> are ignored. +				</p> +				<p>Example:</p> +				<code type="none"> +> Map = #{42 => value_three,1337 => "value two","a" => 1}, +  Ks = ["a",42,"other key"], +  maps:with(Ks,Map). +#{42 => value_three,"a" => 1}</code> +			</desc> +		</func> + +		<func>  			<name name="without" arity="2"/>  			<fsummary></fsummary>  			<desc> diff --git a/lib/stdlib/doc/src/notes.xml b/lib/stdlib/doc/src/notes.xml index 15e6fdfa9f..8582bfc9f9 100644 --- a/lib/stdlib/doc/src/notes.xml +++ b/lib/stdlib/doc/src/notes.xml @@ -30,6 +30,282 @@    </header>    <p>This document describes the changes made to the STDLIB application.</p> +<section><title>STDLIB 2.3</title> + +    <section><title>Fixed Bugs and Malfunctions</title> +      <list> +        <item> +          <p> +	    The documentation of string:tokens/2 now explicitly +	    specifies that adjacent separator characters do not give +	    any empty strings in the resulting list of tokens.</p> +          <p> +	    Own Id: OTP-12036</p> +        </item> +        <item> +          <p> +	    Fix broken deprecation warnings in ssh application</p> +          <p> +	    Own Id: OTP-12187</p> +        </item> +        <item> +          <p> +	    Maps: Properly align union typed assoc values in +	    documentation</p> +          <p> +	    Own Id: OTP-12190</p> +        </item> +        <item> +          <p> +	    Fix filelib:wildcard/2 when 'Cwd' ends with a dot</p> +          <p> +	    Own Id: OTP-12212</p> +        </item> +        <item> +          <p> +	    Allow <c>Name/Arity</c> syntax in maps values inside +	    attributes.</p> +          <p> +	    Own Id: OTP-12213</p> +        </item> +        <item> +          <p> +	    Fix edlin to correctly save text killed with ctrl-u. +	    Prior to this fix, entering text into the Erlang shell +	    and then killing it with ctrl-u followed by yanking it +	    back with ctrl-y would result in the yanked text being +	    the reverse of the original killed text.</p> +          <p> +	    Own Id: OTP-12224</p> +        </item> +        <item> +          <p> +	    If a callback function was terminated with exit/1, there +	    would be no stack trace in the ERROR REPORT produced by +	    gen_server. This has been corrected.</p> +          <p> +	    To keep the backwards compatibility, the actual exit +	    reason for the process is not changed.</p> +          <p> +	    Own Id: OTP-12263 Aux Id: seq12733 </p> +        </item> +        <item> +          <p> +	    Warnings produced by <c>ms_transform</c> could point out +	    the wrong line number.</p> +          <p> +	    Own Id: OTP-12264</p> +        </item> +      </list> +    </section> + + +    <section><title>Improvements and New Features</title> +      <list> +        <item> +          <p> +	    Supports tar file creation on other media than file +	    systems mounted on the local machine.</p> +          <p> +	    The <c>erl_tar</c> api is extended with +	    <c>erl_tar:init/3</c> that enables usage of user provided +	    media storage routines. A ssh-specific set of such +	    routines is hidden in the new function +	    <c>ssh_sftp:open_tar/3</c> to simplify creating a tar +	    archive on a remote ssh server.</p> +          <p> +	    A chunked file reading option is added to +	    <c>erl_tar:add/3,4</c> to save memory on e.g small +	    embedded systems. The size of the slices read from a file +	    in that case can be specified.</p> +          <p> +	    Own Id: OTP-12180 Aux Id: seq12715 </p> +        </item> +        <item> +          <p> +	    I/O requests are optimized for long message queues in the +	    calling process.</p> +          <p> +	    Own Id: OTP-12315</p> +        </item> +      </list> +    </section> + +</section> + +<section><title>STDLIB 2.2</title> + +    <section><title>Fixed Bugs and Malfunctions</title> +      <list> +        <item> +          <p> +	    The type spec of the FormFunc argument to +	    sys:handle_debug/4 was erroneously pointing to dbg_fun(). +	    This is now corrected and the new type is format_fun().</p> +          <p> +	    Own Id: OTP-11800</p> +        </item> +        <item> +          <p> +	    Behaviors such as gen_fsm and gen_server should always +	    invoke format_status/2 before printing the state to the +	    logs.</p> +          <p> +	    Own Id: OTP-11967</p> +        </item> +        <item> +	    <p> The documentation of <c>dets:insert_new/2</c> has +	    been corrected. (Thanks to Alexei Sholik for reporting +	    the bug.) </p> +          <p> +	    Own Id: OTP-12024</p> +        </item> +        <item> +          <p> +	    Printing a term with io_lib:format and control sequence +	    w, precision P and field width F, where F< P would +	    fail in one of the two following ways:</p> +          <p> +	    1) If P < printed length of the term, an infinite loop +	    would be entered, consuming all available memory.</p> +          <p> +	    2) If P >= printed length of the term, an exception +	    would be raised.</p> +          <p> +	    These two problems are now corrected.</p> +          <p> +	    Own Id: OTP-12041</p> +        </item> +        <item> +          <p> +	    The documentation of <c>maps:values/1</c> has been +	    corrected.</p> +          <p> +	    Own Id: OTP-12055</p> +        </item> +        <item> +          <p> +	    Expand shell functions in map expressions.</p> +          <p> +	    Own Id: OTP-12063</p> +        </item> +      </list> +    </section> + + +    <section><title>Improvements and New Features</title> +      <list> +        <item> +          <p> +	    Add maps:with/2</p> +          <p> +	    Own Id: OTP-12137</p> +        </item> +      </list> +    </section> + +</section> + +<section><title>STDLIB 2.1.1</title> + +    <section><title>Fixed Bugs and Malfunctions</title> +      <list> +        <item> +          <p> +	    OTP-11850 fixed filelib:wildcard/1 to work with broken +	    symlinks. This correction, however, introduced problems +	    since symlinks were no longer followed for functions like +	    filelib:ensure_dir/1, filelib:is_dir/1, +	    filelib:file_size/1, etc. This is now corrected.</p> +          <p> +	    Own Id: OTP-12054 Aux Id: seq12660 </p> +        </item> +      </list> +    </section> + +</section> + +<section><title>STDLIB 2.1</title> + +    <section><title>Fixed Bugs and Malfunctions</title> +      <list> +        <item> +	    <p><c>filelib:wildcard("broken_symlink")</c> would return +	    an empty list if "broken_symlink" was a symlink that did +	    not point to an existing file.</p> +          <p> +	    Own Id: OTP-11850 Aux Id: seq12571 </p> +        </item> +        <item> +	    <p><c>erl_tar</c> can now handle files names that contain +	    Unicode characters. See "UNICODE SUPPORT" in the +	    documentation for <c>erl_tar</c>.</p> +	    <p>When creating a tar file, <c>erl_tar</c> would +	    sometime write a too short end of tape marker. GNU tar +	    would correctly extract files from such tar file, but +	    would complain about "A lone zero block at...".</p> +          <p> +	    Own Id: OTP-11854</p> +        </item> +        <item> +	    <p> When redefining and exporting the type <c>map()</c> +	    the Erlang Code Linter (<c>erl_lint</c>) erroneously +	    emitted an error. This bug has been fixed. </p> +          <p> +	    Own Id: OTP-11872</p> +        </item> +        <item> +          <p> +	    Fix evaluation of map updates in the debugger and +	    erl_eval</p> +          <p> +	    Reported-by: José Valim</p> +          <p> +	    Own Id: OTP-11922</p> +        </item> +      </list> +    </section> + + +    <section><title>Improvements and New Features</title> +      <list> +        <item> +	    <p>The following native functions now bump an appropriate +	    amount of reductions and yield when out of +	    reductions:</p> <list> +	    <item><c>erlang:binary_to_list/1</c></item> +	    <item><c>erlang:binary_to_list/3</c></item> +	    <item><c>erlang:bitstring_to_list/1</c></item> +	    <item><c>erlang:list_to_binary/1</c></item> +	    <item><c>erlang:iolist_to_binary/1</c></item> +	    <item><c>erlang:list_to_bitstring/1</c></item> +	    <item><c>binary:list_to_bin/1</c></item> </list> +	    <p>Characteristics impact:</p> <taglist> +	    <tag>Performance</tag> <item>The functions converting +	    from lists got a performance loss for very small lists, +	    and a performance gain for very large lists.</item> +	    <tag>Priority</tag> <item>Previously a process executing +	    one of these functions effectively got an unfair priority +	    boost. This priority boost depended on the input size. +	    The larger the input was, the larger the priority boost +	    got. This unfair priority boost is now lost. </item> +	    </taglist> +          <p> +	    Own Id: OTP-11888</p> +        </item> +        <item> +          <p> +	    Add <c>maps:get/3</c> to maps module. The function will +	    return the supplied default value if the key does not +	    exist in the map.</p> +          <p> +	    Own Id: OTP-11951</p> +        </item> +      </list> +    </section> + +</section> +  <section><title>STDLIB 2.0</title>      <section><title>Fixed Bugs and Malfunctions</title> @@ -343,10 +619,10 @@  	    "hello"}, % add new associations</c></item> <item><c>#{  	    "hi" := V1, a := V2, b := V3} = M2. % match keys with  	    values</c></item> </taglist></p> -          <p> -	    For information on how to use Maps please see the -	    <seealso marker="doc/reference_manual:maps">Reference -	    Manual</seealso>.</p> +        <p> +	    For information on how to use Maps please see Map Expressions in the +		<seealso marker="doc/reference_manual:expressions#map_expressions"> +			Reference Manual</seealso>.</p>            <p>  	    The current implementation is without the following  	    features: <taglist> <item>No variable keys</item> @@ -2193,7 +2469,7 @@  	    platforms than before. If <c>configure</c> warns about no  	    atomic implementation available, try using the  	    <c>libatomic_ops</c> library. Use the <seealso -	    marker="doc/installation_guide:INSTALL#How-to-Build-and-Install-ErlangOTP_A-Closer-Look-at-the-individual-Steps_Configuring">--with-libatomic_ops=PATH</seealso> +	    marker="doc/installation_guide:INSTALL#Advanced-configuration-and-build-of-ErlangOTP">--with-libatomic_ops=PATH</seealso>  	    <c>configure</c> command line argument when specifying  	    where the <c>libatomic_ops</c> installation is located.  	    The <c>libatomic_ops</c> library can be downloaded from: @@ -2211,7 +2487,7 @@  	    the pentium 4 processor. If you want the runtime system  	    to be compatible with older processors (back to 486) you  	    need to pass the <seealso -	    marker="doc/installation_guide:INSTALL#How-to-Build-and-Install-ErlangOTP_A-Closer-Look-at-the-individual-Steps_Configuring">--enable-ethread-pre-pentium4-compatibility</seealso> +	    marker="doc/installation_guide:INSTALL#Advanced-configuration-and-build-of-ErlangOTP">--enable-ethread-pre-pentium4-compatibility</seealso>  	    <c>configure</c> command line argument when configuring  	    the system.</p>            <p> diff --git a/lib/stdlib/doc/src/string.xml b/lib/stdlib/doc/src/string.xml index c96cc95a44..b05d5cbc08 100644 --- a/lib/stdlib/doc/src/string.xml +++ b/lib/stdlib/doc/src/string.xml @@ -4,7 +4,7 @@  <erlref>    <header>      <copyright> -      <year>1996</year><year>2013</year> +      <year>1996</year><year>2014</year>        <holder>Ericsson AB. All Rights Reserved.</holder>      </copyright>      <legalnotice> @@ -124,6 +124,10 @@          <code type="none">  > tokens("abc defxxghix jkl", "x ").  ["abc", "def", "ghi", "jkl"]        </code> +	<p>Note that, as shown in the example above, two or more +	adjacent separator characters in <c><anno>String</anno></c> +	will be treated as one. That is, there will not be any empty +	strings in the resulting list of tokens.</p>        </desc>      </func>      <func> diff --git a/lib/stdlib/doc/src/sys.xml b/lib/stdlib/doc/src/sys.xml index a46fa1289f..19605f325b 100644 --- a/lib/stdlib/doc/src/sys.xml +++ b/lib/stdlib/doc/src/sys.xml @@ -4,7 +4,7 @@  <erlref>    <header>      <copyright> -      <year>1996</year><year>2013</year> +      <year>1996</year><year>2014</year>        <holder>Ericsson AB. All Rights Reserved.</holder>      </copyright>      <legalnotice> @@ -115,6 +115,9 @@      <datatype>        <name name="dbg_fun"/>      </datatype> +    <datatype> +      <name name="format_fun"/> +    </datatype>    </datatypes>    <funcs>      <func> diff --git a/lib/stdlib/doc/src/unicode_usage.xml b/lib/stdlib/doc/src/unicode_usage.xml index bebfbd4514..29b8940c62 100644 --- a/lib/stdlib/doc/src/unicode_usage.xml +++ b/lib/stdlib/doc/src/unicode_usage.xml @@ -50,12 +50,8 @@    encoded files in several circumstances. Most notable is the support    for UTF-8 in files read by <c>file:consult/1</c>, release handler support    for UTF-8 and more support for Unicode character sets in the -  I/O-system.</p> - -  <p>In Erlang/OTP 17.0, the encoding default for Erlang source files was -  switched to UTF-8 and in Erlang/OTP 18.0 Erlang will support atoms in the full -  Unicode range, meaning full Unicode function and module -  names</p> +  I/O-system. In Erlang/OTP 17.0, the encoding default for Erlang source files was +  switched to UTF-8.</p>    <p>This guide outlines the current Unicode support and gives a couple    of recipes for working with Unicode data.</p> @@ -289,8 +285,8 @@      <tag>The language</tag>      <item>Having the source code in UTF-8 also allows you to write      string literals containing Unicode characters with code points > -    255, although atoms, module names and function names will be -    restricted to the ISO-Latin-1 range until the Erlang/OTP 18.0 release. Binary +    255, although atoms, module names and function names are +    restricted to the ISO-Latin-1 range. Binary      literals where you use the <c>/utf8</c> type, can also be      expressed using Unicode characters > 255. Having module names      using characters other than 7-bit ASCII can cause trouble on @@ -385,8 +381,7 @@ external_charlist() = maybe_improper_list(char() |    using characters from the ISO-latin-1 character set and atoms are    restricted to the same ISO-latin-1 range. These restrictions in the    language are of course independent of the encoding of the source -  file. Erlang/OTP 18.0 is expected to handle functions named in -  Unicode as well as Unicode atoms.</p> +  file.</p>    <section>      <title>Bit-syntax</title>      <p>The bit-syntax contains types for coping with binary data in the diff --git a/lib/stdlib/src/binary.erl b/lib/stdlib/src/binary.erl index 4850a59eb6..8d07a356dd 100644 --- a/lib/stdlib/src/binary.erl +++ b/lib/stdlib/src/binary.erl @@ -89,9 +89,9 @@ copy(_, _) ->  decode_unsigned(_) ->      erlang:nif_error(undef). --spec decode_unsigned(Subject, Endianess) -> Unsigned when +-spec decode_unsigned(Subject, Endianness) -> Unsigned when        Subject :: binary(), -      Endianess :: big | little, +      Endianness :: big | little,        Unsigned :: non_neg_integer().  decode_unsigned(_, _) -> @@ -103,9 +103,9 @@ decode_unsigned(_, _) ->  encode_unsigned(_) ->      erlang:nif_error(undef). --spec encode_unsigned(Unsigned, Endianess) -> binary() when +-spec encode_unsigned(Unsigned, Endianness) -> binary() when        Unsigned :: non_neg_integer(), -      Endianess :: big | little. +      Endianness :: big | little.  encode_unsigned(_, _) ->      erlang:nif_error(undef). diff --git a/lib/stdlib/src/c.erl b/lib/stdlib/src/c.erl index c2256c0cf9..9860adf04d 100644 --- a/lib/stdlib/src/c.erl +++ b/lib/stdlib/src/c.erl @@ -509,9 +509,12 @@ m(M) ->      {exports,E} = lists:keyfind(exports, 1, L),      Time = get_compile_time(L),      COpts = get_compile_options(L), -    format("Module ~w compiled: ",[M]), print_time(Time), -    format("Compiler options:  ~p~n", [COpts]), +    format("Module: ~w~n", [M]), +    print_md5(L), +    format("Compiled: "), +    print_time(Time),      print_object_file(M), +    format("Compiler options:  ~p~n", [COpts]),      format("Exports: ~n",[]), print_exports(keysort(1, E)).  print_object_file(Mod) -> @@ -522,6 +525,12 @@ print_object_file(Mod) ->  	    ignore      end. +print_md5(L) -> +    case lists:keyfind(md5, 1, L) of +        {md5,<<MD5:128>>} -> io:format("MD5: ~.16b~n",[MD5]); +        _ -> ok +    end. +  get_compile_time(L) ->      case get_compile_info(L, time) of  	{ok,Val} -> Val; @@ -569,8 +578,8 @@ split_print_exports([{F1, A1}|T1], [{F2, A2} | T2]) ->  split_print_exports([], []) -> ok.  print_time({Year,Month,Day,Hour,Min,_Secs}) -> -    format("Date: ~s ~w ~w, ", [month(Month),Day,Year]), -    format("Time: ~.2.0w.~.2.0w~n", [Hour,Min]); +    format("~s ~w ~w, ", [month(Month),Day,Year]), +    format("~.2.0w:~.2.0w~n", [Hour,Min]);  print_time(notime) ->      format("No compile time info available~n",[]). diff --git a/lib/stdlib/src/dets.erl b/lib/stdlib/src/dets.erl index c32da1624f..a4bd45ea19 100644 --- a/lib/stdlib/src/dets.erl +++ b/lib/stdlib/src/dets.erl @@ -440,9 +440,10 @@ insert(Tab, Objs) when is_list(Objs) ->  insert(Tab, Obj) ->      badarg(treq(Tab, {insert, [Obj]}), [Tab, Obj]). --spec insert_new(Name, Objects) -> boolean() when +-spec insert_new(Name, Objects) -> boolean() | {'error', Reason} when        Name :: tab_name(), -      Objects :: object() | [object()]. +      Objects :: object() | [object()], +      Reason :: term().  insert_new(Tab, Objs) when is_list(Objs) ->      badarg(treq(Tab, {insert_new, Objs}), [Tab, Objs]); @@ -2838,17 +2839,22 @@ fsck_try(Fd, Tab, FH, Fname, SlotNumbers, Version) ->  tempfile(Fname) ->      Tmp = lists:concat([Fname, ".TMP"]), -    tempfile(Tmp, 10). - -tempfile(Tmp, 0) -> -    Tmp; -tempfile(Tmp, N) ->      case file:delete(Tmp) of -        {error, eacces} -> % 'dets_process_died' happened anyway... (W-nd-ws) -            timer:sleep(1000), -            tempfile(Tmp, N-1); -        _ -> -            Tmp +        {error, _Reason} -> % typically enoent +            ok; +        ok -> +            assure_no_file(Tmp) +    end, +    Tmp. + +assure_no_file(File) -> +    case file:read_file_info(File) of +        {ok, _FileInfo} -> +            %% Wait for some other process to close the file: +            timer:sleep(100), +            assure_no_file(File); +        {error, _} -> +            ok      end.  %% -> {ok, NewHead} | {try_again, integer()} | Error diff --git a/lib/stdlib/src/dets_server.erl b/lib/stdlib/src/dets_server.erl index 268c201047..3164d40f35 100644 --- a/lib/stdlib/src/dets_server.erl +++ b/lib/stdlib/src/dets_server.erl @@ -1,7 +1,7 @@  %%  %% %CopyrightBegin%  %%  -%% Copyright Ericsson AB 2001-2013. All Rights Reserved. +%% Copyright Ericsson AB 2001-2014. All Rights Reserved.  %%   %% The contents of this file are subject to the Erlang Public License,  %% Version 1.1, (the "License"); you may not use this file except in @@ -171,9 +171,15 @@ handle_info({pending_reply, {Ref, Result0}}, State) ->  		link(Pid),  		do_link(Store, FromPid),  		true = ets:insert(Store, {FromPid, Tab}), -		true = ets:insert(?REGISTRY, {Tab, 1, Pid}), -		true = ets:insert(?OWNERS, {Pid, Tab}), +                %% do_internal_open() has already done the following: +                %% true = ets:insert(?REGISTRY, {Tab, 1, Pid}), +                %% true = ets:insert(?OWNERS, {Pid, Tab}),  		{ok, Tab}; +            {Reply, internal_open} -> +                %% Clean up what do_internal_open() did: +                true = ets:delete(?REGISTRY, Tab), +                true = ets:delete(?OWNERS, Pid), +                Reply;  	    {Reply, _} -> % ok or Error  		Reply  	end, @@ -309,6 +315,12 @@ do_internal_open(State, From, Args) ->                        [T, _, _] -> T;                        [_, _] -> Ref                    end, +            %% Pretend the table is open. If someone else tries to +            %% open the file it will always become a pending +            %% 'add_user' request. If someone tries to use the table +            %% there will be a delay, but that is OK. +            true = ets:insert(?REGISTRY, {Tab, 1, Pid}), +            true = ets:insert(?OWNERS, {Pid, Tab}),              pending_call(Tab, Pid, Ref, From, Args, internal_open, State);          Error ->              {Error, State} diff --git a/lib/stdlib/src/edlin.erl b/lib/stdlib/src/edlin.erl index be9a4f5107..b3bc5f6d92 100644 --- a/lib/stdlib/src/edlin.erl +++ b/lib/stdlib/src/edlin.erl @@ -390,7 +390,7 @@ do_op(end_of_line, Bef, [C|Aft], Rs) ->  do_op(end_of_line, Bef, [], Rs) ->      {{Bef,[]},Rs};  do_op(ctlu, Bef, Aft, Rs) -> -    put(kill_buffer, Bef), +    put(kill_buffer, reverse(Bef)),      {{[], Aft}, [{delete_chars, -length(Bef)} | Rs]};  do_op(beep, Bef, Aft, Rs) ->      {{Bef,Aft},[beep|Rs]}; diff --git a/lib/stdlib/src/epp.erl b/lib/stdlib/src/epp.erl index 9b506b0a44..5f8637c118 100644 --- a/lib/stdlib/src/epp.erl +++ b/lib/stdlib/src/epp.erl @@ -1121,8 +1121,20 @@ skip_toks(From, St, [I|Sis]) ->  	    skip_toks(From, St#epp{location=Cl}, Sis);  	{ok,_Toks,Cl} ->  	    skip_toks(From, St#epp{location=Cl}, [I|Sis]); -	{error,_E,Cl} -> -	    skip_toks(From, St#epp{location=Cl}, [I|Sis]); +	{error,E,Cl} -> +	    case E of +		{_,file_io_server,invalid_unicode} -> +		    %% The compiler needs to know that there was +		    %% invalid unicode characters in the file +		    %% (and there is no point in continuing anyway +		    %% since io server process has terminated). +		    epp_reply(From, {error,E}), +		    leave_file(wait_request(St), St); +		_ -> +		    %% Some other invalid token, such as a bad floating +		    %% point number. Just ignore it. +		    skip_toks(From, St#epp{location=Cl}, [I|Sis]) +	    end;  	{eof,Cl} ->  	    leave_file(From, St#epp{location=Cl,istk=[I|Sis]});  	{error,_E} -> diff --git a/lib/stdlib/src/erl_eval.erl b/lib/stdlib/src/erl_eval.erl index 3cfedfee97..639ddfc214 100644 --- a/lib/stdlib/src/erl_eval.erl +++ b/lib/stdlib/src/erl_eval.erl @@ -77,7 +77,7 @@  %% Only exprs/2 checks the command by calling erl_lint. The reason is  %% that if there is a function handler present, then it is possible  %% that there are valid constructs in Expression to be taken care of -%% by a function handler but considerad errors by erl_lint. +%% by a function handler but considered errors by erl_lint.  -spec(exprs(Expressions, Bindings) -> {value, Value, NewBindings} when        Expressions :: expressions(), diff --git a/lib/stdlib/src/erl_expand_records.erl b/lib/stdlib/src/erl_expand_records.erl index 57e768ba9d..64a00acd88 100644 --- a/lib/stdlib/src/erl_expand_records.erl +++ b/lib/stdlib/src/erl_expand_records.erl @@ -1,7 +1,7 @@  %%  %% %CopyrightBegin%  %% -%% Copyright Ericsson AB 2005-2012. All Rights Reserved. +%% Copyright Ericsson AB 2005-2014. All Rights Reserved.  %%  %% The contents of this file are subject to the Erlang Public License,  %% Version 1.1, (the "License"); you may not use this file except in @@ -38,6 +38,8 @@                   checked_ra=[]        % successfully accessed records                  }). +-define(REC_OFFSET, 100000000). % A hundred millions. Also in v3_core. +  -spec(module(AbsForms, CompileOptions) -> AbsForms when        AbsForms :: [erl_parse:abstract_form()],        CompileOptions :: [compile:option()]). @@ -144,10 +146,11 @@ pattern({map_field_exact,Line,K0,V0}, St0) ->  %%    {{struct,Line,Tag,TPs},TPsvs,St1};  pattern({record_index,Line,Name,Field}, St) ->      {index_expr(Line, Field, Name, record_fields(Name, St)),St}; -pattern({record,Line,Name,Pfs}, St0) -> +pattern({record,Line0,Name,Pfs}, St0) ->      Fs = record_fields(Name, St0),      {TMs,St1} = pattern_list(pattern_fields(Fs, Pfs), St0), -    {{tuple,Line,[{atom,Line,Name} | TMs]},St1}; +    Line = record_offset(Line0, St1), +    {{tuple,Line,[{atom,Line0,Name} | TMs]},St1};  pattern({bin,Line,Es0}, St0) ->      {Es1,St1} = pattern_bin(Es0, St0),      {{bin,Line,Es1},St1}; @@ -329,8 +332,9 @@ expr({map_field_exact,Line,K0,V0}, St0) ->  expr({record_index,Line,Name,F}, St) ->      I = index_expr(Line, F, Name, record_fields(Name, St)),      expr(I, St); -expr({record,Line,Name,Is}, St) -> -    expr({tuple,Line,[{atom,Line,Name} |  +expr({record,Line0,Name,Is}, St) -> +    Line = record_offset(Line0, St), +    expr({tuple,Line,[{atom,Line0,Name} |                        record_inits(record_fields(Name, St), Is)]},           St);  expr({record_field,Line,R,Name,F}, St) -> @@ -582,8 +586,9 @@ strict_get_record_field(Line, R, {atom,_,F}=Index, Name, St0) ->              I = index_expr(F, Fs, 2),              P = record_pattern(2, I, Var, length(Fs)+1, Line, [{atom,Line,Name}]),              NLine = neg_line(Line), +            RLine = record_offset(NLine, St),  	    E = {'case',NLine,R, -		     [{clause,NLine,[{tuple,NLine,P}],[],[Var]}, +		     [{clause,NLine,[{tuple,RLine,P}],[],[Var]},  		      {clause,NLine,[{var,NLine,'_'}],[],  		       [{call,NLine,{remote,NLine,  				    {atom,NLine,erlang}, @@ -697,9 +702,10 @@ record_update(R, Name, Fs, Us0, St0) ->  record_match(R, Name, Lr, Fs, Us, St0) ->      {Ps,News,St1} = record_upd_fs(Fs, Us, St0),      NLr = neg_line(Lr), +    RLine = record_offset(Lr, St1),      {{'case',Lr,R, -      [{clause,Lr,[{tuple,Lr,[{atom,Lr,Name} | Ps]}],[], -        [{tuple,Lr,[{atom,Lr,Name} | News]}]}, +      [{clause,Lr,[{tuple,RLine,[{atom,Lr,Name} | Ps]}],[], +        [{tuple,RLine,[{atom,Lr,Name} | News]}]},         {clause,NLr,[{var,NLr,'_'}],[],          [call_error(NLr, {tuple,NLr,[{atom,NLr,badrecord},{atom,NLr,Name}]})]}        ]}, @@ -728,6 +734,10 @@ record_setel(R, Name, Fs, Us0) ->      Lr = element(2, hd(Us)),      Wildcards = duplicate(length(Fs), {var,Lr,'_'}),      NLr = neg_line(Lr), +    %% Note: calling record_offset() here is not necessary since it is +    %% targeted at Dialyzer which always calls the compiler with +    %% 'strict_record_updates' meaning that record_setel() will never +    %% be called.      {'case',Lr,R,       [{clause,Lr,[{tuple,Lr,[{atom,Lr,Name} | Wildcards]}],[],         [foldr(fun ({I,Lf,Val}, Acc) -> @@ -836,7 +846,7 @@ optimize_is_record(H0, G0, #exprec{compile=Opts}) ->  	[] ->  	    {H0,G0};  	Rs0 -> -	    case lists:member(no_is_record_optimization, Opts) of +	    case lists:member(dialyzer, Opts) of % no_is_record_optimization  		true ->  		    {H0,G0};  		false -> @@ -961,3 +971,10 @@ opt_remove_2(A, _) -> A.  neg_line(L) ->      erl_parse:set_line(L, fun(Line) -> -abs(Line) end). + +record_offset(L, St) -> +    case lists:member(dialyzer, St#exprec.compile) of +        true when L >= 0 -> L+?REC_OFFSET; +        true when L < 0  -> L-?REC_OFFSET; +        false -> L +    end. diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl index e1ae3b7aea..1d4a2a1fef 100644 --- a/lib/stdlib/src/erl_parse.yrl +++ b/lib/stdlib/src/erl_parse.yrl @@ -753,6 +753,9 @@ attribute_farity({cons,L,H,T}) ->  attribute_farity({tuple,L,Args0}) ->      Args = attribute_farity_list(Args0),      {tuple,L,Args}; +attribute_farity({map,L,Args0}) -> +    Args = attribute_farity_map(Args0), +    {map,L,Args};  attribute_farity({op,L,'/',{atom,_,_}=Name,{integer,_,_}=Arity}) ->      {tuple,L,[Name,Arity]};  attribute_farity(Other) -> Other. @@ -760,6 +763,10 @@ attribute_farity(Other) -> Other.  attribute_farity_list(Args) ->      [attribute_farity(A) || A <- Args]. +%% It is not meaningful to have farity keys. +attribute_farity_map(Args) -> +    [{Op,L,K,attribute_farity(V)} || {Op,L,K,V} <- Args]. +  -spec error_bad_decl(integer(), attributes()) -> no_return().  error_bad_decl(L, S) -> @@ -954,7 +961,9 @@ abstract([H|T], L, none=E) ->  abstract(List, L, E) when is_list(List) ->      abstract_list(List, [], L, E);  abstract(Tuple, L, E) when is_tuple(Tuple) -> -    {tuple,L,abstract_tuple_list(tuple_to_list(Tuple), L, E)}. +    {tuple,L,abstract_tuple_list(tuple_to_list(Tuple), L, E)}; +abstract(Map, L, E) when is_map(Map) -> +    {map,L,abstract_map_fields(maps:to_list(Map),L,E)}.  abstract_list([H|T], String, L, E) ->      case is_integer(H) andalso H >= 0 andalso E(H) of @@ -979,6 +988,9 @@ abstract_tuple_list([H|T], L, E) ->  abstract_tuple_list([], _L, _E) ->      []. +abstract_map_fields(Fs,L,E) -> +    [{map_field_assoc,L,abstract(K,L,E),abstract(V,L,E)}||{K,V}<-Fs]. +  abstract_byte(Byte, L) when is_integer(Byte) ->      {bin_element, L, {integer, L, Byte}, default, default};  abstract_byte(Bits, L) -> diff --git a/lib/stdlib/src/erl_pp.erl b/lib/stdlib/src/erl_pp.erl index 82bc2c1460..1fd6d2a8df 100644 --- a/lib/stdlib/src/erl_pp.erl +++ b/lib/stdlib/src/erl_pp.erl @@ -300,7 +300,15 @@ map_pair_types(Fs) ->      tuple_type(Fs, fun map_pair_type/1).  map_pair_type({type,_Line,map_field_assoc,Ktype,Vtype}) -> -    {seq,[],[]," =>",[ltype(Ktype),ltype(Vtype)]}. +    map_assoc_typed(ltype(Ktype), Vtype). + +map_assoc_typed(B, {type,_,union,Ts}) -> +    {first,[B,$\s],{seq,[],[],[],map_assoc_union_type(Ts)}}; +map_assoc_typed(B, Type) -> +    {list,[{cstep,[B," =>"],ltype(Type)}]}. + +map_assoc_union_type([T|Ts]) -> +    [[leaf("=> "),ltype(T)] | ltypes(Ts, fun union_elem/1)].  record_type(Name, Fields) ->      {first,[record_name(Name)],field_types(Fields)}. diff --git a/lib/stdlib/src/erl_scan.erl b/lib/stdlib/src/erl_scan.erl index ae59d5f44f..6fd6bb888b 100644 --- a/lib/stdlib/src/erl_scan.erl +++ b/lib/stdlib/src/erl_scan.erl @@ -1075,7 +1075,7 @@ scan_number([$#|Cs]=Cs0, St, Line, Col, Toks, Ncs0) ->      Ncs = lists:reverse(Ncs0),      case catch list_to_integer(Ncs) of          B when B >= 2, B =< 1+$Z-$A+10 -> -            Bcs = ?STR(St, Ncs++[$#]), +            Bcs = Ncs++[$#],              scan_based_int(Cs, St, Line, Col, Toks, {B,[],Bcs});          B ->              Len = length(Ncs), @@ -1108,7 +1108,7 @@ scan_based_int(Cs, St, Line, Col, Toks, {B,Ncs0,Bcs}) ->      Ncs = lists:reverse(Ncs0),      case catch erlang:list_to_integer(Ncs, B) of          N when is_integer(N) -> -            tok3(Cs, St, Line, Col, Toks, integer, ?STR(St, Bcs++Ncs), N); +            tok3(Cs, St, Line, Col, Toks, integer, Bcs++Ncs, N);          _ ->              Len = length(Bcs)+length(Ncs),              Ncol = incr_column(Col, Len), diff --git a/lib/stdlib/src/erl_tar.erl b/lib/stdlib/src/erl_tar.erl index acf7a5cd40..caa3276d09 100644 --- a/lib/stdlib/src/erl_tar.erl +++ b/lib/stdlib/src/erl_tar.erl @@ -22,7 +22,7 @@  %% Purpose: Unix tar (tape archive) utility.  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --export([create/2, create/3, extract/1, extract/2, table/1, table/2, +-export([init/3, create/2, create/3, extract/1, extract/2, table/1, table/2,  	 open/2, close/1, add/3, add/4,  	 t/1, tt/1, format_error/1]). @@ -30,10 +30,16 @@  -record(add_opts,  	{read_info,				% Fun to use for read file/link info. +	 chunk_size = 0,  % For file reading when sending to sftp. 0=do not chunk  	 verbose = false :: boolean()}).	% Verbose on/off.  %% Opens a tar archive. +init(UsrHandle, AccessMode, Fun) when is_function(Fun,2) ->    +    {ok, {AccessMode,{tar_descriptor,UsrHandle,Fun}}}. + +%%%================================================================		    +%%% The open function with friends is to keep the file and binary api of this module  open(Name, Mode) ->      case open_mode(Mode) of  	{ok, Access, Raw, Opts} -> @@ -46,27 +52,37 @@ open1({binary,Bin}, read, _Raw, Opts) ->      case file:open(Bin, [ram,binary,read]) of  	{ok,File} ->              _ = [ram_file:uncompress(File) || Opts =:= [compressed]], -	    {ok,{read,File}}; +	    init(File,read,file_fun());  	Error ->  	    Error      end;  open1({file, Fd}, read, _Raw, _Opts) -> -    {ok, {read, Fd}}; +    init(Fd, read, file_fun());  open1(Name, Access, Raw, Opts) ->      case file:open(Name, Raw ++ [binary, Access|Opts]) of  	{ok, File} -> -	    {ok, {Access, File}}; +	    init(File, Access, file_fun());  	{error, Reason} ->  	    {error, {Name, Reason}}      end. +file_fun() -> +    fun(write, {Fd,Data}) ->  file:write(Fd, Data); +       (position, {Fd,Pos}) -> file:position(Fd, Pos); +       (read2, {Fd,Size}) -> file:read(Fd,Size); +       (close, Fd) -> file:close(Fd) +    end. + +%%% End of file and binary api (except for open_mode/1 downwards +%%%================================================================		    +  %% Closes a tar archive.  close({read, File}) -> -    ok = file:close(File); +    ok = do_close(File);  close({write, File}) ->      PadResult = pad_file(File), -    ok = file:close(File), +    ok = do_close(File),      PadResult;  close(_) ->      {error, einval}. @@ -75,7 +91,6 @@ close(_) ->  add(File, Name, Options) ->      add(File, Name, Name, Options). -  add({write, File}, Name, NameInArchive, Options) ->      Opts = #add_opts{read_info=fun(F) -> file:read_link_info(F) end},      add1(File, Name, NameInArchive, add_opts(Options, Opts)); @@ -88,6 +103,8 @@ add_opts([dereference|T], Opts) ->      add_opts(T, Opts#add_opts{read_info=fun(F) -> file:read_file_info(F) end});  add_opts([verbose|T], Opts) ->      add_opts(T, Opts#add_opts{verbose=true}); +add_opts([{chunks,N}|T], Opts) -> +    add_opts(T, Opts#add_opts{chunk_size=N});  add_opts([_|T], Opts) ->      add_opts(T, Opts);  add_opts([], Opts) -> @@ -321,16 +338,46 @@ add1(TarFile, Name, NameInArchive, Opts) ->  	    {error, {Name, Reason}}      end. +add1(Tar, Name, Header, chunked, Options) -> +    add_verbose(Options, "a ~ts [chunked ", [Name]), +    try +	ok = do_write(Tar, Header), +	{ok,D} = file:open(Name, [read,binary]), +	{ok,NumBytes} = add_read_write_chunks(D, Tar, Options#add_opts.chunk_size, 0, Options), +	_ = file:close(D), +	ok = do_write(Tar, padding(NumBytes,?record_size)) +    of  +	ok -> +	    add_verbose(Options, "~n", []), +	    ok +    catch +	error:{badmatch,{error,Error}} -> +	    add_verbose(Options, "~n", []), +	    {error,{Name,Error}} +    end;  add1(Tar, Name, Header, Bin, Options) ->      add_verbose(Options, "a ~ts~n", [Name]), -    file:write(Tar, [Header, Bin, padding(byte_size(Bin), ?record_size)]). +    do_write(Tar, [Header, Bin, padding(byte_size(Bin), ?record_size)]). + +add_read_write_chunks(D, Tar, ChunkSize, SumNumBytes, Options) -> +    case file:read(D, ChunkSize) of +	{ok,Bin} ->  +	    ok = do_write(Tar, Bin), +	    add_verbose(Options, ".", []), +	    add_read_write_chunks(D, Tar, ChunkSize, SumNumBytes+byte_size(Bin), Options); +	eof -> +	    add_verbose(Options, "]", []), +	    {ok,SumNumBytes}; +	Other -> +	    Other +    end.  add_directory(TarFile, DirName, NameInArchive, Info, Options) ->      case file:list_dir(DirName) of  	{ok, []} ->  	    add_verbose(Options, "a ~ts~n", [DirName]),  	    Header = create_header(NameInArchive, Info), -	    file:write(TarFile, Header); +	    do_write(TarFile, Header);  	{ok, Files} ->  	    Add = fun (File) ->  			  add1(TarFile, @@ -396,7 +443,7 @@ to_string(Str0, Count) ->  %% Pads out end of file.  pad_file(File) -> -    {ok,Position} = file:position(File, {cur,0}), +    {ok,Position} = do_position(File, {cur,0}),      %% There must be at least two zero records at the end.      Fill = case ?block_size - (Position rem ?block_size) of  	       Fill0 when Fill0 < 2*?record_size -> @@ -407,7 +454,7 @@ pad_file(File) ->  		   %% Large enough.  		   Fill0  	   end, -    file:write(File, zeroes(Fill)). +    do_write(File, zeroes(Fill)).  split_filename(Name) when length(Name) =< ?th_name_len ->      {"", Name}; @@ -485,27 +532,36 @@ read_opts([_|Rest], Opts) ->  read_opts([], Opts) ->      Opts. +foldl_read({AccessMode,TD={tar_descriptor,_UsrHandle,_AccessFun}}, Fun, Accu, Opts) -> +    case AccessMode of +	read -> +	    foldl_read0(TD, Fun, Accu, Opts); +	_ -> +	    {error,{read_mode_expected,AccessMode}} +    end;  foldl_read(TarName, Fun, Accu, Opts) ->      case open(TarName, [read|Opts#read_opts.open_mode]) of  	{ok, {read, File}} -> -	    Result =  -		case catch foldl_read1(Fun, Accu, File, Opts) of -		    {'EXIT', Reason} -> -			exit(Reason); -		    {error, {Reason, Format, Args}} -> -			read_verbose(Opts, Format, Args), -			{error, Reason}; -		    {error, Reason} -> -			{error, Reason}; -		    Ok -> -			Ok -		end, -	    ok = file:close(File), +	    Result = foldl_read0(File, Fun, Accu, Opts), +	    ok = do_close(File),  	    Result;  	Error ->  	    Error      end. +foldl_read0(File, Fun, Accu, Opts) -> +    case catch foldl_read1(Fun, Accu, File, Opts) of +	{'EXIT', Reason} -> +	    exit(Reason); +	{error, {Reason, Format, Args}} -> +	    read_verbose(Opts, Format, Args), +	    {error, Reason}; +	{error, Reason} -> +	    {error, Reason}; +	Ok -> +	    Ok +    end. +  foldl_read1(Fun, Accu0, File, Opts) ->      case get_header(File) of  	eof -> @@ -559,7 +615,7 @@ check_extract(Name, #read_opts{files=Files}) ->      ordsets:is_element(Name, Files).  get_header(File) -> -    case file:read(File, ?record_size) of +    case do_read(File, ?record_size) of  	eof ->  	    throw({error,eof});  	{ok, Bin} when is_binary(Bin) -> @@ -690,7 +746,7 @@ get_element(File, #tar_header{size = 0}) ->      skip_to_next(File),      {ok,<<>>};  get_element(File, #tar_header{size = Size}) -> -    case file:read(File, Size) of +    case do_read(File, Size) of  	{ok,Bin}=Res when byte_size(Bin) =:= Size ->  	    skip_to_next(File),  	    Res; @@ -880,7 +936,7 @@ skip(File, Size) ->      %% Note: There is no point in handling failure to get the current position      %% in the file.  If it doesn't work, something serious is wrong.      Amount = ((Size + ?record_size - 1) div ?record_size) * ?record_size, -    {ok,_} = file:position(File, {cur, Amount}), +    {ok,_} = do_position(File, {cur, Amount}),      ok.  %% Skips to the next record in the file. @@ -888,9 +944,9 @@ skip(File, Size) ->  skip_to_next(File) ->      %% Note: There is no point in handling failure to get the current position      %% in the file.  If it doesn't work, something serious is wrong. -    {ok, Position} = file:position(File, {cur, 0}), +    {ok, Position} = do_position(File, {cur, 0}),      NewPosition = ((Position + ?record_size - 1) div ?record_size) * ?record_size, -    {ok,NewPosition} = file:position(File, NewPosition), +    {ok,NewPosition} = do_position(File, NewPosition),      ok.  %% Prints the message on if the verbose option is given. @@ -916,6 +972,9 @@ posix_to_erlang_time(Sec) ->  read_file_and_info(Name, Opts) ->      ReadInfo = Opts#add_opts.read_info,      case ReadInfo(Name) of +	{ok,Info} when Info#file_info.type =:= regular, +		       Opts#add_opts.chunk_size>0 -> +	    {ok,chunked,Info};  	{ok,Info} when Info#file_info.type =:= regular ->  	    case file:read_file(Name) of  		{ok,Bin} -> @@ -962,3 +1021,12 @@ open_mode([], Access, Raw, Opts) ->      {ok, Access, Raw, Opts};  open_mode(_, _, _, _) ->      {error, einval}. + +%%%================================================================ +do_write({tar_descriptor,UsrHandle,Fun}, Data) -> Fun(write,{UsrHandle,Data}). + +do_position({tar_descriptor,UsrHandle,Fun}, Pos) -> Fun(position,{UsrHandle,Pos}). + +do_read({tar_descriptor,UsrHandle,Fun}, Len) -> Fun(read2,{UsrHandle,Len}). + +do_close({tar_descriptor,UsrHandle,Fun}) -> Fun(close,UsrHandle). diff --git a/lib/stdlib/src/filelib.erl b/lib/stdlib/src/filelib.erl index c0921e4cf1..daae1fd2d2 100644 --- a/lib/stdlib/src/filelib.erl +++ b/lib/stdlib/src/filelib.erl @@ -265,7 +265,7 @@ do_wildcard(Pattern, Cwd, Mod) ->      lists:sort(Files).  do_wildcard_1({exists,File}, Mod) -> -    case eval_read_file_info(File, Mod) of +    case eval_read_link_info(File, Mod) of  	{ok,_} -> [File];  	_ -> []      end; @@ -371,7 +371,7 @@ compile_wildcard(Pattern, Cwd0) ->      [Root|Rest] = filename:split(Pattern),      case filename:pathtype(Root) of  	relative -> -	    Cwd = filename:join([Cwd0]), +	    Cwd = prepare_base(Cwd0),  	    compile_wildcard_2([Root|Rest], {cwd,Cwd});  	_ ->  	    compile_wildcard_2(Rest, {root,0,Root}) @@ -488,7 +488,7 @@ badpattern(Reason) ->      error({badpattern,Reason}).  eval_read_file_info(File, file) -> -    file:read_link_info(File); +    file:read_file_info(File);  eval_read_file_info(File, erl_prim_loader) ->      case erl_prim_loader:read_file_info(File) of  	error -> {error, erl_prim_loader}; @@ -497,6 +497,16 @@ eval_read_file_info(File, erl_prim_loader) ->  eval_read_file_info(File, Mod) ->      Mod:read_file_info(File). +eval_read_link_info(File, file) -> +    file:read_link_info(File); +eval_read_link_info(File, erl_prim_loader) -> +    case erl_prim_loader:read_link_info(File) of +        error -> {error, erl_prim_loader}; +        Res-> Res +    end; +eval_read_link_info(File, Mod) -> +    Mod:read_link_info(File). +  eval_list_dir(Dir, file) ->      file:list_dir(Dir);  eval_list_dir(Dir, erl_prim_loader) -> diff --git a/lib/stdlib/src/gen_event.erl b/lib/stdlib/src/gen_event.erl index d39dd89d3a..469acdc37c 100644 --- a/lib/stdlib/src/gen_event.erl +++ b/lib/stdlib/src/gen_event.erl @@ -1,7 +1,7 @@  %%  %% %CopyrightBegin%  %% -%% Copyright Ericsson AB 1996-2013. All Rights Reserved. +%% Copyright Ericsson AB 1996-2014. All Rights Reserved.  %%  %% The contents of this file are subject to the Erlang Public License,  %% Version 1.1, (the "License"); you may not use this file except in @@ -49,8 +49,6 @@  -import(error_logger, [error_msg/2]). --define(reply(X), From ! {element(2,Tag), X}). -  -record(handler, {module             :: atom(),  		  id = false,  		  state, @@ -249,49 +247,49 @@ handle_msg(Msg, Parent, ServerName, MSL, Debug) ->  	{notify, Event} ->  	    {Hib,MSL1} = server_notify(Event, handle_event, MSL, ServerName),  	    loop(Parent, ServerName, MSL1, Debug, Hib); -	{From, Tag, {sync_notify, Event}} -> +	{_From, Tag, {sync_notify, Event}} ->  	    {Hib, MSL1} = server_notify(Event, handle_event, MSL, ServerName), -	    ?reply(ok), +	    reply(Tag, ok),  	    loop(Parent, ServerName, MSL1, Debug, Hib);  	{'EXIT', From, Reason} ->  	    MSL1 = handle_exit(From, Reason, MSL, ServerName),  	    loop(Parent, ServerName, MSL1, Debug, false); -	{From, Tag, {call, Handler, Query}} -> +	{_From, Tag, {call, Handler, Query}} ->  	    {Hib, Reply, MSL1} = server_call(Handler, Query, MSL, ServerName), -	    ?reply(Reply), +	    reply(Tag, Reply),  	    loop(Parent, ServerName, MSL1, Debug, Hib); -	{From, Tag, {add_handler, Handler, Args}} -> +	{_From, Tag, {add_handler, Handler, Args}} ->  	    {Hib, Reply, MSL1} = server_add_handler(Handler, Args, MSL), -	    ?reply(Reply), +	    reply(Tag, Reply),  	    loop(Parent, ServerName, MSL1, Debug, Hib); -	{From, Tag, {add_sup_handler, Handler, Args, SupP}} -> +	{_From, Tag, {add_sup_handler, Handler, Args, SupP}} ->  	    {Hib, Reply, MSL1} = server_add_sup_handler(Handler, Args, MSL, SupP), -	    ?reply(Reply), +	    reply(Tag, Reply),  	    loop(Parent, ServerName, MSL1, Debug, Hib); -	{From, Tag, {delete_handler, Handler, Args}} -> +	{_From, Tag, {delete_handler, Handler, Args}} ->  	    {Reply, MSL1} = server_delete_handler(Handler, Args, MSL,  						  ServerName), -	    ?reply(Reply), +	    reply(Tag, Reply),  	    loop(Parent, ServerName, MSL1, Debug, false); -	{From, Tag, {swap_handler, Handler1, Args1, Handler2, Args2}} -> +	{_From, Tag, {swap_handler, Handler1, Args1, Handler2, Args2}} ->  	    {Hib, Reply, MSL1} = server_swap_handler(Handler1, Args1, Handler2,  						     Args2, MSL, ServerName), -	    ?reply(Reply), +	    reply(Tag, Reply),  	    loop(Parent, ServerName, MSL1, Debug, Hib); -	{From, Tag, {swap_sup_handler, Handler1, Args1, Handler2, Args2, +	{_From, Tag, {swap_sup_handler, Handler1, Args1, Handler2, Args2,  		     Sup}} ->  	    {Hib, Reply, MSL1} = server_swap_handler(Handler1, Args1, Handler2,  						Args2, MSL, Sup, ServerName), -	    ?reply(Reply), +	    reply(Tag, Reply),  	    loop(Parent, ServerName, MSL1, Debug, Hib); -	{From, Tag, stop} -> +	{_From, Tag, stop} ->  	    catch terminate_server(normal, Parent, MSL, ServerName), -	    ?reply(ok); -	{From, Tag, which_handlers} -> -	    ?reply(the_handlers(MSL)), +	    reply(Tag, ok); +	{_From, Tag, which_handlers} -> +	    reply(Tag, the_handlers(MSL)),  	    loop(Parent, ServerName, MSL, Debug, false); -	{From, Tag, get_modules} -> -	    ?reply(get_modules(MSL)), +	{_From, Tag, get_modules} -> +	    reply(Tag, get_modules(MSL)),  	    loop(Parent, ServerName, MSL, Debug, false);  	Other  ->  	    {Hib, MSL1} = server_notify(Other, handle_info, MSL, ServerName), @@ -303,6 +301,10 @@ terminate_server(Reason, Parent, MSL, ServerName) ->      do_unlink(Parent, MSL),      exit(Reason). +reply({From, Ref}, Msg) -> +    From ! {Ref, Msg}, +    ok. +  %% unlink the supervisor process of all supervised handlers.  %% We do not want a handler supervisor to EXIT due to the  %% termination of the event manager (server). diff --git a/lib/stdlib/src/gen_fsm.erl b/lib/stdlib/src/gen_fsm.erl index e914f7d0b2..5afe3e8b09 100644 --- a/lib/stdlib/src/gen_fsm.erl +++ b/lib/stdlib/src/gen_fsm.erl @@ -1,7 +1,7 @@  %%  %% %CopyrightBegin%  %% -%% Copyright Ericsson AB 1996-2013. All Rights Reserved. +%% Copyright Ericsson AB 1996-2014. All Rights Reserved.  %%  %% The contents of this file are subject to the Erlang Public License,  %% Version 1.1, (the "License"); you may not use this file except in @@ -594,7 +594,8 @@ reply(Name, {To, Tag}, Reply, Debug, StateName) ->  terminate(Reason, Name, Msg, Mod, StateName, StateData, Debug) ->      case catch Mod:terminate(Reason, StateName, StateData) of  	{'EXIT', R} -> -	    error_info(R, Name, Msg, StateName, StateData, Debug), +	    FmtStateData = format_status(terminate, Mod, get(), StateData), +	    error_info(R, Name, Msg, StateName, FmtStateData, Debug),  	    exit(R);  	_ ->  	    case Reason of @@ -605,17 +606,7 @@ terminate(Reason, Name, Msg, Mod, StateName, StateData, Debug) ->   		{shutdown,_}=Shutdown ->   		    exit(Shutdown);  		_ -> -                    FmtStateData = -                        case erlang:function_exported(Mod, format_status, 2) of -                            true -> -                                Args = [get(), StateData], -                                case catch Mod:format_status(terminate, Args) of -                                    {'EXIT', _} -> StateData; -                                    Else -> Else -                                end; -                            _ -> -                                StateData -                        end, +                    FmtStateData = format_status(terminate, Mod, get(), StateData),  		    error_info(Reason,Name,Msg,StateName,FmtStateData,Debug),  		    exit(Reason)  	    end @@ -680,21 +671,29 @@ format_status(Opt, StatusData) ->      Header = gen:format_status_header("Status for state machine",                                        Name),      Log = sys:get_debug(log, Debug, []), -    DefaultStatus = [{data, [{"StateData", StateData}]}], -    Specfic = -	case erlang:function_exported(Mod, format_status, 2) of -	    true -> -		case catch Mod:format_status(Opt,[PDict,StateData]) of -		    {'EXIT', _} -> DefaultStatus; -                    StatusList when is_list(StatusList) -> StatusList; -		    Else -> [Else] -		end; -	    _ -> -		DefaultStatus -	end, +    Specfic = format_status(Opt, Mod, PDict, StateData), +    Specfic = case format_status(Opt, Mod, PDict, StateData) of +		  S when is_list(S) -> S; +		  S -> [S] +	      end,      [{header, Header},       {data, [{"Status", SysState},  	     {"Parent", Parent},  	     {"Logged events", Log},  	     {"StateName", StateName}]} |       Specfic]. + +format_status(Opt, Mod, PDict, State) -> +    DefStatus = case Opt of +		    terminate -> State; +		    _ -> [{data, [{"StateData", State}]}] +		end, +    case erlang:function_exported(Mod, format_status, 2) of +	true -> +	    case catch Mod:format_status(Opt, [PDict, State]) of +		{'EXIT', _} -> DefStatus; +		Else -> Else +	    end; +	_ -> +	    DefStatus +    end. diff --git a/lib/stdlib/src/gen_server.erl b/lib/stdlib/src/gen_server.erl index 202a931fae..18ef4a2507 100644 --- a/lib/stdlib/src/gen_server.erl +++ b/lib/stdlib/src/gen_server.erl @@ -1,7 +1,7 @@  %%  %% %CopyrightBegin%  %% -%% Copyright Ericsson AB 1996-2013. All Rights Reserved. +%% Copyright Ericsson AB 1996-2014. All Rights Reserved.  %%  %% The contents of this file are subject to the Erlang Public License,  %% Version 1.1, (the "License"); you may not use this file except in @@ -567,28 +567,88 @@ start_monitor(Node, Name) when is_atom(Node), is_atom(Name) ->  	    end      end. +%% --------------------------------------------------- +%% Helper functions for try-catch of callbacks. +%% Returns the return value of the callback, or +%% {'EXIT', ExitReason, ReportReason} (if an exception occurs) +%% +%% ExitReason is the reason that shall be used when the process +%% terminates. +%% +%% ReportReason is the reason that shall be printed in the error +%% report. +%% +%% These functions are introduced in order to add the stack trace in +%% the error report produced when a callback is terminated with +%% erlang:exit/1 (OTP-12263). +%% --------------------------------------------------- + +try_dispatch({'$gen_cast', Msg}, Mod, State) -> +    try_dispatch(Mod, handle_cast, Msg, State); +try_dispatch(Info, Mod, State) -> +    try_dispatch(Mod, handle_info, Info, State). + +try_dispatch(Mod, Func, Msg, State) -> +    try +	{ok, Mod:Func(Msg, State)} +    catch +	throw:R -> +	    {ok, R}; +	error:R -> +	    Stacktrace = erlang:get_stacktrace(), +	    {'EXIT', {R, Stacktrace}, {R, Stacktrace}}; +	exit:R -> +	    Stacktrace = erlang:get_stacktrace(), +	    {'EXIT', R, {R, Stacktrace}} +    end. + +try_handle_call(Mod, Msg, From, State) -> +    try +	{ok, Mod:handle_call(Msg, From, State)} +    catch +	throw:R -> +	    {ok, R}; +	error:R -> +	    Stacktrace = erlang:get_stacktrace(), +	    {'EXIT', {R, Stacktrace}, {R, Stacktrace}}; +	exit:R -> +	    Stacktrace = erlang:get_stacktrace(), +	    {'EXIT', R, {R, Stacktrace}} +    end. + +try_terminate(Mod, Reason, State) -> +    try +	{ok, Mod:terminate(Reason, State)} +    catch +	throw:R -> +	    {ok, R}; +	error:R -> +	    Stacktrace = erlang:get_stacktrace(), +	    {'EXIT', {R, Stacktrace}, {R, Stacktrace}}; +	exit:R -> +	    Stacktrace = erlang:get_stacktrace(), +	    {'EXIT', R, {R, Stacktrace}} +    end. + +  %%% ---------------------------------------------------  %%% Message handling functions  %%% --------------------------------------------------- -dispatch({'$gen_cast', Msg}, Mod, State) -> -    Mod:handle_cast(Msg, State); -dispatch(Info, Mod, State) -> -    Mod:handle_info(Info, State). -  handle_msg({'$gen_call', From, Msg}, Parent, Name, State, Mod) -> -    case catch Mod:handle_call(Msg, From, State) of -	{reply, Reply, NState} -> +    Result = try_handle_call(Mod, Msg, From, State), +    case Result of +	{ok, {reply, Reply, NState}} ->  	    reply(From, Reply),  	    loop(Parent, Name, NState, Mod, infinity, []); -	{reply, Reply, NState, Time1} -> +	{ok, {reply, Reply, NState, Time1}} ->  	    reply(From, Reply),  	    loop(Parent, Name, NState, Mod, Time1, []); -	{noreply, NState} -> +	{ok, {noreply, NState}} ->  	    loop(Parent, Name, NState, Mod, infinity, []); -	{noreply, NState, Time1} -> +	{ok, {noreply, NState, Time1}} ->  	    loop(Parent, Name, NState, Mod, Time1, []); -	{stop, Reason, Reply, NState} -> +	{ok, {stop, Reason, Reply, NState}} ->  	    {'EXIT', R} =   		(catch terminate(Reason, Name, Msg, Mod, NState, [])),  	    reply(From, Reply), @@ -596,26 +656,27 @@ handle_msg({'$gen_call', From, Msg}, Parent, Name, State, Mod) ->  	Other -> handle_common_reply(Other, Parent, Name, Msg, Mod, State)      end;  handle_msg(Msg, Parent, Name, State, Mod) -> -    Reply = (catch dispatch(Msg, Mod, State)), +    Reply = try_dispatch(Msg, Mod, State),      handle_common_reply(Reply, Parent, Name, Msg, Mod, State).  handle_msg({'$gen_call', From, Msg}, Parent, Name, State, Mod, Debug) -> -    case catch Mod:handle_call(Msg, From, State) of -	{reply, Reply, NState} -> +    Result = try_handle_call(Mod, Msg, From, State), +    case Result of +	{ok, {reply, Reply, NState}} ->  	    Debug1 = reply(Name, From, Reply, NState, Debug),  	    loop(Parent, Name, NState, Mod, infinity, Debug1); -	{reply, Reply, NState, Time1} -> +	{ok, {reply, Reply, NState, Time1}} ->  	    Debug1 = reply(Name, From, Reply, NState, Debug),  	    loop(Parent, Name, NState, Mod, Time1, Debug1); -	{noreply, NState} -> +	{ok, {noreply, NState}} ->  	    Debug1 = sys:handle_debug(Debug, fun print_event/3, Name,  				      {noreply, NState}),  	    loop(Parent, Name, NState, Mod, infinity, Debug1); -	{noreply, NState, Time1} -> +	{ok, {noreply, NState, Time1}} ->  	    Debug1 = sys:handle_debug(Debug, fun print_event/3, Name,  				      {noreply, NState}),  	    loop(Parent, Name, NState, Mod, Time1, Debug1); -	{stop, Reason, Reply, NState} -> +	{ok, {stop, Reason, Reply, NState}} ->  	    {'EXIT', R} =   		(catch terminate(Reason, Name, Msg, Mod, NState, Debug)),  	    _ = reply(Name, From, Reply, NState, Debug), @@ -624,39 +685,39 @@ handle_msg({'$gen_call', From, Msg}, Parent, Name, State, Mod, Debug) ->  	    handle_common_reply(Other, Parent, Name, Msg, Mod, State, Debug)      end;  handle_msg(Msg, Parent, Name, State, Mod, Debug) -> -    Reply = (catch dispatch(Msg, Mod, State)), +    Reply = try_dispatch(Msg, Mod, State),      handle_common_reply(Reply, Parent, Name, Msg, Mod, State, Debug).  handle_common_reply(Reply, Parent, Name, Msg, Mod, State) ->      case Reply of -	{noreply, NState} -> +	{ok, {noreply, NState}} ->  	    loop(Parent, Name, NState, Mod, infinity, []); -	{noreply, NState, Time1} -> +	{ok, {noreply, NState, Time1}} ->  	    loop(Parent, Name, NState, Mod, Time1, []); -	{stop, Reason, NState} -> +	{ok, {stop, Reason, NState}} ->  	    terminate(Reason, Name, Msg, Mod, NState, []); -	{'EXIT', What} -> -	    terminate(What, Name, Msg, Mod, State, []); -	_ -> -	    terminate({bad_return_value, Reply}, Name, Msg, Mod, State, []) +	{'EXIT', ExitReason, ReportReason} -> +	    terminate(ExitReason, ReportReason, Name, Msg, Mod, State, []); +	{ok, BadReply} -> +	    terminate({bad_return_value, BadReply}, Name, Msg, Mod, State, [])      end.  handle_common_reply(Reply, Parent, Name, Msg, Mod, State, Debug) ->      case Reply of -	{noreply, NState} -> +	{ok, {noreply, NState}} ->  	    Debug1 = sys:handle_debug(Debug, fun print_event/3, Name,  				      {noreply, NState}),  	    loop(Parent, Name, NState, Mod, infinity, Debug1); -	{noreply, NState, Time1} -> +	{ok, {noreply, NState, Time1}} ->  	    Debug1 = sys:handle_debug(Debug, fun print_event/3, Name,  				      {noreply, NState}),  	    loop(Parent, Name, NState, Mod, Time1, Debug1); -	{stop, Reason, NState} -> +	{ok, {stop, Reason, NState}} ->  	    terminate(Reason, Name, Msg, Mod, NState, Debug); -	{'EXIT', What} -> -	    terminate(What, Name, Msg, Mod, State, Debug); -	_ -> -	    terminate({bad_return_value, Reply}, Name, Msg, Mod, State, Debug) +	{'EXIT', ExitReason, ReportReason} -> +	    terminate(ExitReason, ReportReason, Name, Msg, Mod, State, Debug); +	{ok, BadReply} -> +	    terminate({bad_return_value, BadReply}, Name, Msg, Mod, State, Debug)      end.  reply(Name, {To, Tag}, Reply, State, Debug) -> @@ -717,13 +778,20 @@ print_event(Dev, Event, Name) ->  %%% Terminate the server.  %%% --------------------------------------------------- +-spec terminate(_, _, _, _, _, _) -> no_return().  terminate(Reason, Name, Msg, Mod, State, Debug) -> -    case catch Mod:terminate(Reason, State) of -	{'EXIT', R} -> -	    error_info(R, Name, Msg, State, Debug), -	    exit(R); +    terminate(Reason, Reason, Name, Msg, Mod, State, Debug). + +-spec terminate(_, _, _, _, _, _, _) -> no_return(). +terminate(ExitReason, ReportReason, Name, Msg, Mod, State, Debug) -> +    Reply = try_terminate(Mod, ExitReason, State), +    case Reply of +	{'EXIT', ExitReason1, ReportReason1} -> +	    FmtState = format_status(terminate, Mod, get(), State), +	    error_info(ReportReason1, Name, Msg, FmtState, Debug), +	    exit(ExitReason1);  	_ -> -	    case Reason of +	    case ExitReason of  		normal ->  		    exit(normal);  		shutdown -> @@ -731,19 +799,9 @@ terminate(Reason, Name, Msg, Mod, State, Debug) ->  		{shutdown,_}=Shutdown ->  		    exit(Shutdown);  		_ -> -		    FmtState = -			case erlang:function_exported(Mod, format_status, 2) of -			    true -> -				Args = [get(), State], -				case catch Mod:format_status(terminate, Args) of -				    {'EXIT', _} -> State; -				    Else -> Else -				end; -			    _ -> -				State -			end, -		    error_info(Reason, Name, Msg, FmtState, Debug), -		    exit(Reason) +		    FmtState = format_status(terminate, Mod, get(), State), +		    error_info(ReportReason, Name, Msg, FmtState, Debug), +		    exit(ExitReason)  	    end      end. @@ -875,23 +933,29 @@ name_to_pid(Name) ->  %%-----------------------------------------------------------------  format_status(Opt, StatusData) ->      [PDict, SysState, Parent, Debug, [Name, State, Mod, _Time]] = StatusData, -    Header = gen:format_status_header("Status for generic server", -                                      Name), +    Header = gen:format_status_header("Status for generic server", Name),      Log = sys:get_debug(log, Debug, []), -    DefaultStatus = [{data, [{"State", State}]}], -    Specfic = -	case erlang:function_exported(Mod, format_status, 2) of -	    true -> -		case catch Mod:format_status(Opt, [PDict, State]) of -		    {'EXIT', _} -> DefaultStatus; -                    StatusList when is_list(StatusList) -> StatusList; -		    Else -> [Else] -		end; -	    _ -> -		DefaultStatus -	end, +    Specfic = case format_status(Opt, Mod, PDict, State) of +		  S when is_list(S) -> S; +		  S -> [S] +	      end,      [{header, Header},       {data, [{"Status", SysState},  	     {"Parent", Parent},  	     {"Logged events", Log}]} |       Specfic]. + +format_status(Opt, Mod, PDict, State) -> +    DefStatus = case Opt of +		    terminate -> State; +		    _ -> [{data, [{"State", State}]}] +		end, +    case erlang:function_exported(Mod, format_status, 2) of +	true -> +	    case catch Mod:format_status(Opt, [PDict, State]) of +		{'EXIT', _} -> DefStatus; +		Else -> Else +	    end; +	_ -> +	    DefStatus +    end. diff --git a/lib/stdlib/src/io.erl b/lib/stdlib/src/io.erl index 27e2a82b41..b9ace2f442 100644 --- a/lib/stdlib/src/io.erl +++ b/lib/stdlib/src/io.erl @@ -566,12 +566,23 @@ request(Name, Request) when is_atom(Name) ->  execute_request(Pid, {Convert,Converted}) ->      Mref = erlang:monitor(process, Pid), -    Pid ! {io_request,self(),Pid,Converted}, -    if -	Convert -> -	    convert_binaries(wait_io_mon_reply(Pid, Mref)); -	true -> -	    wait_io_mon_reply(Pid, Mref) +    Pid ! {io_request,self(),Mref,Converted}, + +    receive +	{io_reply, Mref, Reply} -> +	    erlang:demonitor(Mref, [flush]), +	    if +		Convert -> +		    convert_binaries(Reply); +		true -> +		    Reply +	    end; +	{'DOWN', Mref, _, _, _} -> +	    receive +		{'EXIT', Pid, _What} -> true +	    after 0 -> true +	    end, +	    {error,terminated}      end.  requests(Requests) ->				%Requests as atomic action @@ -597,26 +608,6 @@ default_input() ->  default_output() ->      group_leader(). -wait_io_mon_reply(From, Mref) -> -    receive -	{io_reply, From, Reply} -> -	    erlang:demonitor(Mref, [flush]), -	    Reply; -	{'EXIT', From, _What} -> -	    receive -		{'DOWN', Mref, _, _, _} -> true -	    after 0 -> true -	    end, -	    {error,terminated}; -	{'DOWN', Mref, _, _, _} -> -	    receive -		{'EXIT', From, _What} -> true -	    after 0 -> true -	    end, -	    {error,terminated} -    end. - -  %% io_requests(Requests)  %%  Transform requests into correct i/o server messages. Only handle the  %%  one we KNOW must be changed, others, including incorrect ones, are diff --git a/lib/stdlib/src/io_lib_format.erl b/lib/stdlib/src/io_lib_format.erl index 56e15a17ec..89ae6fb187 100644 --- a/lib/stdlib/src/io_lib_format.erl +++ b/lib/stdlib/src/io_lib_format.erl @@ -255,7 +255,7 @@ term(T, none, _Adj, none, _Pad) -> T;  term(T, none, Adj, P, Pad) -> term(T, P, Adj, P, Pad);  term(T, F, Adj, P0, Pad) ->      L = lists:flatlength(T), -    P = case P0 of none -> erlang:min(L, F); _ -> P0 end, +    P = erlang:min(L, case P0 of none -> F; _ -> min(P0, F) end),      if  	L > P ->  	    adjust(chars($*, P), chars(Pad, F-P), Adj); diff --git a/lib/stdlib/src/maps.erl b/lib/stdlib/src/maps.erl index 4ef1638e6d..ba4d6a5c87 100644 --- a/lib/stdlib/src/maps.erl +++ b/lib/stdlib/src/maps.erl @@ -24,6 +24,7 @@  	map/2,  	size/1,      without/2, +    with/2,      get/3      ]). @@ -133,10 +134,10 @@ to_list(_) -> erlang:nif_error(undef).  update(_,_,_) -> erlang:nif_error(undef). --spec values(Map) -> Keys when +-spec values(Map) -> Values when      Map :: map(), -    Keys :: [Key], -    Key :: term(). +    Values :: [Value], +    Value :: term().  values(_) -> erlang:nif_error(undef). @@ -201,3 +202,13 @@ size(Map) when is_map(Map) ->  without(Ks, M) when is_list(Ks), is_map(M) ->      maps:from_list([{K,V}||{K,V} <- maps:to_list(M), not lists:member(K, Ks)]). + + +-spec with(Ks, Map1) -> Map2 when +    Ks :: [K], +    Map1 :: map(), +    Map2 :: map(), +    K :: term(). + +with(Ks, M) when is_list(Ks), is_map(M) -> +    maps:from_list([{K,V}||{K,V} <- maps:to_list(M), lists:member(K, Ks)]). diff --git a/lib/stdlib/src/ms_transform.erl b/lib/stdlib/src/ms_transform.erl index 27dfcf52e1..97564e2e44 100644 --- a/lib/stdlib/src/ms_transform.erl +++ b/lib/stdlib/src/ms_transform.erl @@ -725,10 +725,10 @@ transform_head([V],OuterBound) ->      th(NewV,NewBind,OuterBound). -toplevel_head_match({match,Line,{var,_,VName},Expr},B,OB) -> +toplevel_head_match({match,_,{var,Line,VName},Expr},B,OB) ->      warn_var_clash(Line,VName,OB),      {Expr,new_bind({VName,'$_'},B)}; -toplevel_head_match({match,Line,Expr,{var,_,VName}},B,OB) -> +toplevel_head_match({match,_,Expr,{var,Line,VName}},B,OB) ->      warn_var_clash(Line,VName,OB),      {Expr,new_bind({VName,'$_'},B)};  toplevel_head_match(Other,B,_OB) -> diff --git a/lib/stdlib/src/otp_internal.erl b/lib/stdlib/src/otp_internal.erl index c0ee8799c8..6c25beabe9 100644 --- a/lib/stdlib/src/otp_internal.erl +++ b/lib/stdlib/src/otp_internal.erl @@ -421,13 +421,13 @@ obsolete_1(ssh_cm, stop_listener, 1) ->  obsolete_1(ssh_cm, session_open, A) when A =:= 2; A =:= 4 ->      {removed,{ssh_connection,session_channel,A},"R14B"};  obsolete_1(ssh_cm, direct_tcpip, A) when A =:= 6; A =:= 8 -> -    {removed,{ssh_connection,direct_tcpip,A}}; +    {removed,{ssh_connection,direct_tcpip,A},"R14B"};  obsolete_1(ssh_cm, tcpip_forward, 3) ->      {removed,{ssh_connection,tcpip_forward,3},"R14B"};  obsolete_1(ssh_cm, cancel_tcpip_forward, 3) ->      {removed,{ssh_connection,cancel_tcpip_forward,3},"R14B"};  obsolete_1(ssh_cm, open_pty, A) when A =:= 3; A =:= 7; A =:= 9 -> -    {removed,{ssh_connection,open_pty,A},"R14"}; +    {removed,{ssh_connection,open_pty,A},"R14B"};  obsolete_1(ssh_cm, setenv, 5) ->      {removed,{ssh_connection,setenv,5},"R14B"};  obsolete_1(ssh_cm, shell, 2) -> @@ -441,11 +441,11 @@ obsolete_1(ssh_cm, winch, A) when A =:= 4; A =:= 6 ->  obsolete_1(ssh_cm, signal, 3) ->      {removed,{ssh_connection,signal,3},"R14B"};  obsolete_1(ssh_cm, attach, A) when A =:= 2; A =:= 3 -> -    {removed,{ssh,attach,A}}; +    {removed,"no longer useful; removed in R14B"};  obsolete_1(ssh_cm, detach, 2) -> -    {removed,"no longer useful; will be removed in R14B"}; +    {removed,"no longer useful; removed in R14B"};  obsolete_1(ssh_cm, set_user_ack, 4) -> -    {removed,"no longer useful; will be removed in R14B"}; +    {removed,"no longer useful; removed in R14B"};  obsolete_1(ssh_cm, adjust_window, 3) ->      {removed,{ssh_connection,adjust_window,3},"R14B"};  obsolete_1(ssh_cm, close, 2) -> @@ -461,9 +461,9 @@ obsolete_1(ssh_cm, send_ack, A) when 3 =< A, A =< 5 ->  obsolete_1(ssh_ssh, connect, A) when 1 =< A, A =< 3 ->      {removed,{ssh,shell,A},"R14B"};  obsolete_1(ssh_sshd, listen, A) when 0 =< A, A =< 3 -> -    {removed,{ssh,daemon,[1,2,3]},"R14"}; +    {removed,{ssh,daemon,[1,2,3]},"R14B"};  obsolete_1(ssh_sshd, stop, 1) -> -    {removed,{ssh,stop_listener,1}}; +    {removed,{ssh,stop_listener,1},"R14B"};  %% Added in R13A.  obsolete_1(regexp, _, _) -> diff --git a/lib/stdlib/src/proc_lib.erl b/lib/stdlib/src/proc_lib.erl index 1eb6fc2e86..bf2a4e7ac5 100644 --- a/lib/stdlib/src/proc_lib.erl +++ b/lib/stdlib/src/proc_lib.erl @@ -216,10 +216,8 @@ ensure_link(SpawnOpts) ->  init_p(Parent, Ancestors, Fun) when is_function(Fun) ->      put('$ancestors', [Parent|Ancestors]), -    {module,Mod} = erlang:fun_info(Fun, module), -    {name,Name} = erlang:fun_info(Fun, name), -    {arity,Arity} = erlang:fun_info(Fun, arity), -    put('$initial_call', {Mod,Name,Arity}), +    Mfa = erlang:fun_info_mfa(Fun), +    put('$initial_call', Mfa),      try  	Fun()      catch diff --git a/lib/stdlib/src/shell.erl b/lib/stdlib/src/shell.erl index 3b90542452..679c13f0cf 100644 --- a/lib/stdlib/src/shell.erl +++ b/lib/stdlib/src/shell.erl @@ -371,6 +371,14 @@ expand_expr({bc,L,E,Qs}, C) ->      {bc,L,expand_expr(E, C),expand_quals(Qs, C)};  expand_expr({tuple,L,Elts}, C) ->      {tuple,L,expand_exprs(Elts, C)}; +expand_expr({map,L,Es}, C) -> +    {map,L,expand_exprs(Es, C)}; +expand_expr({map,L,Arg,Es}, C) -> +    {map,L,expand_expr(Arg, C),expand_exprs(Es, C)}; +expand_expr({map_field_assoc,L,K,V}, C) -> +    {map_field_assoc,L,expand_expr(K, C),expand_expr(V, C)}; +expand_expr({map_field_exact,L,K,V}, C) -> +    {map_field_exact,L,expand_expr(K, C),expand_expr(V, C)};  expand_expr({record_index,L,Name,F}, C) ->      {record_index,L,Name,expand_expr(F, C)};  expand_expr({record,L,Name,Is}, C) -> diff --git a/lib/stdlib/src/stdlib.app.src b/lib/stdlib/src/stdlib.app.src index d388410de0..aa9899da3b 100644 --- a/lib/stdlib/src/stdlib.app.src +++ b/lib/stdlib/src/stdlib.app.src @@ -103,7 +103,7 @@                 dets]},    {applications, [kernel]},    {env, []}, -  {runtime_dependencies, ["sasl-2.4","kernel-3.0","erts-6.0","crypto-3.3", +  {runtime_dependencies, ["sasl-2.4","kernel-3.0.2","erts-6.2","crypto-3.3",  			  "compiler-5.0"]}  ]}. diff --git a/lib/stdlib/src/stdlib.appup.src b/lib/stdlib/src/stdlib.appup.src index 22eefb2514..7802ea884f 100644 --- a/lib/stdlib/src/stdlib.appup.src +++ b/lib/stdlib/src/stdlib.appup.src @@ -17,9 +17,11 @@  %% %CopyrightEnd%  {"%VSN%",   %% Up from - max one major revision back - [{<<"2\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R17 + [{<<"2\\.[1-2](\\.[0-9]+)*">>,[restart_new_emulator]}, %% 17.1-17.3 +  {<<"2\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, %% 17.0    {<<"1\\.19(\\.[0-9]+)*">>,[restart_new_emulator]}],%% R16   %% Down to - max one major revision back - [{<<"2\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R17 + [{<<"2\\.[1-2](\\.[0-9]+)*">>,[restart_new_emulator]}, %% 17.1-17.3 +  {<<"2\\.0(\\.[0-9]+)*">>,[restart_new_emulator]}, %% 17.0    {<<"1\\.19(\\.[0-9]+)*">>,[restart_new_emulator]}] %% R16  }. diff --git a/lib/stdlib/src/sys.erl b/lib/stdlib/src/sys.erl index e25cc25f57..d3ba09ce82 100644 --- a/lib/stdlib/src/sys.erl +++ b/lib/stdlib/src/sys.erl @@ -1,7 +1,7 @@  %%  %% %CopyrightBegin%  %%  -%% Copyright Ericsson AB 1996-2013. All Rights Reserved. +%% Copyright Ericsson AB 1996-2014. All Rights Reserved.  %%   %% The contents of this file are subject to the Erlang Public License,  %% Version 1.1, (the "License"); you may not use this file except in @@ -46,7 +46,7 @@                           {N :: non_neg_integer(),                            [{Event :: system_event(),                              FuncState :: _, -                            FormFunc :: dbg_fun()}]}} +                            FormFunc :: format_fun()}]}}                        | {'statistics', {file:date_time(),                                          {'reductions', non_neg_integer()},                                          MessagesIn :: non_neg_integer(), @@ -57,6 +57,10 @@                               Event :: system_event(),                               ProcState :: _) -> 'done' | (NewFuncState :: _)). +-type format_fun()   :: fun((Device :: io:device() | file:io_device(), +			     Event :: system_event(), +			     Extra :: term()) -> any()). +  %%-----------------------------------------------------------------  %% System messages  %%----------------------------------------------------------------- @@ -346,7 +350,7 @@ handle_system_msg(SysState, Msg, From, Parent, Mod, Debug, Misc, Hib) ->  %%-----------------------------------------------------------------  -spec handle_debug(Debug, FormFunc, Extra, Event) -> [dbg_opt()] when        Debug :: [dbg_opt()], -      FormFunc :: dbg_fun(), +      FormFunc :: format_fun(),        Extra :: term(),        Event :: system_event().  handle_debug([{trace, true} | T], FormFunc, State, Event) -> diff --git a/lib/stdlib/test/dets_SUITE.erl b/lib/stdlib/test/dets_SUITE.erl index 6be37cbecf..3b08ac165e 100644 --- a/lib/stdlib/test/dets_SUITE.erl +++ b/lib/stdlib/test/dets_SUITE.erl @@ -223,8 +223,7 @@ open(Config, Version) ->      ?format("Crashing dets server \n", []),      process_flag(trap_exit, true), -    Procs = [whereis(?DETS_SERVER) | map(fun(Tab) -> dets:info(Tab, pid) end, -				 Tabs)], +    Procs = [whereis(?DETS_SERVER) | [dets:info(Tab, pid) || Tab <- Tabs]],      foreach(fun(Pid) -> exit(Pid, kill) end, Procs),      timer:sleep(100),      c:flush(),  %% flush all the EXIT sigs @@ -235,18 +234,32 @@ open(Config, Version) ->      open_files(1, All, Version),      ?format("Checking contents of repaired files \n", []),      check(Tabs, Data), -     -    close_all(Tabs), +    close_all(Tabs),      delete_files(All), -    P1 = pps(), +      {Ports0, Procs0} = P0, -    {Ports1, Procs1} = P1, -    true = Ports1 =:= Ports0, -    %% The dets_server process has been restarted: -    [_] = Procs0 -- Procs1, -    [_] = Procs1 -- Procs0, -    ok. +    Test = fun() -> +                   P1 = pps(), +                   {Ports1, Procs1} = P1, +                   show("Old port", Ports0 -- Ports1), +                   show("New port", Ports1 -- Ports0), +                   show("Old procs", Procs0 -- Procs1), +                   show("New procs", Procs1 -- Procs0), +                   io:format("Remaining Dets-pids (should be nil): ~p~n", +                             [find_dets_pids()]), +                   true = Ports1 =:= Ports0, +                   %% The dets_server process has been restarted: +                   [_] = Procs0 -- Procs1, +                   [_] = Procs1 -- Procs0, +                   ok +           end, +    case catch Test() of +        ok -> ok; +        _ -> +            timer:sleep(500), +            ok = Test() +    end.  check(Tabs, Data) ->      foreach(fun(Tab) -> @@ -2032,6 +2045,12 @@ match(Config, Version) ->      CrashPos = if Version =:= 8 -> 5; Version =:= 9 -> 1 end,      crash(Fname, ObjPos2+CrashPos),      {ok, _} = dets:open_file(T, Args), +    case dets:insert_new(T, Obj) of % OTP-12024 +        ok -> +            bad_object(dets:sync(T), Fname); +        Else3 -> +            bad_object(Else3, Fname) +    end,      io:format("Expect corrupt table:~n"),      case ins(T, N) of          ok -> @@ -3269,12 +3288,22 @@ simultaneous_open(Config) ->      File = filename(Tab, Config),      ok = monit(Tab, File), -    ok = kill_while_repairing(Tab, File), -    ok = kill_while_init(Tab, File), -    ok = open_ro(Tab, File), -    ok = open_w(Tab, File, 0, Config), -    ok = open_w(Tab, File, 100, Config), -    ok. +    case feasible() of +        false -> {comment, "OK, but did not run all of the test"}; +        true -> +            ok = kill_while_repairing(Tab, File), +            ok = kill_while_init(Tab, File), +            ok = open_ro(Tab, File), +            ok = open_w(Tab, File, 0, Config), +            ok = open_w(Tab, File, 100, Config) +    end. + +feasible() -> +    LP = erlang:system_info(logical_processors), +    (is_integer(LP) +     andalso LP >= erlang:system_info(schedulers_online) +     andalso not erlang:system_info(debug_compiled) +     andalso not erlang:system_info(lock_checking)).  %% One process logs and another process closes the log. Before  %% monitors were used, this would make the client never return. @@ -3301,7 +3330,6 @@ kill_while_repairing(Tab, File) ->      Delay = 1000,      dets:start(),      Parent = self(), -    Ps = processes(),      F = fun() ->                   R = (catch dets:open_file(Tab, [{file,File}])),                  timer:sleep(Delay), @@ -3312,7 +3340,7 @@ kill_while_repairing(Tab, File) ->      P1 = spawn(F),      P2 = spawn(F),      P3 = spawn(F), -    DetsPid = find_dets_pid([P1, P2, P3 | Ps]), +    DetsPid = find_dets_pid(),      exit(DetsPid, kill),      receive {P1,R1} -> R1 end, @@ -3336,12 +3364,6 @@ kill_while_repairing(Tab, File) ->      file:delete(File),      ok. -find_dets_pid(P0) -> -    case lists:sort(processes() -- P0) of -        [P, _] -> P; -        _ -> timer:sleep(100), find_dets_pid(P0) -    end. -  find_dets_pid() ->      case find_dets_pids() of          [] -> @@ -3415,6 +3437,13 @@ open_ro(Tab, File) ->  open_w(Tab, File, Delay, Config) ->      create_opened_log(File), + +    Tab2 = t2, +    File2 = filename(Tab2, Config), +    file:delete(File2), +    {ok,Tab2} = dets:open_file(Tab2, [{file,File2}]), +    ok = dets:close(Tab2), +      Parent = self(),      F = fun() ->                   R = dets:open_file(Tab, [{file,File}]), @@ -3424,16 +3453,16 @@ open_w(Tab, File, Delay, Config) ->      Pid1 = spawn(F),      Pid2 = spawn(F),      Pid3 = spawn(F), -    undefined = dets:info(Tab), % is repairing now -    0 = qlen(), -    Tab2 = t2, -    File2 = filename(Tab2, Config), -    file:delete(File2), +    ok = wait_for_repair_to_start(Tab), + +    %% It is assumed that it takes some time to repair the file.      {ok,Tab2} = dets:open_file(Tab2, [{file,File2}]), +    %% The Dets server managed to handle to open_file request. +    0 = qlen(), % still repairing +      ok = dets:close(Tab2),      file:delete(File2), -    0 = qlen(), % still repairing      receive {Pid1,R1} -> {ok, Tab} = R1 end,      receive {Pid2,R2} -> {ok, Tab} = R2 end, @@ -3450,6 +3479,15 @@ open_w(Tab, File, Delay, Config) ->      file:delete(File),      ok. +wait_for_repair_to_start(Tab) -> +    case catch dets_server:get_pid(Tab) of +        {'EXIT', _} -> +            timer:sleep(1), +            wait_for_repair_to_start(Tab); +        Pid when is_pid(Pid) -> +            ok +    end. +  qlen() ->      {_, {_, N}} = lists:keysearch(message_queue_len, 1, process_info(self())),      N. @@ -4344,6 +4382,7 @@ check_badarg({'EXIT', {badarg, [{M,F,A,_} | _]}}, M, F, Args)  ->      true = test_server:is_native(M) andalso length(Args) =:= A.  check_pps({Ports0,Procs0} = P0) -> +    ok = check_dets_tables(),      case pps() of          P0 ->              ok; @@ -4369,13 +4408,45 @@ check_pps({Ports0,Procs0} = P0) ->  	    end      end. +%% Copied from dets_server.erl: +-define(REGISTRY, dets_registry). +-define(OWNERS, dets_owners). +-define(STORE, dets). + +check_dets_tables() -> +    Store = [T || +                T <- ets:all(), +                ets:info(T, name) =:= ?STORE, +                owner(T) =:= dets], +    S = case Store of +            [Tab] -> ets:tab2list(Tab); +            [] -> [] +        end, +    case {ets:tab2list(?REGISTRY), ets:tab2list(?OWNERS), S} of +        {[], [], []} -> ok; +        {R, O, _} -> +            io:format("Registry: ~p~n", [R]), +            io:format("Owners: ~p~n", [O]), +            io:format("Store: ~p~n", [S]), +            not_ok +    end. + +owner(Tab) -> +    Owner = ets:info(Tab, owner), +    case process_info(Owner, registered_name) of +	{registered_name, Name} -> Name; +	_ -> Owner +    end. +  show(_S, []) ->      ok; -show(S, [Pid|Pids]) when is_pid(Pid) -> -    io:format("~s: ~p~n", [S, erlang:process_info(Pid)]), +show(S, [{Pid, Name, InitCall}|Pids]) when is_pid(Pid) -> +    io:format("~s: ~w (~w), ~w: ~p~n", +              [S, Pid, proc_reg_name(Name), InitCall, +               erlang:process_info(Pid)]),      show(S, Pids); -show(S, [Port|Ports]) when is_port(Port)-> -    io:format("~s: ~p~n", [S, erlang:port_info(Port)]), +show(S, [{Port, _}|Ports]) when is_port(Port)-> +    io:format("~s: ~w: ~p~n", [S, Port, erlang:port_info(Port)]),      show(S, Ports).  pps() -> @@ -4391,5 +4462,8 @@ process_list() ->        safe_second_element(process_info(P, initial_call))} ||           P <- processes()]. +proc_reg_name({registered_name, Name}) -> Name; +proc_reg_name([]) -> no_reg_name. +  safe_second_element({_,Info}) -> Info;  safe_second_element(Other) -> Other. diff --git a/lib/stdlib/test/erl_pp_SUITE.erl b/lib/stdlib/test/erl_pp_SUITE.erl index babf3a49eb..927fe0b595 100644 --- a/lib/stdlib/test/erl_pp_SUITE.erl +++ b/lib/stdlib/test/erl_pp_SUITE.erl @@ -604,20 +604,20 @@ import_export(Config) when is_list(Config) ->  misc_attrs(suite) ->      [];  misc_attrs(Config) when is_list(Config) -> -    ?line ok = pp_forms(<<"-module(m). ">>), -    ?line ok = pp_forms(<<"-module(m, [Aafjlksfjdlsjflsdfjlsdjflkdsfjlk," -                          "Blsjfdlslfjsdf]). ">>), -    ?line ok = pp_forms(<<"-export([]). ">>), -    ?line ok = pp_forms(<<"-export([foo/2, bar/0]). ">>), -    ?line ok = pp_forms(<<"-export([bar/0]). ">>), -    ?line ok = pp_forms(<<"-import(lists, []). ">>), -    ?line ok = pp_forms(<<"-import(lists, [map/2]). ">>), -    ?line ok = pp_forms(<<"-import(lists, [map/2, foreach/2]). ">>), -    ?line ok = pp_forms(<<"-'wild '({attr2,3}). ">>), -    ?line ok = pp_forms(<<"-record(a, {b,c}). ">>), -    ?line ok = pp_forms(<<"-record(' a ', {}). ">>), -    ?line ok = pp_forms(<<"-record(' a ', {foo = foo:bar()}). ">>), - +    ok = pp_forms(<<"-module(m). ">>), +    ok = pp_forms(<<"-module(m, [Aafjlksfjdlsjflsdfjlsdjflkdsfjlk," +                    "Blsjfdlslfjsdf]). ">>), +    ok = pp_forms(<<"-export([]). ">>), +    ok = pp_forms(<<"-export([foo/2, bar/0]). ">>), +    ok = pp_forms(<<"-export([bar/0]). ">>), +    ok = pp_forms(<<"-import(lists, []). ">>), +    ok = pp_forms(<<"-import(lists, [map/2]). ">>), +    ok = pp_forms(<<"-import(lists, [map/2, foreach/2]). ">>), +    ok = pp_forms(<<"-'wild '({attr2,3}). ">>), +    ok = pp_forms(<<"-record(a, {b,c}). ">>), +    ok = pp_forms(<<"-record(' a ', {}). ">>), +    ok = pp_forms(<<"-record(' a ', {foo = foo:bar()}). ">>), +    ok = pp_forms(<<"-custom1(#{test1 => init/2, test2 => [val/1, val/2]}). ">>),      ok.  dialyzer_attrs(suite) -> diff --git a/lib/stdlib/test/erl_scan_SUITE.erl b/lib/stdlib/test/erl_scan_SUITE.erl index 35067e8116..9be9f641c8 100644 --- a/lib/stdlib/test/erl_scan_SUITE.erl +++ b/lib/stdlib/test/erl_scan_SUITE.erl @@ -204,20 +204,20 @@ reserved_words() ->      [begin           ?line {RW, true} = {RW, erl_scan:reserved_word(RW)},           S = atom_to_list(RW), -         Ts = [{RW,1}], +         Ts = [{RW,{1,1}}],           ?line test_string(S, Ts)       end || RW <- L],      ok.  atoms() -> -    ?line test_string("a -                 b", [{atom,1,a},{atom,2,b}]), -    ?line test_string("'a b'", [{atom,1,'a b'}]), -    ?line test_string("a", [{atom,1,a}]), -    ?line test_string("a@2", [{atom,1,a@2}]), -    ?line test_string([39,65,200,39], [{atom,1,'AÈ'}]), -    ?line test_string("ärlig östen", [{atom,1,ärlig},{atom,1,östen}]), +    test_string("a +                 b", [{atom,{1,1},a},{atom,{2,18},b}]), +    test_string("'a b'", [{atom,{1,1},'a b'}]), +    test_string("a", [{atom,{1,1},a}]), +    test_string("a@2", [{atom,{1,1},a@2}]), +    test_string([39,65,200,39], [{atom,{1,1},'AÈ'}]), +    test_string("ärlig östen", [{atom,{1,1},ärlig},{atom,{1,7},östen}]),      ?line {ok,[{atom,_,'$a'}],{1,6}} =          erl_scan:string("'$\\a'", {1,1}),      ?line test("'$\\a'"), @@ -230,7 +230,7 @@ punctuations() ->      %% One token at a time:      [begin           W = list_to_atom(S), -         Ts = [{W,1}], +         Ts = [{W,{1,1}}],           ?line test_string(S, Ts)       end || S <- L],      Three = ["/=:=", "<=:=", "==:=", ">=:="], % three tokens... @@ -246,53 +246,60 @@ punctuations() ->      [begin           W1 = list_to_atom(S1),           W2 = list_to_atom(S2), -         Ts = [{W1,1},{W2,1}], +         Ts = [{W1,{1,1}},{W2,{1,-L2+1}}],           ?line test_string(S, Ts) -     end || {S,[{_,S1,S2}|_]}  <- SL], +     end || {S,[{L2,S1,S2}|_]}  <- SL], -    PTs1 = [{'!',1},{'(',1},{')',1},{',',1},{';',1},{'=',1},{'[',1}, -            {']',1},{'{',1},{'|',1},{'}',1}], +    PTs1 = [{'!',{1,1}},{'(',{1,2}},{')',{1,3}},{',',{1,4}},{';',{1,5}}, +            {'=',{1,6}},{'[',{1,7}},{']',{1,8}},{'{',{1,9}},{'|',{1,10}}, +            {'}',{1,11}}],      ?line test_string("!(),;=[]{|}", PTs1), -    PTs2 = [{'#',1},{'&',1},{'*',1},{'+',1},{'/',1}, -            {':',1},{'<',1},{'>',1},{'?',1},{'@',1}, -            {'\\',1},{'^',1},{'`',1},{'~',1}], +    PTs2 = [{'#',{1,1}},{'&',{1,2}},{'*',{1,3}},{'+',{1,4}},{'/',{1,5}}, +            {':',{1,6}},{'<',{1,7}},{'>',{1,8}},{'?',{1,9}},{'@',{1,10}}, +            {'\\',{1,11}},{'^',{1,12}},{'`',{1,13}},{'~',{1,14}}],      ?line test_string("#&*+/:<>?@\\^`~", PTs2), -    ?line test_string(".. ", [{'..',1}]), -    ?line test("1 .. 2"), -    ?line test_string("...", [{'...',1}]), +    test_string(".. ", [{'..',{1,1}}]), +    test_string("1 .. 2", +                [{integer,{1,1},1},{'..',{1,3}},{integer,{1,6},2}]), +    test_string("...", [{'...',{1,1}}]),      ok.  comments() ->      ?line test("a %%\n b"),      ?line {ok,[],1} = erl_scan:string("%"),      ?line test("a %%\n b"), -    ?line {ok,[{atom,_,a},{atom,_,b}],{2,3}} = +    {ok,[{atom,{1,1},a},{atom,{2,2},b}],{2,3}} =          erl_scan:string("a %%\n b",{1,1}), -    ?line {ok,[{atom,_,a},{comment,_,"%%"},{atom,_,b}],{2,3}} = +    {ok,[{atom,{1,1},a},{comment,{1,3},"%%"},{atom,{2,2},b}],{2,3}} =          erl_scan:string("a %%\n b",{1,1}, [return_comments]), -    ?line {ok,[{atom,_,a}, -               {white_space,_," "}, -               {white_space,_,"\n "}, -               {atom,_,b}], -           {2,3}} = +    {ok,[{atom,{1,1},a}, +         {white_space,{1,2}," "}, +         {white_space,{1,5},"\n "}, +         {atom,{2,2},b}], +     {2,3}} =          erl_scan:string("a %%\n b",{1,1},[return_white_spaces]), -    ?line {ok,[{atom,_,a}, -               {white_space,_," "}, -               {comment,_,"%%"}, -               {white_space,_,"\n "}, -               {atom,_,b}], -           {2,3}} = erl_scan:string("a %%\n b",{1,1},[return]), +    {ok,[{atom,{1,1},a}, +         {white_space,{1,2}," "}, +         {comment,{1,3},"%%"}, +         {white_space,{1,5},"\n "}, +         {atom,{2,2},b}], +     {2,3}} = erl_scan:string("a %%\n b",{1,1},[return]),      ok.  errors() ->      ?line {error,{1,erl_scan,{string,$',"qa"}},1} = erl_scan:string("'qa"), %' +    {error,{{1,1},erl_scan,{string,$',"qa"}},{1,4}} = %' +        erl_scan:string("'qa", {1,1}, []), %'      ?line {error,{1,erl_scan,{string,$","str"}},1} = %"          erl_scan:string("\"str"), %" +    {error,{{1,1},erl_scan,{string,$","str"}},{1,5}} = %" +        erl_scan:string("\"str", {1,1}, []), %"      ?line {error,{1,erl_scan,char},1} = erl_scan:string("$"), -    ?line test_string([34,65,200,34], [{string,1,"AÈ"}]), -    ?line test_string("\\", [{'\\',1}]), +    {error,{{1,1},erl_scan,char},{1,2}} = erl_scan:string("$", {1,1}, []), +    test_string([34,65,200,34], [{string,{1,1},"AÈ"}]), +    test_string("\\", [{'\\',{1,1}}]),      ?line {'EXIT',_} =          (catch {foo, erl_scan:string('$\\a', {1,1})}), % type error      ?line {'EXIT',_} = @@ -304,7 +311,7 @@ errors() ->  integers() ->      [begin           I = list_to_integer(S), -         Ts = [{integer,1,I}], +         Ts = [{integer,{1,1},I}],           ?line test_string(S, Ts)       end || S <- [[N] || N <- lists:seq($0, $9)] ++ ["2323","000"] ],      ok. @@ -313,14 +320,16 @@ base_integers() ->      [begin           B = list_to_integer(BS),           I = erlang:list_to_integer(S, B), -         Ts = [{integer,1,I}], +         Ts = [{integer,{1,1},I}],           ?line test_string(BS++"#"++S, Ts)       end || {BS,S} <- [{"2","11"}, {"5","23234"}, {"12","05a"},                         {"16","abcdef"}, {"16","ABCDEF"}] ],      ?line {error,{1,erl_scan,{base,1}},1} = erl_scan:string("1#000"), +    {error,{{1,1},erl_scan,{base,1}},{1,2}} = +        erl_scan:string("1#000", {1,1}, []), -    ?line test_string("12#bc", [{integer,1,11},{atom,1,c}]), +    test_string("12#bc", [{integer,{1,1},11},{atom,{1,5},c}]),      [begin           Str = BS ++ "#" ++ S, @@ -329,40 +338,53 @@ base_integers() ->       end || {BS,S} <- [{"3","3"},{"15","f"}, {"12","c"}] ],      ?line {ok,[{integer,1,239},{'@',1}],1} = erl_scan:string("16#ef@"), -    ?line {ok,[{integer,1,14},{atom,1,g@}],1} = erl_scan:string("16#eg@"), +    {ok,[{integer,{1,1},239},{'@',{1,6}}],{1,7}} = +        erl_scan:string("16#ef@", {1,1}, []), +    {ok,[{integer,{1,1},14},{atom,{1,5},g@}],{1,7}} = +        erl_scan:string("16#eg@", {1,1}, []),      ok.  floats() ->      [begin           F = list_to_float(FS), -         Ts = [{float,1,F}], +         Ts = [{float,{1,1},F}],           ?line test_string(FS, Ts)       end || FS <- ["1.0","001.17","3.31200","1.0e0","1.0E17",                     "34.21E-18", "17.0E+14"]], -    ?line test_string("1.e2", [{integer,1,1},{'.',1},{atom,1,e2}]), +    test_string("1.e2", [{integer,{1,1},1},{'.',{1,2}},{atom,{1,3},e2}]),      ?line {error,{1,erl_scan,{illegal,float}},1} =          erl_scan:string("1.0e400"), +    {error,{{1,1},erl_scan,{illegal,float}},{1,8}} = +        erl_scan:string("1.0e400", {1,1}, []),      [begin -         ?line {error,{1,erl_scan,{illegal,float}},1} = erl_scan:string(S) +         {error,{1,erl_scan,{illegal,float}},1} = erl_scan:string(S), +         {error,{{1,1},erl_scan,{illegal,float}},{1,_}} = +             erl_scan:string(S, {1,1}, [])       end || S <- ["1.14Ea"]],      ok.  dots() -> -    Dot = [{".",    {ok,[{dot,1}],1}}, -           {". ",   {ok,[{dot,1}],1}}, -           {".\n",  {ok,[{dot,1}],2}}, -           {".%",   {ok,[{dot,1}],1}}, -           {".\210",{ok,[{dot,1}],1}}, -           {".% öh",{ok,[{dot,1}],1}}, -           {".%\n", {ok,[{dot,1}],2}}, -           {".$",   {error,{1,erl_scan,char},1}}, -           {".$\\", {error,{1,erl_scan,char},1}}, -           {".a",   {ok,[{'.',1},{atom,1,a}],1}} +    Dot = [{".",    {ok,[{dot,1}],1}, {ok,[{dot,{1,1}}],{1,2}}}, +           {". ",   {ok,[{dot,1}],1}, {ok,[{dot,{1,1}}],{1,3}}}, +           {".\n",  {ok,[{dot,1}],2}, {ok,[{dot,{1,1}}],{2,1}}}, +           {".%",   {ok,[{dot,1}],1}, {ok,[{dot,{1,1}}],{1,3}}}, +           {".\210",{ok,[{dot,1}],1}, {ok,[{dot,{1,1}}],{1,3}}}, +           {".% öh",{ok,[{dot,1}],1}, {ok,[{dot,{1,1}}],{1,6}}}, +           {".%\n", {ok,[{dot,1}],2}, {ok,[{dot,{1,1}}],{2,1}}}, +           {".$",   {error,{1,erl_scan,char},1}, +                    {error,{{1,2},erl_scan,char},{1,3}}}, +           {".$\\", {error,{1,erl_scan,char},1}, +                    {error,{{1,2},erl_scan,char},{1,4}}}, +           {".a",   {ok,[{'.',1},{atom,1,a}],1}, +                    {ok,[{'.',{1,1}},{atom,{1,2},a}],{1,3}}}            ], -    ?line [R = erl_scan:string(S) || {S, R} <- Dot], +    [begin +         R = erl_scan:string(S), +         R2 = erl_scan:string(S, {1,1}, []) +     end || {S, R, R2} <- Dot],      ?line {ok,[{dot,_}=T1],{1,2}} = erl_scan:string(".", {1,1}, text),      ?line [{column,1},{length,1},{line,1},{text,"."}] = @@ -379,55 +401,55 @@ dots() ->      ?line {error,{{1,2},erl_scan,char},{1,4}} =          erl_scan:string(".$\\", {1,1}), -    ?line test(". "), -    ?line test(".  "), -    ?line test(".\n"), -    ?line test(".\n\n"), -    ?line test(".\n\r"), -    ?line test(".\n\n\n"), -    ?line test(".\210"), -    ?line test(".%\n"), -    ?line test(".a"), - -    ?line test("%. \n. "), +    test_string(". ", [{dot,{1,1}}]), +    test_string(".  ", [{dot,{1,1}}]), +    test_string(".\n", [{dot,{1,1}}]), +    test_string(".\n\n", [{dot,{1,1}}]), +    test_string(".\n\r", [{dot,{1,1}}]), +    test_string(".\n\n\n", [{dot,{1,1}}]), +    test_string(".\210", [{dot,{1,1}}]), +    test_string(".%\n", [{dot,{1,1}}]), +    test_string(".a", [{'.',{1,1}},{atom,{1,2},a}]), + +    test_string("%. \n. ", [{dot,{2,1}}]),      ?line {more,C} = erl_scan:tokens([], "%. ",{1,1}, return), -    ?line {done,{ok,[{comment,_,"%. "}, -                     {white_space,_,"\n"}, -                     {dot,_}], -                 {2,3}}, ""} = +    {done,{ok,[{comment,{1,1},"%. "}, +               {white_space,{1,4},"\n"}, +               {dot,{2,1}}], +           {2,3}}, ""} =          erl_scan:tokens(C, "\n. ", {1,1}, return), % any loc, any options      ?line [test_string(S, R) || -              {S, R} <- [{".$\n",   [{'.',1},{char,1,$\n}]}, -                         {"$\\\n",  [{char,1,$\n}]}, -                         {"'\\\n'", [{atom,1,'\n'}]}, -                         {"$\n",    [{char,1,$\n}]}] ], +              {S, R} <- [{".$\n",   [{'.',{1,1}},{char,{1,2},$\n}]}, +                         {"$\\\n",  [{char,{1,1},$\n}]}, +                         {"'\\\n'", [{atom,{1,1},'\n'}]}, +                         {"$\n",    [{char,{1,1},$\n}]}] ],      ok.  chars() ->      [begin           L = lists:flatten(io_lib:format("$\\~.8b", [C])), -         Ts = [{char,1,C}], +         Ts = [{char,{1,1},C}],           ?line test_string(L, Ts)       end || C <- lists:seq(0, 255)],      %% Leading zeroes...      [begin           L = lists:flatten(io_lib:format("$\\~3.8.0b", [C])), -         Ts = [{char,1,C}], +         Ts = [{char,{1,1},C}],           ?line test_string(L, Ts)       end || C <- lists:seq(0, 255)],      %% $\^\n now increments the line...      [begin           L = "$\\^" ++ [C], -         Ts = [{char,1,C band 2#11111}], +         Ts = [{char,{1,1},C band 2#11111}],           ?line test_string(L, Ts)       end || C <- lists:seq(0, 255)],      [begin           L = "$\\" ++ [C], -         Ts = [{char,1,V}], +         Ts = [{char,{1,1},V}],           ?line test_string(L, Ts)       end || {C,V} <- [{$n,$\n}, {$r,$\r}, {$t,$\t}, {$v,$\v},                        {$b,$\b}, {$f,$\f}, {$e,$\e}, {$s,$\s}, @@ -440,45 +462,45 @@ chars() ->      No = EC ++ Ds ++ X ++ New,      [begin           L = "$\\" ++ [C], -         Ts = [{char,1,C}], +         Ts = [{char,{1,1},C}],           ?line test_string(L, Ts)       end || C <- lists:seq(0, 255) -- No],      [begin           L = "'$\\" ++ [C] ++ "'", -         Ts = [{atom,1,list_to_atom("$"++[C])}], +         Ts = [{atom,{1,1},list_to_atom("$"++[C])}],           ?line test_string(L, Ts)       end || C <- lists:seq(0, 255) -- No], -    ?line test_string("\"\\013a\\\n\"", [{string,1,"\va\n"}]), +    test_string("\"\\013a\\\n\"", [{string,{1,1},"\va\n"}]), -    ?line test_string("'\n'", [{atom,1,'\n'}]), -    ?line test_string("\"\n\a\"", [{string,1,"\na"}]), +    test_string("'\n'", [{atom,{1,1},'\n'}]), +    test_string("\"\n\a\"", [{string,{1,1},"\na"}]),      %% No escape      [begin           L = "$" ++ [C], -         Ts = [{char,1,C}], +         Ts = [{char,{1,1},C}],           ?line test_string(L, Ts)       end || C <- lists:seq(0, 255) -- (No ++ [$\\])], -    ?line test_string("$\n", [{char,1,$\n}]), +    test_string("$\n", [{char,{1,1},$\n}]),      ?line {error,{{1,1},erl_scan,char},{1,4}} =          erl_scan:string("$\\^",{1,1}), -    ?line test_string("$\\\n", [{char,1,$\n}]), +    test_string("$\\\n", [{char,{1,1},$\n}]),      %% Robert's scanner returns line 1: -    ?line test_string("$\\\n", [{char,1,$\n}]), -    ?line test_string("$\n\n", [{char,1,$\n}]), +    test_string("$\\\n", [{char,{1,1},$\n}]), +    test_string("$\n\n", [{char,{1,1},$\n}]),      ?line test("$\n\n"),      ok.  variables() -> -    ?line test_string("     \237_Aouåeiyäö", [{var,1,'_Aouåeiyäö'}]), -    ?line test_string("A_b_c@", [{var,1,'A_b_c@'}]), -    ?line test_string("V@2", [{var,1,'V@2'}]), -    ?line test_string("ABDÀ", [{var,1,'ABDÀ'}]), -    ?line test_string("Ärlig Östen", [{var,1,'Ärlig'},{var,1,'Östen'}]), +    test_string("     \237_Aouåeiyäö", [{var,{1,7},'_Aouåeiyäö'}]), +    test_string("A_b_c@", [{var,{1,1},'A_b_c@'}]), +    test_string("V@2", [{var,{1,1},'V@2'}]), +    test_string("ABDÀ", [{var,{1,1},'ABDÀ'}]), +    test_string("Ärlig Östen", [{var,{1,1},'Ärlig'},{var,{1,7},'Östen'}]),      ok.  eof() -> @@ -508,11 +530,25 @@ eof() ->      ?line {done,{ok,[{atom,1,a}],1},eof} =          erl_scan:tokens(C5,eof,1), +    %% With column. +    {more, C6} = erl_scan:tokens([], "a", {1,1}), +    %% An error before R13A. +    %% {done,{error,{1,erl_scan,scan},1},eof} = +    {done,{ok,[{atom,{1,1},a}],{1,2}},eof} = +        erl_scan:tokens(C6,eof,1), +      %% A dot followed by eof is special:      ?line {more, C} = erl_scan:tokens([], "a.", 1),      ?line {done,{ok,[{atom,1,a},{dot,1}],1},eof} = erl_scan:tokens(C,eof,1),      ?line {ok,[{atom,1,foo},{dot,1}],1} = erl_scan:string("foo."), +    %% With column. +    {more, CCol} = erl_scan:tokens([], "a.", {1,1}), +    {done,{ok,[{atom,{1,1},a},{dot,{1,2}}],{1,3}},eof} = +        erl_scan:tokens(CCol,eof,1), +    {ok,[{atom,{1,1},foo},{dot,{1,4}}],{1,5}} = +        erl_scan:string("foo.", {1,1}, []), +      ok.  illegal() -> @@ -816,34 +852,34 @@ unicode() ->          erl_scan:string([1089]),      ?line {error,{{1,1},erl_scan,{illegal,character}},{1,2}} =          erl_scan:string([1089], {1,1}), -    ?line {error,{1,erl_scan,{illegal,atom}},1} = +    {error,{1,erl_scan,{illegal,atom}},1} =          erl_scan:string("'a"++[1089]++"b'", 1), -    ?line {error,{{1,1},erl_scan,{illegal,atom}},{1,6}} = +    {error,{{1,1},erl_scan,{illegal,atom}},{1,6}} =          erl_scan:string("'a"++[1089]++"b'", {1,1}),      ?line test("\"a"++[1089]++"b\""), -    ?line {ok,[{char,1,1}],1} = +    {ok,[{char,1,1}],1} =          erl_scan:string([$$,$\\,$^,1089], 1), -    ?line {error,{1,erl_scan,Error},1} = +    {error,{1,erl_scan,Error},1} =          erl_scan:string("\"qa\x{aaa}", 1), -    ?line "unterminated string starting with \"qa"++[2730]++"\"" = +    "unterminated string starting with \"qa"++[2730]++"\"" =          erl_scan:format_error(Error),      ?line {error,{{1,1},erl_scan,_},{1,11}} =          erl_scan:string("\"qa\\x{aaa}",{1,1}), -    ?line {error,{{1,1},erl_scan,{illegal,atom}},{1,12}} = +    {error,{{1,1},erl_scan,{illegal,atom}},{1,12}} =          erl_scan:string("'qa\\x{aaa}'",{1,1}), -    ?line {ok,[{char,1,1089}],1} = +    {ok,[{char,1,1089}],1} =          erl_scan:string([$$,1089], 1), -    ?line {ok,[{char,1,1089}],1} = +    {ok,[{char,1,1089}],1} =          erl_scan:string([$$,$\\,1089], 1),      Qs = "$\\x{aaa}", -    ?line {ok,[{char,1,$\x{aaa}}],1} = +    {ok,[{char,1,$\x{aaa}}],1} =          erl_scan:string(Qs, 1), -    ?line {ok,[Q2],{1,9}} = +    {ok,[Q2],{1,9}} =          erl_scan:string("$\\x{aaa}", {1,1}, [text]), -    ?line [{category,char},{column,1},{length,8}, +    [{category,char},{column,1},{length,8},             {line,1},{symbol,16#aaa},{text,Qs}] =          erl_scan:token_info(Q2), @@ -1164,7 +1200,13 @@ otp_11807(Config) when is_list(Config) ->           (catch erl_parse:abstract("string", [{encoding,bad}])),     ok. -test_string(String, Expected) -> +test_string(String, ExpectedWithCol) -> +    {ok, ExpectedWithCol, _EndWithCol} = erl_scan:string(String, {1, 1}, []), +    Expected = [ begin +                     {L,_C} = element(2, T), +                     setelement(2, T, L) +                 end +                    || T <- ExpectedWithCol ],      {ok, Expected, _End} = erl_scan:string(String),      test(String). diff --git a/lib/stdlib/test/filelib_SUITE.erl b/lib/stdlib/test/filelib_SUITE.erl index 8203a03a7a..bd313390b3 100644 --- a/lib/stdlib/test/filelib_SUITE.erl +++ b/lib/stdlib/test/filelib_SUITE.erl @@ -1,7 +1,7 @@  %%  %% %CopyrightBegin%  %% -%% Copyright Ericsson AB 2005-2013. All Rights Reserved. +%% Copyright Ericsson AB 2005-2014. All Rights Reserved.  %%  %% The contents of this file are subject to the Erlang Public License,  %% Version 1.1, (the "License"); you may not use this file except in @@ -23,7 +23,8 @@  	 init_per_group/2,end_per_group/2,  	 init_per_testcase/2,end_per_testcase/2,  	 wildcard_one/1,wildcard_two/1,wildcard_errors/1, -	 fold_files/1,otp_5960/1,ensure_dir_eexist/1,symlinks/1]). +	 fold_files/1,otp_5960/1,ensure_dir_eexist/1,ensure_dir_symlink/1, +	 wildcard_symlink/1, is_file_symlink/1, file_props_symlink/1]).  -import(lists, [foreach/2]). @@ -43,7 +44,8 @@ suite() -> [{ct_hooks,[ts_install_cth]}].  all() ->       [wildcard_one, wildcard_two, wildcard_errors, -     fold_files, otp_5960, ensure_dir_eexist, symlinks]. +     fold_files, otp_5960, ensure_dir_eexist, ensure_dir_symlink, +     wildcard_symlink, is_file_symlink, file_props_symlink].  groups() ->       []. @@ -86,6 +88,7 @@ wildcard_two(Config) when is_list(Config) ->      ?line ok = file:make_dir(Dir),      ?line do_wildcard_1(Dir, fun(Wc) -> io:format("~p~n",[{Wc,Dir, X = filelib:wildcard(Wc, Dir)}]),X  end),      ?line do_wildcard_1(Dir, fun(Wc) -> filelib:wildcard(Wc, Dir++"/") end), +    ?line do_wildcard_1(Dir, fun(Wc) -> filelib:wildcard(Wc, Dir++"/.") end),      case os:type() of  	{win32,_} ->  	    ok; @@ -367,9 +370,28 @@ ensure_dir_eexist(Config) when is_list(Config) ->      ?line {error, eexist} = filelib:ensure_dir(NeedFileB),      ok. -symlinks(Config) when is_list(Config) -> +ensure_dir_symlink(Config) when is_list(Config) ->      PrivDir = ?config(priv_dir, Config), -    Dir = filename:join(PrivDir, ?MODULE_STRING++"_symlinks"), +    Dir = filename:join(PrivDir, "ensure_dir_symlink"), +    Name = filename:join(Dir, "same_name_as_file_and_dir"), +    ok = filelib:ensure_dir(Name), +    ok = file:write_file(Name, <<"some string\n">>), +    %% With a symlink to the directory. +    Symlink = filename:join(PrivDir, "ensure_dir_symlink_link"), +    case file:make_symlink(Dir, Symlink) of +        {error,enotsup} -> +            {skip,"Symlinks not supported on this platform"}; +        {error,eperm} -> +            {win32,_} = os:type(), +            {skip,"Windows user not privileged to create symlinks"}; +        ok -> +            SymlinkedName = filename:join(Symlink, "same_name_as_file_and_dir"), +            ok = filelib:ensure_dir(SymlinkedName) +    end. + +wildcard_symlink(Config) when is_list(Config) -> +    PrivDir = ?config(priv_dir, Config), +    Dir = filename:join(PrivDir, ?MODULE_STRING++"_wildcard_symlink"),      SubDir = filename:join(Dir, "sub"),      AFile = filename:join(SubDir, "a_file"),      Alias = filename:join(Dir, "symlink"), @@ -387,6 +409,18 @@ symlinks(Config) when is_list(Config) ->  		basenames(Dir, filelib:wildcard(filename:join(Dir, "*"))),  	    ["symlink"] =  		basenames(Dir, filelib:wildcard(filename:join(Dir, "symlink"))), +	    ["sub","symlink"] = +		basenames(Dir, filelib:wildcard(filename:join(Dir, "*"), +						erl_prim_loader)), +	    ["symlink"] = +		basenames(Dir, filelib:wildcard(filename:join(Dir, "symlink"), +						erl_prim_loader)), +	    ["sub","symlink"] = +		basenames(Dir, filelib:wildcard(filename:join(Dir, "*"), +						prim_file)), +	    ["symlink"] = +		basenames(Dir, filelib:wildcard(filename:join(Dir, "symlink"), +						prim_file)),  	    ok = file:delete(AFile),  	    %% The symlink should still be visible even when its target  	    %% has been deleted. @@ -394,6 +428,18 @@ symlinks(Config) when is_list(Config) ->  		basenames(Dir, filelib:wildcard(filename:join(Dir, "*"))),  	    ["symlink"] =  		basenames(Dir, filelib:wildcard(filename:join(Dir, "symlink"))), +	    ["sub","symlink"] = +		basenames(Dir, filelib:wildcard(filename:join(Dir, "*"), +						erl_prim_loader)), +	    ["symlink"] = +		basenames(Dir, filelib:wildcard(filename:join(Dir, "symlink"), +						erl_prim_loader)), +	    ["sub","symlink"] = +		basenames(Dir, filelib:wildcard(filename:join(Dir, "*"), +						prim_file)), +	    ["symlink"] = +		basenames(Dir, filelib:wildcard(filename:join(Dir, "symlink"), +						prim_file)),  	    ok      end. @@ -402,3 +448,60 @@ basenames(Dir, Files) ->  	 Dir = filename:dirname(F),  	 filename:basename(F)       end || F <- Files]. + +is_file_symlink(Config) -> +    PrivDir = ?config(priv_dir, Config), +    Dir = filename:join(PrivDir, ?MODULE_STRING++"_is_file_symlink"), +    SubDir = filename:join(Dir, "sub"), +    AFile = filename:join(SubDir, "a_file"), +    DirAlias = filename:join(Dir, "dir_symlink"), +    FileAlias = filename:join(Dir, "file_symlink"), +    ok = file:make_dir(Dir), +    ok = file:make_dir(SubDir), +    ok = file:write_file(AFile, "not that big\n"), +    case file:make_symlink(SubDir, DirAlias) of +	{error, enotsup} -> +	    {skip, "Links not supported on this platform"}; +	{error, eperm} -> +	    {win32,_} = os:type(), +	    {skip, "Windows user not privileged to create symlinks"}; +	ok -> +	    true = filelib:is_dir(DirAlias), +	    true = filelib:is_dir(DirAlias, erl_prim_loader), +	    true = filelib:is_dir(DirAlias, prim_file), +	    true = filelib:is_file(DirAlias), +	    true = filelib:is_file(DirAlias, erl_prim_loader), +	    true = filelib:is_file(DirAlias, prim_file), +	    ok = file:make_symlink(AFile,FileAlias), +	    true = filelib:is_file(FileAlias), +	    true = filelib:is_file(FileAlias, erl_prim_loader), +	    true = filelib:is_file(FileAlias, prim_file), +	    true = filelib:is_regular(FileAlias), +	    true = filelib:is_regular(FileAlias, erl_prim_loader), +	    true = filelib:is_regular(FileAlias, prim_file), +	    ok +    end. + +file_props_symlink(Config) -> +    PrivDir = ?config(priv_dir, Config), +    Dir = filename:join(PrivDir, ?MODULE_STRING++"_file_props_symlink"), +    AFile = filename:join(Dir, "a_file"), +    Alias = filename:join(Dir, "symlink"), +    ok = file:make_dir(Dir), +    ok = file:write_file(AFile, "not that big\n"), +    case file:make_symlink(AFile, Alias) of +	{error, enotsup} -> +	    {skip, "Links not supported on this platform"}; +	{error, eperm} -> +	    {win32,_} = os:type(), +	    {skip, "Windows user not privileged to create symlinks"}; +	ok -> +	    {_,_} = LastMod = filelib:last_modified(AFile), +	    LastMod = filelib:last_modified(Alias), +	    LastMod = filelib:last_modified(Alias, erl_prim_loader), +	    LastMod = filelib:last_modified(Alias, prim_file), +	    FileSize = filelib:file_size(AFile), +	    FileSize = filelib:file_size(Alias), +	    FileSize = filelib:file_size(Alias, erl_prim_loader), +	    FileSize = filelib:file_size(Alias, prim_file) +    end. diff --git a/lib/stdlib/test/gen_fsm_SUITE.erl b/lib/stdlib/test/gen_fsm_SUITE.erl index 8aeec07ae8..39f0442824 100644 --- a/lib/stdlib/test/gen_fsm_SUITE.erl +++ b/lib/stdlib/test/gen_fsm_SUITE.erl @@ -1,7 +1,7 @@  %%  %% %CopyrightBegin%  %% -%% Copyright Ericsson AB 1996-2013. All Rights Reserved. +%% Copyright Ericsson AB 1996-2014. All Rights Reserved.  %%  %% The contents of this file are subject to the Erlang Public License,  %% Version 1.1, (the "License"); you may not use this file except in @@ -31,7 +31,9 @@  -export([shutdown/1]). --export([ sys1/1, call_format_status/1, error_format_status/1, get_state/1, replace_state/1]). +-export([ sys1/1, +	  call_format_status/1, error_format_status/1, terminate_crash_format/1, +	  get_state/1, replace_state/1]).  -export([hibernate/1,hiber_idle/3,hiber_wakeup/3,hiber_idle/2,hiber_wakeup/2]). @@ -66,7 +68,8 @@ groups() ->         start8, start9, start10, start11, start12]},       {abnormal, [], [abnormal1, abnormal2]},       {sys, [], -      [sys1, call_format_status, error_format_status, get_state, replace_state]}]. +      [sys1, call_format_status, error_format_status, terminate_crash_format, +       get_state, replace_state]}].  init_per_suite(Config) ->      Config. @@ -403,7 +406,7 @@ error_format_status(Config) when is_list(Config) ->      receive  	{error,_GroupLeader,{Pid,  			     "** State machine"++_, -			     [Pid,{_,_,badreturn},idle,StateData,_]}} -> +			     [Pid,{_,_,badreturn},idle,{formatted,StateData},_]}} ->  	    ok;  	Other ->  	    ?line io:format("Unexpected: ~p", [Other]), @@ -413,6 +416,29 @@ error_format_status(Config) when is_list(Config) ->      process_flag(trap_exit, OldFl),      ok. +terminate_crash_format(Config) when is_list(Config) -> +    error_logger_forwarder:register(), +    OldFl = process_flag(trap_exit, true), +    StateData = crash_terminate, +    {ok, Pid} = gen_fsm:start(gen_fsm_SUITE, {state_data, StateData}, []), +    stop_it(Pid), +    receive +	{error,_GroupLeader,{Pid, +			     "** State machine"++_, +			     [Pid,{_,_,_},idle,{formatted, StateData},_]}} -> +	    ok; +	Other -> +	    io:format("Unexpected: ~p", [Other]), +	    ?t:fail() +    after 5000 -> +	    io:format("Timeout: expected error logger msg", []), +	    ?t:fail() +    end, +    _ = ?t:messages_get(), +    process_flag(trap_exit, OldFl), +    ok. + +  get_state(Config) when is_list(Config) ->      State = self(),      {ok, Pid} = gen_fsm:start(?MODULE, {state_data, State}, []), @@ -867,7 +893,8 @@ init({state_data, StateData}) ->  init(_) ->      {ok, idle, state_data}. - +terminate(_, _State, crash_terminate) -> +    exit({crash, terminate});  terminate({From, stopped}, State, _Data) ->      From ! {self(), {stopped, State}},      ok; @@ -1005,6 +1032,6 @@ handle_sync_event({get, _Pid}, _From, State, Data) ->      {reply, {state, State, Data}, State, Data}.  format_status(terminate, [_Pdict, StateData]) -> -    StateData; +    {formatted, StateData};  format_status(normal, [_Pdict, _StateData]) ->      [format_status_called]. diff --git a/lib/stdlib/test/gen_server_SUITE.erl b/lib/stdlib/test/gen_server_SUITE.erl index 960e7f60e7..0f03fda30a 100644 --- a/lib/stdlib/test/gen_server_SUITE.erl +++ b/lib/stdlib/test/gen_server_SUITE.erl @@ -1,7 +1,7 @@  %%  %% %CopyrightBegin%  %% -%% Copyright Ericsson AB 1996-2013. All Rights Reserved. +%% Copyright Ericsson AB 1996-2014. All Rights Reserved.  %%  %% The contents of this file are subject to the Erlang Public License,  %% Version 1.1, (the "License"); you may not use this file except in @@ -32,7 +32,8 @@  	 spec_init_local_registered_parent/1,   	 spec_init_global_registered_parent/1,  	 otp_5854/1, hibernate/1, otp_7669/1, call_format_status/1, -	 error_format_status/1, get_state/1, replace_state/1, call_with_huge_message_queue/1 +	 error_format_status/1, terminate_crash_format/1, +	 get_state/1, replace_state/1, call_with_huge_message_queue/1  	]).  % spawn export @@ -56,7 +57,8 @@ all() ->       call_remote_n3, spec_init,       spec_init_local_registered_parent,       spec_init_global_registered_parent, otp_5854, hibernate, -     otp_7669, call_format_status, error_format_status, +     otp_7669, +     call_format_status, error_format_status, terminate_crash_format,       get_state, replace_state,       call_with_huge_message_queue]. @@ -273,7 +275,9 @@ crash(Config) when is_list(Config) ->      receive  	{error,_GroupLeader4,{Pid4,  			      "** Generic server"++_, -			      [Pid4,crash,state4,crashed]}} -> +			      [Pid4,crash,{formatted, state4}, +			       {crashed,[{?MODULE,handle_call,3,_} +					 |_Stacktrace]}]}} ->  	    ok;  	Other4a ->   	    ?line io:format("Unexpected: ~p", [Other4a]), @@ -1024,7 +1028,9 @@ error_format_status(Config) when is_list(Config) ->      receive  	{error,_GroupLeader,{Pid,  			     "** Generic server"++_, -			     [Pid,crash,State,crashed]}} -> +			     [Pid,crash,{formatted, State}, +			      {crashed,[{?MODULE,handle_call,3,_} +					|_Stacktrace]}]}} ->  	    ok;  	Other ->  	    ?line io:format("Unexpected: ~p", [Other]), @@ -1034,6 +1040,33 @@ error_format_status(Config) when is_list(Config) ->      process_flag(trap_exit, OldFl),      ok. +%% Verify that error when terminating correctly calls our format_status/2 fun +%% +terminate_crash_format(Config) when is_list(Config) -> +    error_logger_forwarder:register(), +    OldFl = process_flag(trap_exit, true), +    State = crash_terminate, +    {ok, Pid} = gen_server:start_link(?MODULE, {state, State}, []), +    gen_server:call(Pid, stop), +    receive {'EXIT', Pid, {crash, terminate}} -> ok end, +    receive +	{error,_GroupLeader,{Pid, +			     "** Generic server"++_, +			     [Pid,stop, {formatted, State}, +			      {{crash, terminate},[{?MODULE,terminate,2,_} +						  |_Stacktrace]}]}} -> +	    ok; +	Other -> +	    io:format("Unexpected: ~p", [Other]), +	    ?t:fail() +    after 5000 -> +	    io:format("Timeout: expected error logger msg", []), +	    ?t:fail() +    end, +    ?t:messages_get(), +    process_flag(trap_exit, OldFl), +    ok. +  %% Verify that sys:get_state correctly returns gen_server state  %%  get_state(suite) -> @@ -1323,10 +1356,12 @@ terminate({From, stopped}, _State) ->  terminate({From, stopped_info}, _State) ->      From ! {self(), stopped_info},      ok; +terminate(_, crash_terminate) -> +    exit({crash, terminate});  terminate(_Reason, _State) ->      ok.  format_status(terminate, [_PDict, State]) -> -    State; +    {formatted, State};  format_status(normal, [_PDict, _State]) ->      format_status_called. diff --git a/lib/stdlib/test/io_SUITE.erl b/lib/stdlib/test/io_SUITE.erl index 5a8971c071..2203dd8f51 100644 --- a/lib/stdlib/test/io_SUITE.erl +++ b/lib/stdlib/test/io_SUITE.erl @@ -30,7 +30,8 @@  	 io_fread_newlines/1, otp_8989/1, io_lib_fread_literal/1,  	 printable_range/1,  	 io_lib_print_binary_depth_one/1, otp_10302/1, otp_10755/1, -	 otp_10836/1]). +         otp_10836/1, io_lib_width_too_small/1, +         io_with_huge_message_queue/1]).  -export([pretty/2]). @@ -69,7 +70,8 @@ all() ->       io_lib_collect_line_3_wb, cr_whitespace_in_string,       io_fread_newlines, otp_8989, io_lib_fread_literal,       printable_range, -     io_lib_print_binary_depth_one, otp_10302, otp_10755, otp_10836]. +     io_lib_print_binary_depth_one, otp_10302, otp_10755, otp_10836, +     io_lib_width_too_small, io_with_huge_message_queue].  groups() ->       []. @@ -2213,3 +2215,49 @@ compile_file(File, Text, Config) ->      try compile:file(Fname, [return])      after ok %file:delete(Fname)      end. + +io_lib_width_too_small(Config) -> +    "**" = lists:flatten(io_lib:format("~2.3w", [3.14])), +    "**" = lists:flatten(io_lib:format("~2.5w", [3.14])), +    ok. + +%% Test that the time for a huge message queue is not +%% significantly slower than with an empty message queue. +io_with_huge_message_queue(Config) when is_list(Config) -> +    case test_server:is_native(gen) of +	true -> +	    {skip, +	     "gen is native - huge message queue optimization " +	     "is not implemented"}; +	false -> +	    do_io_with_huge_message_queue(Config) +    end. + +do_io_with_huge_message_queue(Config) -> +    PrivDir = ?privdir(Config), +    File = filename:join(PrivDir, "slask"), +    {ok, F1} = file:open(File, [write]), + +    {Time,ok} = timer:tc(fun() -> writes(1000, F1) end), + +    [self() ! {msg,N} || N <- lists:seq(1, 500000)], +    erlang:garbage_collect(), +    {NewTime,ok} = timer:tc(fun() -> writes(1000, F1) end), +    file:close(F1), +    io:format("Time for empty message queue: ~p", [Time]), +    io:format("Time for huge message queue: ~p", [NewTime]), + +    IsCover = test_server:is_cover(), +    case (NewTime+1) / (Time+1) of +	Q when Q < 10; IsCover -> +	    ok; +	Q -> +	    io:format("Q = ~p", [Q]), +	    ?t:fail() +    end, +    ok. + +writes(0, _) -> ok; +writes(N, F1) -> +    file:write(F1, "hello\n"), +    writes(N - 1, F1). diff --git a/lib/stdlib/test/maps_SUITE.erl b/lib/stdlib/test/maps_SUITE.erl index c826ee731a..dda20a615b 100644 --- a/lib/stdlib/test/maps_SUITE.erl +++ b/lib/stdlib/test/maps_SUITE.erl @@ -24,10 +24,7 @@  -include_lib("test_server/include/test_server.hrl"). -% Default timetrap timeout (set in init_per_testcase). -% This should be set relatively high (10-15 times the expected -% max testcasetime). --define(default_timeout, ?t:minutes(4)). +-define(default_timeout, ?t:minutes(1)).  % Test server specific exports  -export([all/0]). @@ -37,13 +34,13 @@  -export([init_per_testcase/2]).  -export([end_per_testcase/2]). --export([get3/1]). +-export([t_get_3/1,t_with_2/1,t_without_2/1]).  suite() ->      [{ct_hooks, [ts_install_cth]}].  all() -> -    [get3]. +    [t_get_3,t_with_2,t_without_2].  init_per_suite(Config) ->      Config. @@ -52,7 +49,7 @@ end_per_suite(_Config) ->      ok.  init_per_testcase(_Case, Config) -> -    ?line Dog=test_server:timetrap(?default_timeout), +    Dog=test_server:timetrap(?default_timeout),      [{watchdog, Dog}|Config].  end_per_testcase(_Case, Config) -> @@ -60,10 +57,24 @@ end_per_testcase(_Case, Config) ->      test_server:timetrap_cancel(Dog),      ok. -get3(Config) when is_list(Config) -> +t_get_3(Config) when is_list(Config) ->      Map = #{ key1 => value1, key2 => value2 },      DefaultValue = "Default value", -    ?line value1 = maps:get(key1, Map, DefaultValue), -    ?line value2 = maps:get(key2, Map, DefaultValue), -    ?line DefaultValue = maps:get(key3, Map, DefaultValue), +    value1 = maps:get(key1, Map, DefaultValue), +    value2 = maps:get(key2, Map, DefaultValue), +    DefaultValue = maps:get(key3, Map, DefaultValue), +    ok. + +t_without_2(_Config) -> +    Ki = [11,22,33,44,55,66,77,88,99], +    M0 = maps:from_list([{{k,I},{v,I}}||I<-lists:seq(1,100)]), +    M1 = maps:from_list([{{k,I},{v,I}}||I<-lists:seq(1,100) -- Ki]), +    M1 = maps:without([{k,I}||I <- Ki],M0), +    ok. + +t_with_2(_Config) -> +    Ki = [11,22,33,44,55,66,77,88,99], +    M0 = maps:from_list([{{k,I},{v,I}}||I<-lists:seq(1,100)]), +    M1 = maps:from_list([{{k,I},{v,I}}||I<-Ki]), +    M1 = maps:with([{k,I}||I <- Ki],M0),      ok. diff --git a/lib/stdlib/test/ms_transform_SUITE.erl b/lib/stdlib/test/ms_transform_SUITE.erl index 4ec13ed472..1577caa80f 100644 --- a/lib/stdlib/test/ms_transform_SUITE.erl +++ b/lib/stdlib/test/ms_transform_SUITE.erl @@ -91,21 +91,23 @@ warnings(Config) when is_list(Config) ->  	    "            end)">>,      ?line [{_,[{_,ms_transform,{?WARN_NUMBER_SHADOW,'A'}}]}] =  	compile_ww(Prog), -    Prog2 = <<"C=5, " -	    "ets:fun2ms(fun({A,B} = C) " -	    "            when is_integer(A) and (A+5 > B) -> " -	    "              {A andalso B,C} " -	    "            end)">>, -    ?line [{_,[{_,ms_transform,{?WARN_NUMBER_SHADOW,'C'}}]}] = +    Prog2 = <<"C = 5, +               ets:fun2ms(fun ({A,B} = +                                       C) when is_integer(A) and (A+5 > B) -> +                                  {A andalso B,C} +                          end)">>, +    [{_,[{3,ms_transform,{?WARN_NUMBER_SHADOW,'C'}}]}] =  	compile_ww(Prog2),      Rec3 = <<"-record(a,{a,b,c,d=foppa}).">>, -    Prog3 = <<"A=3,C=5, " -	    "ets:fun2ms(fun(#a{a = A, b = B} = C) " -	    "            when is_integer(A) and (A+5 > B) -> " -	    "              {A andalso B,C} " -	    "            end)">>, -    ?line [{_,[{_,ms_transform,{?WARN_NUMBER_SHADOW,'A'}}, -	       {_,ms_transform,{?WARN_NUMBER_SHADOW,'C'}}]}] = +    Prog3 = <<"A = 3, +               C = 5, +               ets:fun2ms(fun (C +                                 = #a{a = A, b = B}) +                              when is_integer(A) and (A+5 > B) -> +                                  {A andalso B,C} +                          end)">>, +    [{_,[{3,ms_transform,{?WARN_NUMBER_SHADOW,'C'}}, +         {4,ms_transform,{?WARN_NUMBER_SHADOW,'A'}}]}] =  	compile_ww(Rec3,Prog3),      Rec4 = <<"-record(a,{a,b,c,d=foppa}).">>,      Prog4 = <<"A=3,C=5, " @@ -867,6 +869,7 @@ compile_ww(Records,Expr) ->      "-include_lib(\"stdlib/include/ms_transform.hrl\").\n",      "-export([tmp/0]).\n",      Records/binary,"\n", +    "-file(?FILE, 0). ",      "tmp() ->\n",      Expr/binary,".\n">>,      FN=temp_name(), diff --git a/lib/stdlib/test/shell_SUITE.erl b/lib/stdlib/test/shell_SUITE.erl index e016432f4d..f841e2c4a6 100644 --- a/lib/stdlib/test/shell_SUITE.erl +++ b/lib/stdlib/test/shell_SUITE.erl @@ -2532,6 +2532,11 @@ otp_6554(Config) when is_list(Config) ->            "\n    end.\nok.\n" =           t(<<"begin F = fun() -> foo end, 1 end. B = F(). C = 17. b().">>), +    ?line "3: command not found" = comm_err(<<"#{v(3) => v}.">>), +    ?line "3: command not found" = comm_err(<<"#{k => v(3)}.">>), +    ?line "3: command not found" = comm_err(<<"#{v(3) := v}.">>), +    ?line "3: command not found" = comm_err(<<"#{k := v(3)}.">>), +    ?line "3: command not found" = comm_err(<<"(v(3))#{}.">>),      %% Tests I'd like to do: (you should try them manually)      %% "catch spawn_link(fun() -> timer:sleep(1000), exit(foo) end)."      %%   "** exception error: foo" should be output after 1 second diff --git a/lib/stdlib/test/stdlib_SUITE.erl b/lib/stdlib/test/stdlib_SUITE.erl index 59821220b4..3d09bd27ff 100644 --- a/lib/stdlib/test/stdlib_SUITE.erl +++ b/lib/stdlib/test/stdlib_SUITE.erl @@ -78,17 +78,29 @@ appup_test(_Config) ->  appup_tests(_App,{[],[]}) ->      {skip,"no previous releases available"}; -appup_tests(App,{OkVsns,NokVsns}) -> +appup_tests(App,{OkVsns0,NokVsns}) ->      application:load(App),      {_,_,Vsn} = lists:keyfind(App,1,application:loaded_applications()),      AppupFileName = atom_to_list(App) ++ ".appup",      AppupFile = filename:join([code:lib_dir(App),ebin,AppupFileName]),      {ok,[{Vsn,UpFrom,DownTo}=AppupScript]} = file:consult(AppupFile),      ct:log("~p~n",[AppupScript]), -    ct:log("Testing ok versions: ~p~n",[OkVsns]), +    OkVsns = +	case OkVsns0 -- [Vsn] of +	    OkVsns0 -> +		OkVsns0; +	    Ok -> +		ct:log("Current version, ~p, is same as in previous release.~n" +		       "Removing this from the list of ok versions.", +		      [Vsn]), +		Ok +	end, +    ct:log("Testing that appup allows upgrade from these versions: ~p~n", +	   [OkVsns]),      check_appup(OkVsns,UpFrom,{ok,[restart_new_emulator]}),      check_appup(OkVsns,DownTo,{ok,[restart_new_emulator]}), -    ct:log("Testing not ok versions: ~p~n",[NokVsns]), +    ct:log("Testing that appup does not allow upgrade from these versions: ~p~n", +	   [NokVsns]),      check_appup(NokVsns,UpFrom,error),      check_appup(NokVsns,DownTo,error),      ok. diff --git a/lib/stdlib/test/tar_SUITE.erl b/lib/stdlib/test/tar_SUITE.erl index 6349139925..9b6d65011e 100644 --- a/lib/stdlib/test/tar_SUITE.erl +++ b/lib/stdlib/test/tar_SUITE.erl @@ -654,6 +654,7 @@ open_add_close(Config) when is_list(Config) ->      ?line ok = erl_tar:add(AD, FileOne, []),      ?line ok = erl_tar:add(AD, FileTwo, "second file", []),      ?line ok = erl_tar:add(AD, FileThree, [verbose]), +    ?line ok = erl_tar:add(AD, FileThree, "chunked", [{chunks,11411},verbose]),      ?line ok = erl_tar:add(AD, ADir, [verbose]),      ?line ok = erl_tar:add(AD, AnotherDir, [verbose]),      ?line ok = erl_tar:close(AD), @@ -661,7 +662,7 @@ open_add_close(Config) when is_list(Config) ->      ?line ok = erl_tar:t(TarOne),      ?line ok = erl_tar:tt(TarOne), -    ?line {ok,[FileOne,"second file",FileThree,ADir,SomeContent]} = erl_tar:table(TarOne), +    ?line {ok,[FileOne,"second file",FileThree,"chunked",ADir,SomeContent]} = erl_tar:table(TarOne),      ?line delete_files(["oac_file","oac_small","oac_big",Dir,AnotherDir,ADir]), diff --git a/lib/stdlib/vsn.mk b/lib/stdlib/vsn.mk index 37a6590b06..5be130bac9 100644 --- a/lib/stdlib/vsn.mk +++ b/lib/stdlib/vsn.mk @@ -1 +1 @@ -STDLIB_VSN = 2.0 +STDLIB_VSN = 2.3 | 
