diff options
Diffstat (limited to 'lib/stdlib')
133 files changed, 8240 insertions, 7050 deletions
diff --git a/lib/stdlib/doc/src/Makefile b/lib/stdlib/doc/src/Makefile index 50f6427eaa..6f1e61e70c 100644 --- a/lib/stdlib/doc/src/Makefile +++ b/lib/stdlib/doc/src/Makefile @@ -101,7 +101,6 @@ XML_REF6_FILES = stdlib_app.xml XML_PART_FILES = part.xml part_notes.xml part_notes_history.xml XML_CHAPTER_FILES = io_protocol.xml unicode_usage.xml notes.xml notes_history.xml -GIF_FILES = ushell1.gif ushell2.gif ushell3.gif BOOK_FILES = book.xml @@ -112,8 +111,7 @@ XML_FILES = \ # ---------------------------------------------------- HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \ - $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html) \ - $(GIF_FILES:%.gif=$(HTMLDIR)/%.gif) + $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html) INFO_FILE = ../../info @@ -138,21 +136,16 @@ SPECS_FLAGS = -I../../include -I../../../kernel/include # ---------------------------------------------------- # Targets # ---------------------------------------------------- -$(HTMLDIR)/%.gif: %.gif - $(INSTALL_DATA) $< $@ - docs: man pdf html $(TOP_PDF_FILE): $(XML_FILES) pdf: $(TOP_PDF_FILE) -html: gifs $(HTML_REF_MAN_FILE) +html: $(HTML_REF_MAN_FILE) man: $(MAN3_FILES) $(MAN6_FILES) -gifs: $(GIF_FILES:%=$(HTMLDIR)/%) - debug opt: clean clean_docs: diff --git a/lib/stdlib/doc/src/base64.xml b/lib/stdlib/doc/src/base64.xml index bfe8494a73..f90a82187d 100644 --- a/lib/stdlib/doc/src/base64.xml +++ b/lib/stdlib/doc/src/base64.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2007</year><year>2011</year> + <year>2007</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -37,6 +37,11 @@ <datatype> <name name="ascii_string"/> </datatype> + <datatype> + <name name="ascii_binary"/> + <desc><p>A <c>binary()</c> with ASCII characters in the range 1 to 255.</p> + </desc> + </datatype> </datatypes> <funcs> <func> diff --git a/lib/stdlib/doc/src/binary.xml b/lib/stdlib/doc/src/binary.xml index 7b8e279788..06cfad0b0b 100644 --- a/lib/stdlib/doc/src/binary.xml +++ b/lib/stdlib/doc/src/binary.xml @@ -77,41 +77,30 @@ </datatypes> <funcs> <func> - <name>at(Subject, Pos) -> byte()</name> + <name name="at" arity="2"/> <fsummary>Returns the byte at a specific position in a binary</fsummary> - <type> - <v>Subject = binary()</v> - <v>Pos = integer() >= 0</v> - </type> <desc> - <p>Returns the byte at position <c>Pos</c> (zero-based) in the binary - <c>Subject</c> as an integer. If <c>Pos</c> >= <c>byte_size(Subject)</c>, + <p>Returns the byte at position <c><anno>Pos</anno></c> (zero-based) in the binary + <c><anno>Subject</anno></c> as an integer. If <c><anno>Pos</anno></c> >= <c>byte_size(<anno>Subject</anno>)</c>, a <c>badarg</c> exception is raised.</p> </desc> </func> <func> - <name>bin_to_list(Subject) -> [byte()]</name> + <name name="bin_to_list" arity="1"/> <fsummary>Convert a binary to a list of integers</fsummary> - <type> - <v>Subject = binary()</v> - </type> <desc> - <p>The same as <c>bin_to_list(Subject,{0,byte_size(Subject)})</c>.</p> + <p>The same as <c>bin_to_list(<anno>Subject</anno>,{0,byte_size(<anno>Subject</anno>)})</c>.</p> </desc> </func> <func> - <name>bin_to_list(Subject, PosLen) -> [byte()]</name> + <name name="bin_to_list" arity="2"/> <fsummary>Convert a binary to a list of integers</fsummary> - <type> - <v>Subject = binary()</v> - <v>PosLen = part()</v> - </type> <desc> - <p>Converts <c>Subject</c> to a list of <c>byte()</c>s, each representing + <p>Converts <c><anno>Subject</anno></c> to a list of <c>byte()</c>s, each representing the value of one byte. The <c>part()</c> denotes which part of the <c>binary()</c> to convert. Example:</p> @@ -120,27 +109,19 @@ "rla" %% or [114,108,97] in list notation. </code> - <p>If <c>PosLen</c> in any way references outside the binary, a <c>badarg</c> exception is raised.</p> + <p>If <c><anno>PosLen</anno></c> in any way references outside the binary, a <c>badarg</c> exception is raised.</p> </desc> </func> <func> - <name>bin_to_list(Subject, Pos, Len) -> [byte()]</name> + <name name="bin_to_list" arity="3"/> <fsummary>Convert a binary to a list of integers</fsummary> - <type> - <v>Subject = binary()</v> - <v>Pos = integer() >= 0</v> - <v>Len = integer() >= 0</v> - </type> <desc> - <p>The same as<c> bin_to_list(Subject,{Pos,Len})</c>.</p> + <p>The same as<c> bin_to_list(<anno>Subject</anno>,{<anno>Pos</anno>,<anno>Len</anno>})</c>.</p> </desc> </func> <func> - <name>compile_pattern(Pattern) -> cp()</name> + <name name="compile_pattern" arity="1"/> <fsummary>Pre-compiles a binary search pattern</fsummary> - <type> - <v>Pattern = binary() | [ binary() ]</v> - </type> <desc> <p>Builds an internal structure representing a compilation of a @@ -155,7 +136,7 @@ <p>When a list of binaries is given, it denotes a set of alternative binaries to search for. I.e if <c>[<<"functional">>,<<"programming">>]</c> - is given as <c>Pattern</c>, this + is given as <c><anno>Pattern</anno></c>, this means "either <c><<"functional">></c> or <c><<"programming">></c>". The pattern is a set of alternatives; when only a single binary is given, the set has @@ -163,32 +144,25 @@ <p>The list of binaries used for search alternatives shall be flat and proper.</p> - <p>If <c>Pattern</c> is not a binary or a flat proper list of binaries with length > 0, + <p>If <c><anno>Pattern</anno></c> is not a binary or a flat proper list of binaries with length > 0, a <c>badarg</c> exception will be raised.</p> </desc> </func> <func> - <name>copy(Subject) -> binary()</name> + <name name="copy" arity="1"/> <fsummary>Creates a duplicate of a binary</fsummary> - <type> - <v>Subject = binary()</v> - </type> <desc> - <p>The same as <c>copy(Subject, 1)</c>.</p> + <p>The same as <c>copy(<anno>Subject</anno>, 1)</c>.</p> </desc> </func> <func> - <name>copy(Subject,N) -> binary()</name> + <name name="copy" arity="2"/> <fsummary>Duplicates a binary N times and creates a new</fsummary> - <type> - <v>Subject = binary()</v> - <v>N = integer() >= 0</v> - </type> <desc> - <p>Creates a binary with the content of <c>Subject</c> duplicated <c>N</c> times.</p> + <p>Creates a binary with the content of <c><anno>Subject</anno></c> duplicated <c><anno>N</anno></c> times.</p> - <p>This function will always create a new binary, even if <c>N = + <p>This function will always create a new binary, even if <c><anno>N</anno> = 1</c>. By using <c>copy/1</c> on a binary referencing a larger binary, one might free up the larger binary for garbage collection.</p> @@ -201,32 +175,23 @@ large binaries are no longer used in any process, deliberate copying might be a good idea.</p> </note> - <p>If <c>N</c> < <c>0</c>, a <c>badarg</c> exception is raised.</p> + <p>If <c><anno>N</anno></c> < <c>0</c>, a <c>badarg</c> exception is raised.</p> </desc> </func> <func> - <name>decode_unsigned(Subject) -> Unsigned</name> + <name name="decode_unsigned" arity="1"/> <fsummary>Decode a whole binary into an integer of arbitrary size</fsummary> - <type> - <v>Subject = binary()</v> - <v>Unsigned = integer() >= 0</v> - </type> <desc> - <p>The same as <c>decode_unsigned(Subject,big)</c>.</p> + <p>The same as <c>decode_unsigned(<anno>Subject</anno>, big)</c>.</p> </desc> </func> <func> - <name>decode_unsigned(Subject, Endianess) -> Unsigned</name> + <name name="decode_unsigned" arity="2"/> <fsummary>Decode a whole binary into an integer of arbitrary size</fsummary> - <type> - <v>Subject = binary()</v> - <v>Endianess = big | little</v> - <v>Unsigned = integer() >= 0</v> - </type> <desc> <p>Converts the binary digit representation, in big or little - endian, of a positive integer in <c>Subject</c> to an Erlang <c>integer()</c>.</p> + endian, of a positive integer in <c><anno>Subject</anno></c> to an Erlang <c>integer()</c>.</p> <p>Example:</p> @@ -237,22 +202,15 @@ </desc> </func> <func> - <name>encode_unsigned(Unsigned) -> binary()</name> + <name name="encode_unsigned" arity="1"/> <fsummary>Encodes an unsigned integer into the minimal binary</fsummary> - <type> - <v>Unsigned = integer() >= 0</v> - </type> <desc> - <p>The same as <c>encode_unsigned(Unsigned,big)</c>.</p> + <p>The same as <c>encode_unsigned(<anno>Unsigned</anno>, big)</c>.</p> </desc> </func> <func> - <name>encode_unsigned(Unsigned,Endianess) -> binary()</name> + <name name="encode_unsigned" arity="2"/> <fsummary>Encodes an unsigned integer into the minimal binary</fsummary> - <type> - <v>Unsigned = integer() >= 0</v> - <v>Endianess = big | little</v> - </type> <desc> <p>Converts a positive integer to the smallest possible @@ -268,51 +226,39 @@ </desc> </func> <func> - <name>first(Subject) -> byte()</name> + <name name="first" arity="1"/> <fsummary>Returns the first byte of a binary</fsummary> - <type> - <v>Subject = binary()</v> - </type> <desc> - <p>Returns the first byte of the binary <c>Subject</c> as an integer. If the - size of <c>Subject</c> is zero, a <c>badarg</c> exception is raised.</p> + <p>Returns the first byte of the binary <c><anno>Subject</anno></c> as an integer. If the + size of <c><anno>Subject</anno></c> is zero, a <c>badarg</c> exception is raised.</p> </desc> </func> <func> - <name>last(Subject) -> byte()</name> + <name name="last" arity="1"/> <fsummary>Returns the last byte of a binary</fsummary> - <type> - <v>Subject = binary()</v> - </type> <desc> - <p>Returns the last byte of the binary <c>Subject</c> as an integer. If the - size of <c>Subject</c> is zero, a <c>badarg</c> exception is raised.</p> + <p>Returns the last byte of the binary <c><anno>Subject</anno></c> as an integer. If the + size of <c><anno>Subject</anno></c> is zero, a <c>badarg</c> exception is raised.</p> </desc> </func> <func> - <name>list_to_bin(ByteList) -> binary()</name> + <name name="list_to_bin" arity="1"/> <fsummary>Convert a list of integers and binaries to a binary</fsummary> - <type> - <v>ByteList = iodata() (see module erlang)</v> - </type> <desc> <p>Works exactly as <c>erlang:list_to_binary/1</c>, added for completeness.</p> </desc> </func> <func> - <name>longest_common_prefix(Binaries) -> integer() >= 0</name> + <name name="longest_common_prefix" arity="1"/> <fsummary>Returns length of longest common prefix for a set of binaries</fsummary> - <type> - <v>Binaries = [ binary() ]</v> - </type> <desc> <p>Returns the length of the longest common prefix of the - binaries in the list <c>Binaries</c>. Example:</p> + binaries in the list <c><anno>Binaries</anno></c>. Example:</p> <code> 1> binary:longest_common_prefix([<<"erlang">>,<<"ergonomy">>]). @@ -321,19 +267,16 @@ 0 </code> - <p>If <c>Binaries</c> is not a flat list of binaries, a <c>badarg</c> exception is raised.</p> + <p>If <c><anno>Binaries</anno></c> is not a flat list of binaries, a <c>badarg</c> exception is raised.</p> </desc> </func> <func> - <name>longest_common_suffix(Binaries) -> integer() >= 0</name> + <name name="longest_common_suffix" arity="1"/> <fsummary>Returns length of longest common suffix for a set of binaries</fsummary> - <type> - <v>Binaries = [ binary() ]</v> - </type> <desc> <p>Returns the length of the longest common suffix of the - binaries in the list <c>Binaries</c>. Example:</p> + binaries in the list <c><anno>Binaries</anno></c>. Example:</p> <code> 1> binary:longest_common_suffix([<<"erlang">>,<<"fang">>]). @@ -347,35 +290,24 @@ </desc> </func> <func> - <name>match(Subject, Pattern) -> Found | <c>nomatch</c></name> + <name name="match" arity="2"/> <fsummary>Searches for the first match of a pattern in a binary</fsummary> - <type> - <v>Subject = binary()</v> - <v>Pattern = binary() | [ binary() ] | cp()</v> - <v>Found = part()</v> - </type> <desc> - <p>The same as <c>match(Subject, Pattern, [])</c>.</p> + <p>The same as <c>match(<anno>Subject</anno>, <anno>Pattern</anno>, [])</c>.</p> </desc> </func> <func> - <name>match(Subject,Pattern,Options) -> Found | <c>nomatch</c></name> + <name name="match" arity="3"/> + <type name="part"/> <fsummary>Searches for the first match of a pattern in a binary</fsummary> - <type> - <v>Subject = binary()</v> - <v>Pattern = binary() | [ binary() ] | cp()</v> - <v>Found = part()</v> - <v>Options = [ Option ]</v> - <v>Option = {scope, part()}</v> - </type> <desc> - <p>Searches for the first occurrence of <c>Pattern</c> in <c>Subject</c> and + <p>Searches for the first occurrence of <c><anno>Pattern</anno></c> in <c><anno>Subject</anno></c> and returns the position and length.</p> - <p>The function will return <c>{Pos,Length}</c> for the binary - in <c>Pattern</c> starting at the lowest position in - <c>Subject</c>, Example:</p> + <p>The function will return <c>{Pos, Length}</c> for the binary + in <c><anno>Pattern</anno></c> starting at the lowest position in + <c><anno>Subject</anno></c>, Example:</p> <code> 1> binary:match(<<"abcde">>, [<<"bcde">>,<<"cd">>],[]). @@ -391,16 +323,16 @@ <p>Summary of the options:</p> <taglist> - <tag>{scope, {Start, Length}}</tag> + <tag>{scope, {<anno>Start</anno>, <anno>Length</anno>}}</tag> <item><p>Only the given part is searched. Return values still have - offsets from the beginning of <c>Subject</c>. A negative <c>Length</c> is - allowed as described in the <c>TYPES</c> section of this manual.</p></item> + offsets from the beginning of <c><anno>Subject</anno></c>. A negative <c>Length</c> is + allowed as described in the <c>DATA TYPES</c> section of this manual.</p></item> </taglist> <p>If none of the strings in - <c>Pattern</c> is found, the atom <c>nomatch</c> is returned.</p> + <c><anno>Pattern</anno></c> is found, the atom <c>nomatch</c> is returned.</p> - <p>For a description of <c>Pattern</c>, see + <p>For a description of <c><anno>Pattern</anno></c>, see <seealso marker="#compile_pattern-1">compile_pattern/1</seealso>.</p> <p>If <c>{scope, {Start,Length}}</c> is given in the options @@ -412,32 +344,21 @@ </desc> </func> <func> - <name>matches(Subject, Pattern) -> Found</name> + <name name="matches" arity="2"/> <fsummary>Searches for all matches of a pattern in a binary</fsummary> - <type> - <v>Subject = binary()</v> - <v>Pattern = binary() | [ binary() ] | cp()</v> - <v>Found = [ part() ] | []</v> - </type> <desc> - <p>The same as <c>matches(Subject, Pattern, [])</c>.</p> + <p>The same as <c>matches(<anno>Subject</anno>, <anno>Pattern</anno>, [])</c>.</p> </desc> </func> <func> - <name>matches(Subject,Pattern,Options) -> Found</name> + <name name="matches" arity="3"/> + <type name="part"/> <fsummary>Searches for all matches of a pattern in a binary</fsummary> - <type> - <v>Subject = binary()</v> - <v>Pattern = binary() | [ binary() ] | cp()</v> - <v>Found = [ part() ] | []</v> - <v>Options = [ Option ]</v> - <v>Option = {scope, part()}</v> - </type> <desc> - <p>Works like match, but the <c>Subject</c> is searched until + <p>Works like <c>match/2</c>, but the <c><anno>Subject</anno></c> is searched until exhausted and a list of all non-overlapping parts matching - <c>Pattern</c> is returned (in order). </p> + <c><anno>Pattern</anno></c> is returned (in order). </p> <p>The first and longest match is preferred to a shorter, which is illustrated by the following example:</p> @@ -458,26 +379,22 @@ <p>If none of the strings in pattern is found, an empty list is returned.</p> - <p>For a description of <c>Pattern</c>, see <seealso marker="#compile_pattern-1">compile_pattern/1</seealso> and for a + <p>For a description of <c><anno>Pattern</anno></c>, see <seealso marker="#compile_pattern-1">compile_pattern/1</seealso> and for a description of available options, see <seealso marker="#match-3">match/3</seealso>.</p> - <p>If <c>{scope, {Start,Length}}</c> is given in the options such that - <c>Start</c> is larger than the size of <c>Subject</c>, <c>Start + Length</c> is - less than zero or <c>Start + Length</c> is larger than the size of - <c>Subject</c>, a <c>badarg</c> exception is raised.</p> + <p>If <c>{scope, {<anno>Start</anno>,<anno>Length</anno>}}</c> is given in the options such that + <c><anno>Start</anno></c> is larger than the size of <c><anno>Subject</anno></c>, <c><anno>Start</anno> + <anno>Length</anno></c> is + less than zero or <c><anno>Start</anno> + <anno>Length</anno></c> is larger than the size of + <c><anno>Subject</anno></c>, a <c>badarg</c> exception is raised.</p> </desc> </func> <func> - <name>part(Subject, PosLen) -> binary()</name> + <name name="part" arity="2"/> <fsummary>Extracts a part of a binary</fsummary> - <type> - <v>Subject = binary()</v> - <v>PosLen = part()</v> - </type> <desc> - <p>Extracts the part of the binary <c>Subject</c> described by <c>PosLen</c>.</p> + <p>Extracts the part of the binary <c><anno>Subject</anno></c> described by <c><anno>PosLen</anno></c>.</p> <p>Negative length can be used to extract bytes at the end of a binary:</p> @@ -494,25 +411,20 @@ <c>binary_part/3</c>. Those BIFs are allowed in guard tests.</p> </note> - <p>If <c>PosLen</c> in any way references outside the binary, a <c>badarg</c> exception + <p>If <c><anno>PosLen</anno></c> in any way references outside the binary, a <c>badarg</c> exception is raised.</p> </desc> </func> <func> - <name>part(Subject, Pos, Len) -> binary()</name> + <name name="part" arity="3"/> <fsummary>Extracts a part of a binary</fsummary> - <type> - <v>Subject = binary()</v> - <v>Pos = integer() >= 0</v> - <v>Len = integer()</v> - </type> <desc> - <p>The same as <c>part(Subject, {Pos, Len})</c>.</p> + <p>The same as <c>part(<anno>Subject</anno>, {<anno>Pos</anno>, <anno>Len</anno>})</c>.</p> </desc> </func> <func> - <name>referenced_byte_size(binary()) -> integer() >= 0</name> + <name name="referenced_byte_size" arity="1"/> <fsummary>Determines the size of the actual binary pointed out by a sub-binary</fsummary> <desc> diff --git a/lib/stdlib/doc/src/epp.xml b/lib/stdlib/doc/src/epp.xml index 386ed89fe1..df7bf883fc 100644 --- a/lib/stdlib/doc/src/epp.xml +++ b/lib/stdlib/doc/src/epp.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2012</year> + <year>1996</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -37,6 +37,18 @@ <p>The Erlang code preprocessor includes functions which are used by <c>compile</c> to preprocess macros and include files before the actual parsing takes place.</p> + <p>The Erlang source file <marker + id="encoding"><em>encoding</em></marker> is selected by a + comment in one of the first two lines of the source file. The + first string that matches the regular expression + <c>coding\s*[:=]\s*([-a-zA-Z0-9])+</c> selects the encoding. If + the matching string is not a valid encoding it is ignored. The + valid encodings are <c>Latin-1</c> and <c>UTF-8</c> where the + case of the characters can be chosen freely. Examples:</p> + <pre> +%% coding: utf-8 +%% For this file we have chosen encoding = Latin-1 +%% -*- coding: latin-1 -*-</pre> </description> <datatypes> <datatype> @@ -46,6 +58,9 @@ <name name="epp_handle"></name> <desc><p>Handle to the epp server.</p></desc> </datatype> + <datatype> + <name name="source_encoding"></name> + </datatype> </datatypes> <funcs> <func> @@ -83,6 +98,65 @@ </desc> </func> <func> + <name name="default_encoding" arity="0"/> + <fsummary>Return the default encoding of Erlang source files</fsummary> + <desc> + <p>Returns the default encoding of Erlang source files.</p> + </desc> + </func> + <func> + <name name="encoding_to_string" arity="1"/> + <fsummary>Return a string representation of an encoding</fsummary> + <desc> + <p>Returns a string representation of an encoding. The string + is recognized by <c>read_encoding/1,2</c>, + <c>read_encoding_from_binary/1,2</c>, and + <c>set_encoding/1</c> as a valid encoding.</p> + </desc> + </func> + <func> + <name name="read_encoding" arity="1"/> + <name name="read_encoding" arity="2"/> + <fsummary>Read the encoding from a file</fsummary> + <desc> + <p>Read the <seealso marker="#encoding">encoding</seealso> from + a file. Returns the read encoding, or <c>none</c> if no + valid encoding was found.</p> + <p>The option <c>in_comment_only</c> is <c>true</c> by + default, which is correct for Erlang source files. If set to + <c>false</c> the encoding string does not necessarily have to + occur in a comment.</p> + </desc> + </func> + <func> + <name name="read_encoding_from_binary" arity="1"/> + <name name="read_encoding_from_binary" arity="2"/> + <fsummary>Read the encoding from a binary</fsummary> + <desc> + <p>Read the <seealso marker="#encoding">encoding</seealso> from + a binary. Returns the read encoding, or <c>none</c> if no + valid encoding was found.</p> + <p>The option <c>in_comment_only</c> is <c>true</c> by + default, which is correct for Erlang source files. If set to + <c>false</c> the encoding string does not necessarily have to + occur in a comment.</p> + </desc> + </func> + <func> + <name name="set_encoding" arity="1"/> + <fsummary>Read and set the encoding of an IO device</fsummary> + <desc> + <p>Reads the <seealso marker="#encoding">encoding</seealso> from + an IO device and sets the encoding of the device + accordingly. The position of the IO device referenced by + <c><anno>File</anno></c> is not affected. If no valid + encoding can be read from the IO device the encoding of the + IO device is set to the default encoding.</p> + <p>Returns the read encoding, or <c>none</c> if no valid + encoding was found.</p> + </desc> + </func> + <func> <name name="format_error" arity="1"/> <fsummary>Format an error descriptor</fsummary> <desc> diff --git a/lib/stdlib/doc/src/erl_pp.xml b/lib/stdlib/doc/src/erl_pp.xml index 57b5828bcd..9ae4f3d91f 100644 --- a/lib/stdlib/doc/src/erl_pp.xml +++ b/lib/stdlib/doc/src/erl_pp.xml @@ -5,7 +5,7 @@ <header> <copyright> <year>1996</year> - <year>2011</year> + <year>2012</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> @@ -63,6 +63,12 @@ breaks and only a space is used as a separator.</p> </desc> </datatype> + <datatype> + <name name="option"/> + </datatype> + <datatype> + <name name="options"/> + </datatype> </datatypes> <funcs> <func> diff --git a/lib/stdlib/doc/src/ets.xml b/lib/stdlib/doc/src/ets.xml index 487b06473f..6db1ae3aa3 100644 --- a/lib/stdlib/doc/src/ets.xml +++ b/lib/stdlib/doc/src/ets.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2012</year> + <year>1996</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -128,11 +128,17 @@ <datatypes> <datatype> + <name name="access"/> + </datatype> + <datatype> <name><marker id="type-continuation">continuation()</marker></name> <desc> <p>Opaque continuation used by <seealso marker="#select/1"> - <c>select/1</c></seealso> and <seealso marker="#select/3"> - <c>select/3</c></seealso>.</p> + <c>select/1,3</c></seealso>, <seealso marker="#select_reverse/1"> + <c>select_reverse/1,3</c></seealso>, <seealso + marker="#match/1"> + <c>match/1,3</c></seealso>, and <seealso marker="#match_object/1"> + <c>match_object/1,3</c></seealso>.</p> </desc> </datatype> <datatype> @@ -140,6 +146,10 @@ <desc><p>A match specification, see above.</p></desc> </datatype> <datatype> + <name name="comp_match_spec"/> + <desc><p>A compiled match specification.</p></desc> + </datatype> + <datatype> <name name="match_pattern"/> </datatype> <datatype> @@ -149,14 +159,14 @@ <name name="tid"/> <desc><p>A table identifier, as returned by new/2.</p></desc> </datatype> + <datatype> + <name name="type"/> + </datatype> </datatypes> <funcs> <func> - <name>all() -> [Tab]</name> + <name name="all" arity="0"/> <fsummary>Return a list of all ETS tables.</fsummary> - <type> - <v>Tab = tid() | atom()</v> - </type> <desc> <p>Returns a list of all tables at the node. Named tables are given by their names, unnamed tables are given by their @@ -164,48 +174,34 @@ </desc> </func> <func> - <name>delete(Tab) -> true</name> + <name name="delete" arity="1"/> <fsummary>Delete an entire ETS table.</fsummary> - <type> - <v>Tab = tid() | atom()</v> - </type> <desc> - <p>Deletes the entire table <c>Tab</c>.</p> + <p>Deletes the entire table <c><anno>Tab</anno></c>.</p> </desc> </func> <func> - <name>delete(Tab, Key) -> true</name> + <name name="delete" arity="2"/> <fsummary>Delete all objects with a given key from an ETS table.</fsummary> - <type> - <v>Tab = tid() | atom()</v> - <v>Key = term()</v> - </type> <desc> - <p>Deletes all objects with the key <c>Key</c> from the table - <c>Tab</c>.</p> + <p>Deletes all objects with the key <c><anno>Key</anno></c> from the table + <c><anno>Tab</anno></c>.</p> </desc> </func> <func> - <name>delete_all_objects(Tab) -> true</name> + <name name="delete_all_objects" arity="1"/> <fsummary>Delete all objects in an ETS table.</fsummary> - <type> - <v>Tab = tid() | atom()</v> - </type> <desc> - <p>Delete all objects in the ETS table <c>Tab</c>. + <p>Delete all objects in the ETS table <c><anno>Tab</anno></c>. The operation is guaranteed to be <seealso marker="#concurrency">atomic and isolated</seealso>.</p> </desc> </func> <func> - <name>delete_object(Tab,Object) -> true</name> + <name name="delete_object" arity="2"/> <fsummary>Deletes a specific from an ETS table.</fsummary> - <type> - <v>Tab = tid() | atom()</v> - <v>Object = tuple()</v> - </type> <desc> - <p>Delete the exact object <c>Object</c> from the ETS table, + <p>Delete the exact object <c><anno>Object</anno></c> from the ETS table, leaving objects with the same key but other differences (useful for type <c>bag</c>). In a <c>duplicate_bag</c>, all instances of the object will be deleted.</p> @@ -257,14 +253,10 @@ </desc> </func> <func> - <name>first(Tab) -> Key | '$end_of_table'</name> + <name name="first" arity="1"/> <fsummary>Return the first key in an ETS table.</fsummary> - <type> - <v>Tab = tid() | atom()</v> - <v>Key = term()</v> - </type> <desc> - <p>Returns the first key <c>Key</c> in the table <c>Tab</c>. + <p>Returns the first key <c><anno>Key</anno></c> in the table <c><anno>Tab</anno></c>. If the table is of the <c>ordered_set</c> type, the first key in Erlang term order will be returned. If the table is of any other type, the first key according to the table's internal @@ -336,7 +328,7 @@ the source file.</p> <p>The fun is very restricted, it can take only a single parameter (the object to match): a sole variable or a - tuple. It needs to use the <c>is_</c>XXX guard tests. + tuple. It needs to use the <c>is_</c> guard tests. Language constructs that have no representation in a match_spec (like <c>if</c>, <c>case</c>, <c>receive</c> etc) are not allowed.</p> @@ -386,19 +378,14 @@ Error: fun containing local Erlang function calls </desc> </func> <func> - <name>give_away(Tab, Pid, GiftData) -> true</name> + <name name="give_away" arity="3"/> <fsummary>Change owner of a table.</fsummary> - <type> - <v>Tab = tid() | atom()</v> - <v>Pid = pid()</v> - <v>GiftData = term()</v> - </type> <desc> - <p>Make process <c>Pid</c> the new owner of table <c>Tab</c>. + <p>Make process <c><anno>Pid</anno></c> the new owner of table <c><anno>Tab</anno></c>. If successful, the message - <c>{'ETS-TRANSFER',Tab,FromPid,GiftData}</c> will be sent + <c>{'ETS-TRANSFER',<anno>Tab</anno>,FromPid,<anno>GiftData</anno>}</c> will be sent to the new owner.</p> - <p>The process <c>Pid</c> must be alive, local and not already the + <p>The process <c><anno>Pid</anno></c> must be alive, local and not already the owner of the table. The calling process must be the table owner.</p> <p>Note that <c>give_away</c> does not at all affect the <seealso marker="#heir">heir</seealso> option of the table. A table @@ -421,81 +408,72 @@ Error: fun containing local Erlang function calls </desc> </func> <func> - <name>info(Tab) -> [{Item, Value}] | undefined</name> + <name name="info" arity="1"/> <fsummary>Return information about an ETS table.</fsummary> - <type> - <v>Tab = tid() | atom()</v> - <v>Item = atom(), see below</v> - <v>Value = term(), see below</v> - </type> <desc> - <p>Returns information about the table <c>Tab</c> as a list of - <c>{Item, Value}</c> tuples. If <c>Tab</c> has the correct type + <p>Returns information about the table <c><anno>Tab</anno></c> as a list of + tuples. If <c><anno>Tab</anno></c> has the correct type for a table identifier, but does not refer to an existing ETS - table, <c>undefined</c> is returned. If <c>Tab</c> is not of the + table, <c>undefined</c> is returned. If <c><anno>Tab</anno></c> is not of the correct type, this function fails with reason <c>badarg</c>.</p> <list type="bulleted"> - <item><c>Item=memory, Value=integer()</c> <br></br> + <item><c>{compressed, boolean()}</c> <br></br> - The number of words allocated to the table.</item> - <item><c>Item=owner, Value=pid()</c> <br></br> - - The pid of the owner of the table.</item> - <item><c>Item=heir, Value=pid()|none</c> <br></br> + Indicates if the table is compressed or not.</item> + <item><c>{heir, pid() | none}</c> <br></br> The pid of the heir of the table, or <c>none</c> if no heir is set.</item> - <item><c>Item=name, Value=atom()</c> <br></br> + <item><c>{keypos, integer() >= 1}</c> <br></br> - The name of the table.</item> - <item><c>Item=size, Value=integer()</c> <br></br> + The key position.</item> + <item><c>{memory, integer() >= 0</c> <br></br> - The number of objects inserted in the table.</item> - <item><c>Item=node, Value=atom()</c> <br></br> + The number of words allocated to the table.</item> + <item><c>{name, atom()}</c> <br></br> - The node where the table is stored. This field is no longer - meaningful as tables cannot be accessed from other nodes.</item> - <item><c>Item=named_table, Value=true|false</c> <br></br> + The name of the table.</item> + <item><c>{named_table, boolean()}</c> <br></br> Indicates if the table is named or not.</item> - <item><c>Item=type, Value=set|ordered_set|bag|duplicate_bag</c> <br></br> + <item><c>{node, node()}</c> <br></br> - The table type.</item> - <item><c>Item=keypos, Value=integer()</c> <br></br> + The node where the table is stored. This field is no longer + meaningful as tables cannot be accessed from other nodes.</item> + <item><c>{owner, pid()}</c> <br></br> - The key position.</item> - <item><c>Item=protection, Value=public|protected|private</c> <br></br> + The pid of the owner of the table.</item> + <item><c>{protection, <seealso marker="#type-access">access()</seealso>}</c> <br></br> The table access rights.</item> - <item><c>Item=compressed, Value=true|false</c> <br></br> + <item><c>{size, integer() >= 0</c> <br></br> - Indicates if the table is compressed or not.</item> + The number of objects inserted in the table.</item> + <item><c>{type, <seealso marker="#type-type">type()</seealso>}</c> <br></br> + + The table type.</item> </list> </desc> </func> <func> - <name>info(Tab, Item) -> Value | undefined</name> + <name name="info" arity="2"/> <fsummary>Return the information associated with given item for an ETS table.</fsummary> - <type> - <v>Tab = tid() | atom()</v> - <v>Item, Value - see below</v> - </type> <desc> <p>Returns the information associated with <c>Item</c> for - the table <c>Tab</c>, or returns <c>undefined</c> if <c>Tab</c> + the table <c><anno>Tab</anno></c>, or returns <c>undefined</c> if <c>Tab</c> does not refer an existing ETS table. - If <c>Tab</c> is not of the correct type, or if <c>Item</c> is not + If <c><anno>Tab</anno></c> is not of the correct type, or if <c><anno>Item</anno></c> is not one of the allowed values, this function fails with reason <c>badarg</c>.</p> <warning><p>In R11B and earlier, this function would not fail but return <c>undefined</c> for invalid values for <c>Item</c>.</p> </warning> - <p>In addition to the <c>{Item,Value}</c> + <p>In addition to the <c>{<anno>Item</anno>,<anno>Value</anno>}</c> pairs defined for <c>info/1</c>, the following items are allowed:</p> <list type="bulleted"> - <item><c>Item=fixed, Value=true|false</c> <br></br> + <item><c>Item=fixed, Value=boolean()</c> <br></br> Indicates if the table is fixed by any process or not.</item> <item> @@ -547,15 +525,11 @@ Error: fun containing local Erlang function calls </desc> </func> <func> - <name>insert(Tab, ObjectOrObjects) -> true</name> + <name name="insert" arity="2"/> <fsummary>Insert an object into an ETS table.</fsummary> - <type> - <v>Tab = tid() | atom()</v> - <v>ObjectOrObjects = tuple() | [tuple()]</v> - </type> <desc> <p>Inserts the object or all of the objects in the list - <c>ObjectOrObjects</c> into the table <c>Tab</c>. + <c><anno>ObjectOrObjects</anno></c> into the table <c><anno>Tab</anno></c>. If the table is a <c>set</c> and the key of the inserted objects <em>matches</em> the key of any object in the table, the old object will be replaced. If the table is an @@ -572,19 +546,15 @@ Error: fun containing local Erlang function calls </desc> </func> <func> - <name>insert_new(Tab, ObjectOrObjects) -> boolean()</name> + <name name="insert_new" arity="2"/> <fsummary>Insert an object into an ETS table if the key is not already present.</fsummary> - <type> - <v>Tab = tid() | atom()</v> - <v>ObjectOrObjects = tuple() | [tuple()]</v> - </type> <desc> <p>This function works exactly like <c>insert/2</c>, with the exception that instead of overwriting objects with the same key (in the case of <c>set</c> or <c>ordered_set</c>) or adding more objects with keys already existing in the table (in the case of <c>bag</c> and <c>duplicate_bag</c>), it - simply returns <c>false</c>. If <c>ObjectOrObjects</c> is a + simply returns <c>false</c>. If <c><anno>ObjectOrObjects</anno></c> is a list, the function checks <em>every</em> key prior to inserting anything. Nothing will be inserted if not <em>all</em> keys present in the list are absent from the @@ -593,11 +563,8 @@ Error: fun containing local Erlang function calls </desc> </func> <func> - <name>is_compiled_ms(Term) -> boolean()</name> + <name name="is_compiled_ms" arity="1"/> <fsummary>Checks if an Erlang term is the result of ets:match_spec_compile</fsummary> - <type> - <v>Term = term()</v> - </type> <desc> <p>This function is used to check if a term is a valid compiled <seealso marker="#match_spec">match_spec</seealso>. @@ -626,14 +593,10 @@ ets:is_compiled_ms(Broken).</code> </desc> </func> <func> - <name>last(Tab) -> Key | '$end_of_table'</name> + <name name="last" arity="1"/> <fsummary>Return the last key in an ETS table of type<c>ordered_set</c>.</fsummary> - <type> - <v>Tab = tid() | atom()</v> - <v>Key = term()</v> - </type> <desc> - <p>Returns the last key <c>Key</c> according to Erlang term + <p>Returns the last key <c><anno>Key</anno></c> according to Erlang term order in the table <c>Tab</c> of the <c>ordered_set</c> type. If the table is of any other type, the function is synonymous to <c>first/2</c>. If the table is empty, @@ -642,16 +605,11 @@ ets:is_compiled_ms(Broken).</code> </desc> </func> <func> - <name>lookup(Tab, Key) -> [Object]</name> + <name name="lookup" arity="2"/> <fsummary>Return all objects with a given key in an ETS table.</fsummary> - <type> - <v>Tab = tid() | atom()</v> - <v>Key = term()</v> - <v>Object = tuple()</v> - </type> <desc> - <p>Returns a list of all objects with the key <c>Key</c> in - the table <c>Tab</c>.</p> + <p>Returns a list of all objects with the key <c><anno>Key</anno></c> in + the table <c><anno>Tab</anno></c>.</p> <p>In the case of <c>set, bag and duplicate_bag</c>, an object is returned only if the given key <em>matches</em> the key of the object in the table. If the table is an @@ -681,22 +639,16 @@ ets:is_compiled_ms(Broken).</code> </desc> </func> <func> - <name>lookup_element(Tab, Key, Pos) -> Elem</name> + <name name="lookup_element" arity="3"/> <fsummary>Return the <c>Pos</c>:th element of all objects with a given key in an ETS table.</fsummary> - <type> - <v>Tab = tid() | atom()</v> - <v>Key = term()</v> - <v>Pos = integer()</v> - <v>Elem = term() | [term()]</v> - </type> <desc> - <p>If the table <c>Tab</c> is of type <c>set</c> or - <c>ordered_set</c>, the function returns the <c>Pos</c>:th - element of the object with the key <c>Key</c>.</p> + <p>If the table <c><anno>Tab</anno></c> is of type <c>set</c> or + <c>ordered_set</c>, the function returns the <c><anno>Pos</anno></c>:th + element of the object with the key <c><anno>Key</anno></c>.</p> <p>If the table is of type <c>bag</c> or <c>duplicate_bag</c>, - the functions returns a list with the <c>Pos</c>:th element of - every object with the key <c>Key</c>.</p> - <p>If no object with the key <c>Key</c> exists, the function + the functions returns a list with the <c><anno>Pos</anno></c>:th element of + every object with the key <c><anno>Key</anno></c>.</p> + <p>If no object with the key <c><anno>Key</anno></c> exists, the function will exit with reason <c>badarg</c>.</p> <p>The difference between <c>set</c>, <c>bag</c> and <c>duplicate_bag</c> on one hand, and <c>ordered_set</c> on @@ -708,16 +660,11 @@ ets:is_compiled_ms(Broken).</code> </desc> </func> <func> - <name>match(Tab, Pattern) -> [Match]</name> + <name name="match" arity="2"/> <fsummary>Match the objects in an ETS table against a pattern.</fsummary> - <type> - <v>Tab = tid() | atom()</v> - <v>Pattern = tuple()</v> - <v>Match = [term()]</v> - </type> <desc> - <p>Matches the objects in the table <c>Tab</c> against the - pattern <c>Pattern</c>.</p> + <p>Matches the objects in the table <c><anno>Tab</anno></c> against the + pattern <c><anno>Pattern</anno></c>.</p> <p>A pattern is a term that may contain:</p> <list type="bulleted"> <item>bound parts (Erlang terms),</item> @@ -744,18 +691,12 @@ ets:is_compiled_ms(Broken).</code> </desc> </func> <func> - <name>match(Tab, Pattern, Limit) -> {[Match],Continuation} | '$end_of_table'</name> + <name name="match" arity="3"/> <fsummary>Match the objects in an ETS table against a pattern and returns part of the answers.</fsummary> - <type> - <v>Tab = tid() | atom()</v> - <v>Pattern = tuple()</v> - <v>Match = [term()]</v> - <v>Continuation = term()</v> - </type> <desc> <p>Works like <c>ets:match/2</c> but only returns a limited - (<c>Limit</c>) number of matching objects. The - <c>Continuation</c> term can then be used in subsequent calls + (<c><anno>Limit</anno></c>) number of matching objects. The + <c><anno>Continuation</anno></c> term can then be used in subsequent calls to <c>ets:match/1</c> to get the next chunk of matching objects. This is a space efficient way to work on objects in a table which is still faster than traversing the table object @@ -764,16 +705,12 @@ ets:is_compiled_ms(Broken).</code> </desc> </func> <func> - <name>match(Continuation) -> {[Match],Continuation} | '$end_of_table'</name> + <name name="match" arity="1"/> <fsummary>Continues matching objects in an ETS table.</fsummary> - <type> - <v>Match = [term()]</v> - <v>Continuation = term()</v> - </type> <desc> <p>Continues a match started with <c>ets:match/3</c>. The next chunk of the size given in the initial <c>ets:match/3</c> - call is returned together with a new <c>Continuation</c> + call is returned together with a new <c><anno>Continuation</anno></c> that can be used in subsequent calls to this function.</p> <p><c>'$end_of_table'</c> is returned when there are no more objects in the table.</p> @@ -789,15 +726,11 @@ ets:is_compiled_ms(Broken).</code> </desc> </func> <func> - <name>match_object(Tab, Pattern) -> [Object]</name> + <name name="match_object" arity="2"/> <fsummary>Match the objects in an ETS table against a pattern.</fsummary> - <type> - <v>Tab = tid() | atom()</v> - <v>Pattern = Object = tuple()</v> - </type> <desc> - <p>Matches the objects in the table <c>Tab</c> against the - pattern <c>Pattern</c>. See <c>match/2</c> for a description + <p>Matches the objects in the table <c><anno>Tab</anno></c> against the + pattern <c><anno>Pattern</anno></c>. See <c>match/2</c> for a description of patterns. The function returns a list of all objects which match the pattern.</p> <p>If the key is specified in the pattern, the match is very @@ -809,18 +742,12 @@ ets:is_compiled_ms(Broken).</code> </desc> </func> <func> - <name>match_object(Tab, Pattern, Limit) -> {[Match],Continuation} | '$end_of_table'</name> + <name name="match_object" arity="3"/> <fsummary>Match the objects in an ETS table against a pattern and returns part of the answers.</fsummary> - <type> - <v>Tab = tid() | atom()</v> - <v>Pattern = tuple()</v> - <v>Match = [term()]</v> - <v>Continuation = term()</v> - </type> <desc> <p>Works like <c>ets:match_object/2</c> but only returns a - limited (<c>Limit</c>) number of matching objects. The - <c>Continuation</c> term can then be used in subsequent calls + limited (<c><anno>Limit</anno></c>) number of matching objects. The + <c><anno>Continuation</anno></c> term can then be used in subsequent calls to <c>ets:match_object/1</c> to get the next chunk of matching objects. This is a space efficient way to work on objects in a table which is still faster than traversing the table object @@ -829,29 +756,21 @@ ets:is_compiled_ms(Broken).</code> </desc> </func> <func> - <name>match_object(Continuation) -> {[Match],Continuation} | '$end_of_table'</name> + <name name="match_object" arity="1"/> <fsummary>Continues matching objects in an ETS table.</fsummary> - <type> - <v>Match = [term()]</v> - <v>Continuation = term()</v> - </type> <desc> <p>Continues a match started with <c>ets:match_object/3</c>. The next chunk of the size given in the initial <c>ets:match_object/3</c> call is returned together with a - new <c>Continuation</c> that can be used in subsequent calls + new <c><anno>Continuation</anno></c> that can be used in subsequent calls to this function.</p> <p><c>'$end_of_table'</c> is returned when there are no more objects in the table.</p> </desc> </func> <func> - <name>match_spec_compile(MatchSpec) -> CompiledMatchSpec</name> + <name name="match_spec_compile" arity="1"/> <fsummary>Compiles a match specification into its internal representation</fsummary> - <type> - <v>MatchSpec = match_spec()</v> - <v>CompiledMatchSpec = comp_match_spec()</v> - </type> <desc> <p>This function transforms a <seealso marker="#match_spec">match_spec</seealso> into an @@ -863,7 +782,7 @@ ets:is_compiled_ms(Broken).</code> valid compiled match_spec, nor can it be stored on disk). The validity of a compiled match_spec can be checked using <c>ets:is_compiled_ms/1</c>.</p> - <p>If the term <c>MatchSpec</c> can not be compiled (does not + <p>If the term <c><anno>MatchSpec</anno></c> can not be compiled (does not represent a valid match_spec), a <c>badarg</c> fault is thrown.</p> <note> @@ -873,25 +792,21 @@ ets:is_compiled_ms(Broken).</code> </desc> </func> <func> - <name>match_spec_run(List,CompiledMatchSpec) -> list()</name> + <name name="match_spec_run" arity="2"/> <fsummary>Performs matching, using a compiled match_spec, on a list of tuples</fsummary> - <type> - <v>List = [ tuple() ]</v> - <v>CompiledMatchSpec = comp_match_spec()</v> - </type> <desc> <p>This function executes the matching specified in a compiled <seealso marker="#match_spec">match_spec</seealso> on - a list of tuples. The <c>CompiledMatchSpec</c> term should be + a list of tuples. The <c><anno>CompiledMatchSpec</anno></c> term should be the result of a call to <c>ets:match_spec_compile/1</c> and is hence the internal representation of the match_spec one wants to use.</p> - <p>The matching will be executed on each element in <c>List</c> + <p>The matching will be executed on each element in <c><anno>List</anno></c> and the function returns a list containing all results. If an - element in <c>List</c> does not match, nothing is returned + element in <c><anno>List</anno></c> does not match, nothing is returned for that element. The length of the result list is therefore equal or less than the the length of the parameter - <c>List</c>. The two calls in the following example will give + <c><anno>List</anno></c>. The two calls in the following example will give the same result (but certainly not the same execution time...):</p> <code type="none"> @@ -910,37 +825,23 @@ ets:select(Table,MatchSpec),</code> </desc> </func> <func> - <name>member(Tab, Key) -> true | false</name> + <name name="member" arity="2"/> <fsummary>Tests for occurrence of a key in an ETS table</fsummary> - <type> - <v>Tab = tid() | atom()</v> - <v>Key = term()</v> - </type> <desc> <p>Works like <c>lookup/2</c>, but does not return the objects. The function returns <c>true</c> if one or more elements in - the table has the key <c>Key</c>, <c>false</c> otherwise.</p> + the table has the key <c><anno>Key</anno></c>, <c>false</c> otherwise.</p> </desc> </func> <func> - <name>new(Name, Options) -> tid() | atom()</name> + <name name="new" arity="2"/> <fsummary>Create a new ETS table.</fsummary> - <type> - <v>Name = atom()</v> - <v>Options = [Option]</v> - <v> Option = Type | Access | named_table | {keypos,Pos} | {heir,pid(),HeirData} | {heir,none} | Tweaks</v> - <v> Type = set | ordered_set | bag | duplicate_bag</v> - <v> Access = public | protected | private</v> - <v> Tweaks = {write_concurrency,boolean()} | {read_concurrency,boolean()} | compressed</v> - <v> Pos = integer()</v> - <v> HeirData = term()</v> - </type> <desc> <p>Creates a new table and returns a table identifier which can be used in subsequent operations. The table identifier can be sent to other processes so that a table can be shared between different processes within a node.</p> - <p>The parameter <c>Options</c> is a list of atoms which + <p>The parameter <c><anno>Options</anno></c> is a list of atoms which specifies table type, access rights, key position and if the table is named or not. If one or more options are left out, the default values are used. This means that not specifying @@ -997,27 +898,27 @@ ets:select(Table,MatchSpec),</code> </item> <item> <p><c>named_table</c> - If this option is present, the name <c>Name</c> is + If this option is present, the name <c><anno>Name</anno></c> is associated with the table identifier. The name can then be used instead of the table identifier in subsequent operations.</p> </item> <item> - <p><c>{keypos,Pos}</c> + <p><c>{keypos,<anno>Pos</anno>}</c> Specfies which element in the stored tuples should be used as key. By default, it is the first element, i.e. - <c>Pos=1</c>. However, this is not always appropriate. In + <c><anno>Pos</anno>=1</c>. However, this is not always appropriate. In particular, we do not want the first element to be the key if we want to store Erlang records in a table.</p> <p>Note that any tuple stored in the table must have at - least <c>Pos</c> number of elements.</p> + least <c><anno>Pos</anno></c> number of elements.</p> </item> <item> <marker id="heir"></marker> - <p><c>{heir,Pid,HeirData} | {heir,none}</c><br></br> + <p><c>{heir,<anno>Pid</anno>,<anno>HeirData</anno>} | {heir,none}</c><br></br> Set a process as heir. The heir will inherit the table if the owner terminates. The message - <c>{'ETS-TRANSFER',tid(),FromPid,HeirData}</c> will be sent to + <c>{'ETS-TRANSFER',tid(),FromPid,<anno>HeirData</anno>}</c> will be sent to the heir when that happens. The heir must be a local process. Default heir is <c>none</c>, which will destroy the table when the owner terminates.</p> @@ -1031,7 +932,8 @@ ets:select(Table,MatchSpec),</code> If set to <c>true</c>, the table is optimized towards concurrent write access. Different objects of the same table can be mutated (and read) by concurrent processes. This is achieved to some degree - at the expense of sequential access and concurrent reader performance. + at the expense of memory consumption and the performance of + sequential access and concurrent reading. The <c>write_concurrency</c> option can be combined with the <seealso marker="#new_2_read_concurrency">read_concurrency</seealso> option. You typically want to combine these when large concurrent @@ -1043,8 +945,11 @@ ets:select(Table,MatchSpec),</code> <seealso marker="#concurrency">atomicy and isolation</seealso>. Functions that makes such promises over several objects (like <c>insert/2</c>) will gain less (or nothing) from this option.</p> - <p>Table type <c>ordered_set</c> is not affected by this option in current - implementation.</p> + <p>In current implementation, table type <c>ordered_set</c> is not + affected by this option. Also, the memory consumption inflicted by + both <c>write_concurrency</c> and <c>read_concurrency</c> is a + constant overhead per table. This overhead can be especially large + when both options are combined.</p> </item> <item> <marker id="new_2_read_concurrency"></marker> @@ -1082,15 +987,11 @@ ets:select(Table,MatchSpec),</code> </desc> </func> <func> - <name>next(Tab, Key1) -> Key2 | '$end_of_table'</name> + <name name="next" arity="2"/> <fsummary>Return the next key in an ETS table.</fsummary> - <type> - <v>Tab = tid() | atom()</v> - <v>Key1 = Key2 = term()</v> - </type> <desc> - <p>Returns the next key <c>Key2</c>, following the key - <c>Key1</c> in the table <c>Tab</c>. If the table is of the + <p>Returns the next key <c><anno>Key2</anno></c>, following the key + <c><anno>Key1</anno></c> in the table <c><anno>Tab</anno></c>. If the table is of the <c>ordered_set</c> type, the next key in Erlang term order is returned. If the table is of any other type, the next key according to the table's internal order is returned. If there @@ -1105,16 +1006,12 @@ ets:select(Table,MatchSpec),</code> </desc> </func> <func> - <name>prev(Tab, Key1) -> Key2 | '$end_of_table'</name> + <name name="prev" arity="2"/> <fsummary>Return the previous key in an ETS table of type<c>ordered_set</c>.</fsummary> - <type> - <v>Tab = tid() | atom()</v> - <v>Key1 = Key2 = term()</v> - </type> <desc> - <p>Returns the previous key <c>Key2</c>, preceding the key - <c>Key1</c> according the Erlang term order in the table - <c>Tab</c> of the <c>ordered_set</c> type. If the table is of + <p>Returns the previous key <c><anno>Key2</anno></c>, preceding the key + <c><anno>Key1</anno></c> according the Erlang term order in the table + <c><anno>Tab</anno></c> of the <c>ordered_set</c> type. If the table is of any other type, the function is synonymous to <c>next/2</c>. If there is no previous key, <c>'$end_of_table'</c> is returned.</p> @@ -1122,14 +1019,11 @@ ets:select(Table,MatchSpec),</code> </desc> </func> <func> - <name>rename(Tab, Name) -> Name</name> + <name name="rename" arity="2"/> <fsummary>Rename a named ETS table.</fsummary> - <type> - <v>Tab = Name = atom()</v> - </type> <desc> - <p>Renames the named table <c>Tab</c> to the new name - <c>Name</c>. Afterwards, the old name can not be used to + <p>Renames the named table <c><anno>Tab</anno></c> to the new name + <c><anno>Name</anno></c>. Afterwards, the old name can not be used to access the table. Renaming an unnamed table has no effect.</p> </desc> </func> @@ -1186,18 +1080,15 @@ ets:select(ets:repair_continuation(Broken,MS)).</code> </desc> </func> <func> - <name>safe_fixtable(Tab, true|false) -> true</name> + <name name="safe_fixtable" arity="2"/> <fsummary>Fix an ETS table for safe traversal.</fsummary> - <type> - <v>Tab = tid() | atom()</v> - </type> <desc> <p>Fixes a table of the <c>set</c>, <c>bag</c> or <c>duplicate_bag</c> table type for safe traversal.</p> <p>A process fixes a table by calling - <c>safe_fixtable(Tab,true)</c>. The table remains fixed until + <c>safe_fixtable(<anno>Tab</anno>, true)</c>. The table remains fixed until the process releases it by calling - <c>safe_fixtable(Tab,false)</c>, or until the process + <c>safe_fixtable(<anno>Tab</anno>, false)</c>, or until the process terminates.</p> <p>If several processes fix a table, the table will remain fixed until all processes have released it (or terminated). @@ -1242,15 +1133,10 @@ clean_all_with_value(Tab,X,Key) -> </desc> </func> <func> - <name>select(Tab, MatchSpec) -> [Match]</name> + <name name="select" arity="2"/> <fsummary>Match the objects in an ETS table against a match_spec.</fsummary> - <type> - <v>Tab = tid() | atom()</v> - <v>Match = term()</v> - <v>MatchSpec = match_spec()</v> - </type> <desc> - <p>Matches the objects in the table <c>Tab</c> using a + <p>Matches the objects in the table <c><anno>Tab</anno></c> using a <seealso marker="#match_spec">match_spec</seealso>. This is a more general call than the <c>ets:match/2</c> and <c>ets:match_object/2</c> calls. In its simplest forms the @@ -1337,18 +1223,12 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code> </desc> </func> <func> - <name>select(Tab, MatchSpec, Limit) -> {[Match],Continuation} | '$end_of_table'</name> + <name name="select" arity="3"/> <fsummary>Match the objects in an ETS table against a match_spec and returns part of the answers.</fsummary> - <type> - <v>Tab = tid() | atom()</v> - <v>Match = term()</v> - <v>MatchSpec = match_spec()</v> - <v>Continuation = term()</v> - </type> <desc> <p>Works like <c>ets:select/2</c> but only returns a limited - (<c>Limit</c>) number of matching objects. The - <c>Continuation</c> term can then be used in subsequent calls + (<c><anno>Limit</anno></c>) number of matching objects. The + <c><anno>Continuation</anno></c> term can then be used in subsequent calls to <c>ets:select/1</c> to get the next chunk of matching objects. This is a space efficient way to work on objects in a table which is still faster than traversing the table object @@ -1357,33 +1237,23 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code> </desc> </func> <func> - <name>select(Continuation) -> {[Match],Continuation} | '$end_of_table'</name> + <name name="select" arity="1"/> <fsummary>Continue matching objects in an ETS table.</fsummary> - <type> - <v>Match = term()</v> - <v>Continuation = term()</v> - </type> <desc> <p>Continues a match started with <c>ets:select/3</c>. The next chunk of the size given in the initial <c>ets:select/3</c> - call is returned together with a new <c>Continuation</c> + call is returned together with a new <c><anno>Continuation</anno></c> that can be used in subsequent calls to this function.</p> <p><c>'$end_of_table'</c> is returned when there are no more objects in the table.</p> </desc> </func> <func> - <name>select_count(Tab, MatchSpec) -> NumMatched</name> + <name name="select_count" arity="2"/> <fsummary>Match the objects in an ETS table against a match_spec and returns the number of objects for which the match_spec returned 'true'</fsummary> - <type> - <v>Tab = tid() | atom()</v> - <v>Object = tuple()</v> - <v>MatchSpec = match_spec()</v> - <v>NumMatched = integer()</v> - </type> <desc> - <p>Matches the objects in the table <c>Tab</c> using a + <p>Matches the objects in the table <c><anno>Tab</anno></c> using a <seealso marker="#match_spec">match_spec</seealso>. If the match_spec returns <c>true</c> for an object, that object considered a match and is counted. For any other result from @@ -1396,16 +1266,10 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code> </desc> </func> <func> - <name>select_delete(Tab, MatchSpec) -> NumDeleted</name> + <name name="select_delete" arity="2"/> <fsummary>Match the objects in an ETS table against a match_spec and deletes objects where the match_spec returns 'true'</fsummary> - <type> - <v>Tab = tid() | atom()</v> - <v>Object = tuple()</v> - <v>MatchSpec = match_spec()</v> - <v>NumDeleted = integer()</v> - </type> <desc> - <p>Matches the objects in the table <c>Tab</c> using a + <p>Matches the objects in the table <c><anno>Tab</anno></c> using a <seealso marker="#match_spec">match_spec</seealso>. If the match_spec returns <c>true</c> for an object, that object is removed from the table. For any other result from the @@ -1422,13 +1286,8 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code> </desc> </func> <func> - <name>select_reverse(Tab, MatchSpec) -> [Match]</name> + <name name="select_reverse" arity="2"/> <fsummary>Match the objects in an ETS table against a match_spec.</fsummary> - <type> - <v>Tab = tid() | atom()</v> - <v>Match = term()</v> - <v>MatchSpec = match_spec()</v> - </type> <desc> <p>Works like <c>select/2</c>, but returns the list in reverse @@ -1438,14 +1297,8 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code> </desc> </func> <func> - <name>select_reverse(Tab, MatchSpec, Limit) -> {[Match],Continuation} | '$end_of_table'</name> + <name name="select_reverse" arity="3"/> <fsummary>Match the objects in an ETS table against a match_spec and returns part of the answers.</fsummary> - <type> - <v>Tab = tid() | atom()</v> - <v>Match = term()</v> - <v>MatchSpec = match_spec()</v> - <v>Continuation = term()</v> - </type> <desc> <p>Works like <c>select/3</c>, but for the <c>ordered_set</c> @@ -1456,18 +1309,14 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code> <p>Note that this is <em>not</em> equivalent to reversing the result list of a <c>select/3</c> call, as the result list - is not only reversed, but also contains the last <c>Limit</c> + is not only reversed, but also contains the last <c><anno>Limit</anno></c> matching objects in the table, not the first.</p> </desc> </func> <func> - <name>select_reverse(Continuation) -> {[Match],Continuation} | '$end_of_table'</name> + <name name="select_reverse" arity="1"/> <fsummary>Continue matching objects in an ETS table.</fsummary> - <type> - <v>Match = term()</v> - <v>Continuation = term()</v> - </type> <desc> <p>Continues a match started with @@ -1477,7 +1326,7 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code> returned list will also contain objects with keys in reverse order.</p> - <p>For all other table types, the behaviour is exatly that of <c>select/1</c>.</p> + <p>For all other table types, the behaviour is exactly that of <c>select/1</c>.</p> <p>Example:</p> <code> 1> T = ets:new(x,[ordered_set]). @@ -1501,14 +1350,8 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code> </desc> </func> <func> - <name>setopts(Tab, Opts) -> true</name> + <name name="setopts" arity="2"/> <fsummary>Set table options.</fsummary> - <type> - <v>Tab = tid() | atom()</v> - <v>Opts = Opt | [Opt]</v> - <v>Opt = {heir,pid(),HeirData} | {heir,none}</v> - <v>HeirData = term()</v> - </type> <desc> <p>Set table options. The only option that currently is allowed to be set after the table has been created is @@ -1517,28 +1360,23 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code> </desc> </func> <func> - <name>slot(Tab, I) -> [Object] | '$end_of_table'</name> + <name name="slot" arity="2"/> <fsummary>Return all objects in a given slot of an ETS table.</fsummary> - <type> - <v>Tab = tid() | atom()</v> - <v>I = integer()</v> - <v>Object = tuple()</v> - </type> <desc> <p>This function is mostly for debugging purposes, Normally one should use <c>first/next</c> or <c>last/prev</c> instead.</p> - <p>Returns all objects in the <c>I</c>:th slot of the table - <c>Tab</c>. A table can be traversed by repeatedly calling - the function, starting with the first slot <c>I=0</c> and + <p>Returns all objects in the <c><anno>I</anno></c>:th slot of the table + <c><anno>Tab</anno></c>. A table can be traversed by repeatedly calling + the function, starting with the first slot <c><anno>I</anno>=0</c> and ending when <c>'$end_of_table'</c> is returned. The function will fail with reason <c>badarg</c> if the - <c>I</c> argument is out of range.</p> + <c><anno>I</anno></c> argument is out of range.</p> <p>Unless a table of type <c>set</c>, <c>bag</c> or <c>duplicate_bag</c> is protected using <c>safe_fixtable/2</c>, see above, a traversal may fail if concurrent updates are made to the table. If the table is of type <c>ordered_set</c>, the function returns a list - containing the <c>I</c>:th object in Erlang term order.</p> + containing the <c><anno>I</anno></c>:th object in Erlang term order.</p> </desc> </func> <func> @@ -1754,16 +1592,16 @@ true</pre> </desc> </func> <func> - <name>update_counter(Tab, Key, UpdateOp) -> Result</name> - <name>update_counter(Tab, Key, [UpdateOp]) -> [Result]</name> - <name>update_counter(Tab, Key, Incr) -> Result</name> + <name name="update_counter" arity="3" clause_i="1"/> + <name name="update_counter" arity="3" clause_i="2"/> + <name name="update_counter" arity="3" clause_i="3"/> + <type variable="Tab"/> + <type variable="Key"/> + <type variable="UpdateOp" name_i="1"/> + <type variable="Pos" name_i="1"/> + <type variable="Threshold" name_i="1"/> + <type variable="SetValue" name_i="1"/> <fsummary>Update a counter object in an ETS table.</fsummary> - <type> - <v>Tab = tid() | atom()</v> - <v>Key = term()</v> - <v>UpdateOp = {Pos,Incr} | {Pos,Incr,Threshold,SetValue}</v> - <v>Pos = Incr = Threshold = SetValue = Result = integer()</v> - </type> <desc> <p>This function provides an efficient way to update one or more counters, without the hassle of having to look up an object, update @@ -1771,22 +1609,22 @@ true</pre> into the table again. (The update is done atomically; i.e. no process can access the ets table in the middle of the operation.) </p> - <p>It will destructively update the object with key <c>Key</c> - in the table <c>Tab</c> by adding <c>Incr</c> to the element - at the <c>Pos</c>:th position. The new counter value is + <p>It will destructively update the object with key <c><anno>Key</anno></c> + in the table <c><anno>Tab</anno></c> by adding <c><anno>Incr</anno></c> to the element + at the <c><anno>Pos</anno></c>:th position. The new counter value is returned. If no position is specified, the element directly following the key (<c><![CDATA[<keypos>+1]]></c>) is updated.</p> - <p>If a <c>Threshold</c> is specified, the counter will be - reset to the value <c>SetValue</c> if the following + <p>If a <c><anno>Threshold</anno></c> is specified, the counter will be + reset to the value <c><anno>SetValue</anno></c> if the following conditions occur:</p> <list type="bulleted"> - <item>The <c>Incr</c> is not negative (<c>>= 0</c>) and the - result would be greater than (<c>></c>) <c>Threshold</c></item> - <item>The <c>Incr</c> is negative (<c><![CDATA[< 0]]></c>) and the + <item>The <c><anno>Incr</anno></c> is not negative (<c>>= 0</c>) and the + result would be greater than (<c>></c>) <c><anno>Threshold</anno></c></item> + <item>The <c><anno>Incr</anno></c> is negative (<c><![CDATA[< 0]]></c>) and the result would be less than (<c><![CDATA[<]]></c>) - <c>Threshold</c></item> + <c><anno>Threshold</anno></c></item> </list> - <p>A list of <c>UpdateOp</c> can be supplied to do several update + <p>A list of <c><anno>UpdateOp</anno></c> can be supplied to do several update operations within the object. The operations are carried out in the order specified in the list. If the same counter position occurs more than one time in the list, the corresponding counter will thus @@ -1797,7 +1635,7 @@ true</pre> returned. If the function should fail, no updates will be done at all. </p> - <p>The given Key is used to identify the object by either + <p>The given <c><anno>Key</anno></c> is used to identify the object by either <em>matching</em> the key of an object in a <c>set</c> table, or <em>compare equal</em> to the key of an object in an <c>ordered_set</c> table (see @@ -1812,29 +1650,28 @@ true</pre> <item>the object has the wrong arity,</item> <item>the element to update is not an integer,</item> <item>the element to update is also the key, or,</item> - <item>any of <c>Pos</c>, <c>Incr</c>, <c>Threshold</c> or - <c>SetValue</c> is not an integer</item> + <item>any of <c><anno>Pos</anno></c>, <c><anno>Incr</anno></c>, <c><anno>Threshold</anno></c> or + <c><anno>SetValue</anno></c> is not an integer</item> </list> </desc> </func> <func> - <name>update_element(Tab, Key, {Pos,Value}) -> true | false</name> - <name>update_element(Tab, Key, [{Pos,Value}]) -> true | false</name> + <name name="update_element" arity="3" clause_i="1"/> + <name name="update_element" arity="3" clause_i="2"/> + <type variable="Tab"/> + <type variable="Key"/> + <type variable="Value"/> + <type variable="Pos"/> <fsummary>Updates the <c>Pos</c>:th element of the object with a given key in an ETS table.</fsummary> - <type> - <v>Tab = tid() | atom()</v> - <v>Key = Value = term()</v> - <v>Pos = integer()</v> - </type> <desc> <p>This function provides an efficient way to update one or more elements within an object, without the hassle of having to look up, update and write back the entire object. </p> - <p>It will destructively update the object with key <c>Key</c> - in the table <c>Tab</c>. The element at the <c>Pos</c>:th position - will be given the value <c>Value</c>. </p> - <p>A list of <c>{Pos,Value}</c> can be supplied to update several + <p>It will destructively update the object with key <c><anno>Key</anno></c> + in the table <c><anno>Tab</anno></c>. The element at the <c><anno>Pos</anno></c>:th position + will be given the value <c><anno>Value</anno></c>. </p> + <p>A list of <c>{<anno>Pos</anno>,<anno>Value</anno>}</c> can be supplied to update several elements within the same object. If the same position occurs more than one in the list, the last value in the list will be written. If the list is empty or the function fails, no updates will be done at @@ -1842,9 +1679,9 @@ true</pre> can never see any intermediate results. </p> <p>The function returns <c>true</c> if an object with the key - <c>Key</c> was found, <c>false</c> otherwise. + <c><anno>Key</anno></c> was found, <c>false</c> otherwise. </p> - <p>The given Key is used to identify the object by either + <p>The given <c><anno>Key</anno></c> is used to identify the object by either <em>matching</em> the key of an object in a <c>set</c> table, or <em>compare equal</em> to the key of an object in an <c>ordered_set</c> table (see @@ -1855,7 +1692,7 @@ true</pre> <list type="bulleted"> <item>the table is not of type <c>set</c> or <c>ordered_set</c>,</item> - <item><c>Pos</c> is less than 1 or greater than the object + <item><c><anno>Pos</anno></c> is less than 1 or greater than the object arity, or,</item> <item>the element to update is also the key</item> </list> diff --git a/lib/stdlib/doc/src/filelib.xml b/lib/stdlib/doc/src/filelib.xml index f3079c7337..bd780b2b2f 100644 --- a/lib/stdlib/doc/src/filelib.xml +++ b/lib/stdlib/doc/src/filelib.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2003</year><year>2011</year> + <year>2003</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -36,8 +36,9 @@ <description> <p>This module contains utilities on a higher level than the <c>file</c> module.</p> - <p>The module supports Unicode file names, so that it will match against regular expressions given in Unicode and that it will find and process raw file names (i.e. files named in a way that does not confirm to the expected encoding).</p> - <p>If the VM operates in Unicode file naming mode on a machine with transparent file naming, the <c>fun()</c> provided to <c>fold_files/5</c> needs to be prepared to handle binary file names.</p> + <p>This module does not support "raw" file names (i.e. files whose names + do not comply with the expected encoding). Such files will be ignored + by the functions in this module.</p> <p>For more information about raw file names, see the <seealso marker="kernel:file">file</seealso> module.</p> </description> @@ -150,6 +151,11 @@ <p>Matches any number of characters up to the end of the filename, the next dot, or the next slash.</p> </item> + <tag>**</tag> + <item> + <p>Two adjacent <c>*</c>'s used as a single pattern will + match all files and zero or more directories and subdirectories.</p> + </item> <tag>[Character1,Character2,...]</tag> <item> <p>Matches any of the characters listed. Two characters @@ -192,6 +198,10 @@ <c>src</c> or <c>include</c> directories, use:</p> <code type="none"> filelib:wildcard("lib/*/{src,include}/*.{erl,hrl}") </code> + <p>To find all <c>.erl</c> or <c>.hrl</c> files in any + subdirectory, use:</p> + <code type="none"> + filelib:wildcard("lib/**/*.{erl,hrl}") </code> </desc> </func> <func> diff --git a/lib/stdlib/doc/src/io.xml b/lib/stdlib/doc/src/io.xml index e6d262466c..90f24c4cbc 100644 --- a/lib/stdlib/doc/src/io.xml +++ b/lib/stdlib/doc/src/io.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2011</year> + <year>1996</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -28,9 +28,9 @@ <rev></rev> </header> <module>io</module> - <modulesummary>Standard IO Server Interface Functions</modulesummary> + <modulesummary>Standard I/O Server Interface Functions</modulesummary> <description> - <p>This module provides an interface to standard Erlang IO servers. + <p>This module provides an interface to standard Erlang I/O servers. The output functions all return <c>ok</c> if they are successful, or exit if they are not.</p> <p>In the following description, all functions have an optional @@ -38,17 +38,16 @@ process which handles the IO protocols. Normally, it is the <c>IoDevice</c> returned by <seealso marker="kernel:file#open/2">file:open/2</seealso>.</p> - <p>For a description of the IO protocols refer to the STDLIB Users Guide.</p> + <p>For a description of the IO protocols refer to the <seealso marker="io_protocol">STDLIB User's Guide</seealso>.</p> <warning> <p>As of R13A, data supplied to the <seealso marker="#put_chars/2">put_chars</seealso> function should be in the <seealso marker="unicode#type-chardata"><c>unicode:chardata()</c></seealso> format. This means that programs supplying binaries to this function need to convert them to UTF-8 - before trying to output the data on an - <c>io_device()</c>.</p> + before trying to output the data on an IO device.</p> - <p>If an io_device() is set in binary mode, the functions <seealso + <p>If an IO device is set in binary mode, the functions <seealso marker="#get_chars/3">get_chars</seealso> and <seealso marker="#get_line/2">get_line</seealso> may return binaries instead of lists. The binaries will, as of R13A, be encoded in @@ -68,9 +67,9 @@ <datatype> <name name="device"/> <desc> - <p>Either <c>standard_io</c>, <c>standard_error</c>, a + <p>An IO device. Either <c>standard_io</c>, <c>standard_error</c>, a registered name, or a pid handling IO protocols (returned from - <seealso marker="kernel:file#open/2">file:open/2</seealso>).</p> + <seealso marker="kernel:file#open/2">file:open/2</seealso>).</p> </desc> </datatype> <datatype> @@ -89,17 +88,14 @@ <name name="format"/> </datatype> <datatype> - <name name="line"/> + <name name="location"/> </datatype> <datatype> <name name="prompt"/> </datatype> <datatype> - <name name="request_error"/> - </datatype> - <datatype> - <name name="error_description"/> - <desc><p>Whatever the I/O-server sends.</p></desc> + <name name="server_no_data"/> + <desc><p>What the I/O-server sends when there is no data.</p></desc> </datatype> </datatypes> @@ -107,11 +103,11 @@ <func> <name name="columns" arity="0"/> <name name="columns" arity="1"/> - <fsummary>Get the number of columns of a device</fsummary> + <fsummary>Get the number of columns of an IO device</fsummary> <desc> <p>Retrieves the number of columns of the <c><anno>IoDevice</anno></c> (i.e. the width of a terminal). The function - only succeeds for terminal devices, for all other devices + only succeeds for terminal devices, for all other IO devices the function returns <c>{error, enotsup}</c></p> </desc> </func> @@ -120,7 +116,7 @@ <name name="put_chars" arity="2"/> <fsummary>Write a list of characters</fsummary> <desc> - <p>Writes the characters of <c><anno>CharData</anno></c> to the io_server() + <p>Writes the characters of <c><anno>CharData</anno></c> to the I/O server (<c><anno>IoDevice</anno></c>).</p> </desc> </func> @@ -135,6 +131,7 @@ <func> <name name="get_chars" arity="2"/> <name name="get_chars" arity="3"/> + <type name="server_no_data"/> <fsummary>Read a specified number of characters</fsummary> <desc> <p>Reads <c><anno>Count</anno></c> characters from standard input @@ -143,19 +140,19 @@ <taglist> <tag><c><anno>Data</anno></c></tag> <item> - <p>The input characters. If the device supports Unicode, + <p>The input characters. If the IO device supports Unicode, the data may represent codepoints larger than 255 (the - latin1 range). If the io_server() is set to deliver + latin1 range). If the I/O server is set to deliver binaries, they will be encoded in UTF-8 (regardless of if - the device actually supports Unicode or not).</p> + the IO device actually supports Unicode or not).</p> </item> <tag><c>eof</c></tag> <item> <p>End of file was encountered.</p> </item> - <tag><c>{error,<anno>Reason</anno>}</c></tag> + <tag><c>{error, <anno>ErrorDescription</anno>}</c></tag> <item> - <p>Other (rare) error condition, for instance <c>{error,estale}</c> + <p>Other (rare) error condition, for instance <c>{error, estale}</c> if reading from an NFS file system.</p> </item> </taglist> @@ -164,6 +161,7 @@ <func> <name name="get_line" arity="1"/> <name name="get_line" arity="2"/> + <type name="server_no_data"/> <fsummary>Read a line</fsummary> <desc> <p>Reads a line from the standard input (<c><anno>IoDevice</anno></c>), @@ -172,19 +170,19 @@ <tag><c><anno>Data</anno></c></tag> <item> <p>The characters in the line terminated by a LF (or end of - file). If the device supports Unicode, + file). If the IO device supports Unicode, the data may represent codepoints larger than 255 (the - latin1 range). If the io_server() is set to deliver + latin1 range). If the I/O server is set to deliver binaries, they will be encoded in UTF-8 (regardless of if - the device actually supports Unicode or not).</p> + the IO device actually supports Unicode or not).</p> </item> <tag><c>eof</c></tag> <item> <p>End of file was encountered.</p> </item> - <tag><c>{error,<anno>Reason</anno>}</c></tag> + <tag><c>{error, <anno>ErrorDescription</anno>}</c></tag> <item> - <p>Other (rare) error condition, for instance <c>{error,estale}</c> + <p>Other (rare) error condition, for instance <c>{error, estale}</c> if reading from an NFS file system.</p> </item> </taglist> @@ -195,7 +193,7 @@ <name name="getopts" arity="1"/> <fsummary>Get the supported options and values from an I/O-server</fsummary> <desc> - <p>This function requests all available options and their current values for a specific io_device(). Example:</p> + <p>This function requests all available options and their current values for a specific IO device. Example:</p> <pre> 1> <input>{ok,F} = file:open("/dev/null",[read]).</input> {ok,<0.42.0>} @@ -213,34 +211,46 @@ </desc> </func> <func> + <name name="printable_range" arity="0"/> + <fsummary>Get user requested printable character range</fsummary> + <desc> + <p>Return the user requested range of printable Unicode characters.</p> + <p>The user can request a range of characters that are to be considered printable in heuristic detection of strings by the shell and by the formatting functions. This is done by supplying <c>+pc <range></c> when starting Erlang.</p> + <p>Currently the only valid values for <c><range></c> are <c>latin1</c> and <c>unicode</c>. <c>latin1</c> means that only code points below 256 (with the exception of control characters etc) will be considered printable. <c>unicode</c> means that all printable characters in all unicode character ranges are considered printable by the io functions.</p> + <p>By default, Erlang is started so that only the <c>latin1</c> range of characters will indicate that a list of integers is a string.</p> + <p>The simplest way to utilize the setting is to call <seealso marker="io_lib#printable_list/1">io_lib:printable_list/1</seealso>, which will use the return value of this function to decide if a list is a string of printable characters or not.</p> + <note><p>In the future, this function may return more values and ranges. It is recommended to use the io_lib:printable_list/1 function to avoid compatibility problems.</p></note> + </desc> + </func> + <func> <name name="setopts" arity="1"/> <name name="setopts" arity="2"/> <fsummary>Set options</fsummary> <desc> - <p>Set options for the io_device() (<c><anno>IoDevice</anno></c>).</p> + <p>Set options for the standard IO device (<c><anno>IoDevice</anno></c>).</p> <p>Possible options and values vary depending on the actual - io_device(). For a list of supported options and their current values - on a specific device, use the <seealso + IO device. For a list of supported options and their current values + on a specific IO device, use the <seealso marker="#getopts/1">getopts/1</seealso> function.</p> - <p>The options and values supported by the current OTP io_devices are:</p> + <p>The options and values supported by the current OTP IO devices are:</p> <taglist> <tag><c>binary, list or {binary, boolean()}</c></tag> <item> - <p>If set in binary mode (binary or {binary,true}), the io_server() sends binary data (encoded in UTF-8) as answers to the get_line, get_chars and, if possible, get_until requests (see the I/O protocol description in STDLIB User's Guide for details). The immediate effect is that <c>get_chars/2,3</c> and <c>get_line/1,2</c> return UTF-8 binaries instead of lists of chars for the affected device.</p> - <p>By default, all io_devices in OTP are set in list mode, but the io functions can handle any of these modes and so should other, user written, modules behaving as clients to I/O-servers.</p> - <p>This option is supported by the standard shell (group.erl), the 'oldshell' (user.erl) and the file I/O servers.</p> + <p>If set in binary mode (<c>binary</c> or <c>{binary, true}</c>), the I/O server sends binary data (encoded in UTF-8) as answers to the <c>get_line</c>, <c>get_chars</c> and, if possible, <c>get_until</c> requests (see the I/O protocol description in <seealso marker="io_protocol">STDLIB User's Guide</seealso> for details). The immediate effect is that <c>get_chars/2,3</c> and <c>get_line/1,2</c> return UTF-8 binaries instead of lists of chars for the affected IO device.</p> + <p>By default, all IO devices in OTP are set in list mode, but the I/O functions can handle any of these modes and so should other, user written, modules behaving as clients to I/O-servers.</p> + <p>This option is supported by the standard shell (<c>group.erl</c>), the 'oldshell' (<c>user.erl</c>) and the file I/O servers.</p> </item> <tag><c>{echo, boolean()}</c></tag> <item> - <p>Denotes if the terminal should echo input. Only supported for the standard shell I/O-server (group.erl)</p> + <p>Denotes if the terminal should echo input. Only supported for the standard shell I/O-server (<c>group.erl</c>)</p> </item> <tag><c>{expand_fun, expand_fun()}</c></tag> <item> <p>Provide a function for tab-completion (expansion) - like the erlang shell. This function is called - when the user presses the Tab key. The expansion is + like the Erlang shell. This function is called + when the user presses the TAB key. The expansion is active when calling line-reading functions such as <c>get_line/1,2</c>.</p> <p>The function is called with the current line, upto @@ -253,25 +263,25 @@ will be printed and the current input line will be written once again.</p> <p>Trivial example (beep on anything except empty line, which - is expanded to "quit"):</p> + is expanded to <c>"quit"</c>):</p> <code type="none"> fun("") -> {yes, "quit", []}; (_) -> {no, "", ["quit"]} end</code> - <p>This option is supported by the standard shell only (group.erl).</p> + <p>This option is supported by the standard shell only (<c>group.erl</c>).</p> </item> <tag><c>{encoding, latin1 | unicode}</c></tag> <item> - <p>Specifies how characters are input or output from or to the actual device, implying that i.e. a terminal is set to handle Unicode input and output or a file is set to handle UTF-8 data encoding.</p> - <p>The option <em>does not</em> affect how data is returned from the io-functions or how it is sent in the I/O-protocol, it only affects how the io_device() is to handle Unicode characters towards the "physical" device.</p> - <p>The standard shell will be set for either unicode or latin1 encoding when the system is started. The actual encoding is set with the help of the "LANG" or "LC_CTYPE" environment variables on Unix-like system or by other means on other systems. The bottom line is that the user can input Unicode characters and the device will be in {encoding, unicode} mode if the device supports it. The mode can be changed, if the assumption of the runtime system is wrong, by setting this option.</p> - <p>The io_device() used when Erlang is started with the "-oldshell" or "-noshell" flags is by default set to latin1 encoding, meaning that any characters beyond codepoint 255 will be escaped and that input is expected to be plain 8-bit ISO-latin-1. If the encoding is changed to Unicode, input and output from the standard file descriptors will be in UTF-8 (regardless of operating system).</p> - <p>Files can also be set in {encoding, unicode}, meaning that data is written and read as UTF-8. More encodings are possible for files, see below.</p> - <p>{encoding, unicode | latin1} is supported by both the standard shell (group.erl including werl on windows), the 'oldshell' (user.erl) and the file I/O servers.</p> + <p>Specifies how characters are input or output from or to the actual IO device, implying that i.e. a terminal is set to handle Unicode input and output or a file is set to handle UTF-8 data encoding.</p> + <p>The option <em>does not</em> affect how data is returned from the I/O functions or how it is sent in the I/O-protocol, it only affects how the IO device is to handle Unicode characters towards the "physical" device.</p> + <p>The standard shell will be set for either Unicode or latin1 encoding when the system is started. The actual encoding is set with the help of the <c>LANG</c> or <c>LC_CTYPE</c> environment variables on Unix-like system or by other means on other systems. The bottom line is that the user can input Unicode characters and the IO device will be in <c>{encoding, unicode}</c> mode if the IO device supports it. The mode can be changed, if the assumption of the runtime system is wrong, by setting this option.</p> + <p>The IO device used when Erlang is started with the "-oldshell" or "-noshell" flags is by default set to latin1 encoding, meaning that any characters beyond codepoint 255 will be escaped and that input is expected to be plain 8-bit ISO-latin-1. If the encoding is changed to Unicode, input and output from the standard file descriptors will be in UTF-8 (regardless of operating system).</p> + <p>Files can also be set in <c>{encoding, unicode}</c>, meaning that data is written and read as UTF-8. More encodings are possible for files, see below.</p> + <p><c>{encoding, unicode | latin1}</c> is supported by both the standard shell (<c>group.erl</c> including <c>werl</c> on Windows®), the 'oldshell' (<c>user.erl</c>) and the file I/O servers.</p> </item> <tag><c>{encoding, utf8 | utf16 | utf32 | {utf16,big} | {utf16,little} | {utf32,big} | {utf32,little}}</c></tag> <item> <p>For disk files, the encoding can be set to various UTF variants. This will have the effect that data is expected to be read as the specified encoding from the file and the data will be written in the specified encoding to the disk file.</p> - <p>{encoding, utf8} will have the same effect as {encoding,unicode} on files.</p> + <p><c>{encoding, utf8}</c> will have the same effect as <c>{encoding, unicode}</c> on files.</p> <p>The extended encodings are only supported on disk files (opened by the <seealso marker="kernel:file#open/2">file:open/2</seealso> function)</p> </item> </taglist> @@ -289,6 +299,7 @@ <func> <name name="read" arity="1"/> <name name="read" arity="2"/> + <type name="server_no_data"/> <fsummary>Read a term</fsummary> <desc> <p>Reads a term <c><anno>Term</anno></c> from the standard input @@ -307,29 +318,43 @@ <item> <p>The parsing failed.</p> </item> + <tag><c>{error, <anno>ErrorDescription</anno>}</c></tag> + <item> + <p>Other (rare) error condition, for instance <c>{error, estale}</c> + if reading from an NFS file system.</p> + </item> </taglist> </desc> </func> <func> <name name="read" arity="3"/> + <name name="read" arity="4"/> + <type name="server_no_data"/> <fsummary>Read a term</fsummary> <desc> <p>Reads a term <c><anno>Term</anno></c> from <c><anno>IoDevice</anno></c>, prompting it - with <c><anno>Prompt</anno></c>. Reading starts at line number - <c><anno>StartLine</anno></c>. It returns:</p> + with <c><anno>Prompt</anno></c>. Reading starts at location + <c><anno>StartLocation</anno></c>. The argument + <c><anno>Options</anno></c> is passed on as the <c>Options</c> + argument of the <c>erl_scan:tokens/4</c> function. It returns:</p> <taglist> - <tag><c>{ok, Term, <anno>EndLine</anno>}</c></tag> + <tag><c>{ok, Term, <anno>EndLocation</anno>}</c></tag> <item> <p>The parsing was successful.</p> </item> - <tag><c>{eof, <anno>EndLine</anno>}</c></tag> + <tag><c>{eof, <anno>EndLocation</anno>}</c></tag> <item> <p>End of file was encountered.</p> </item> - <tag><c>{error, <anno>ErrorInfo</anno>, <anno>ErrorLine</anno>}</c></tag> + <tag><c>{error, <anno>ErrorInfo</anno>, <anno>ErrorLocation</anno>}</c></tag> <item> <p>The parsing failed.</p> </item> + <tag><c>{error, <anno>ErrorDescription</anno>}</c></tag> + <item> + <p>Other (rare) error condition, for instance <c>{error, estale}</c> + if reading from an NFS file system.</p> + </item> </taglist> </desc> </func> @@ -377,10 +402,11 @@ ok</pre> applicable, it is used for both the field width and precision. The default padding character is <c>' '</c> (space).</p> <p><c>Mod</c> is the control sequence modifier. It is either a - single character (currently only 't', for unicode translation, - is supported) that changes the interpretation of Data.</p> - - <p>The following control sequences are available:</p> + single character (currently only <c>t</c>, for Unicode + translation, and <c>l</c>, for stopping <c>p</c> and + <c>P</c> from detecting printable characters, are supported) + that changes the interpretation of Data.</p> + <p>The following control sequences are available:</p> <taglist> <tag><c>~</c></tag> <item> @@ -394,18 +420,18 @@ ok</pre> which in turn defaults to 1. The following example illustrates:</p> <pre> -2> <input>io:fwrite("|~10.5c|~-10.5c|~5c|~n", [$a, $b, $c]).</input> +1> <input>io:fwrite("|~10.5c|~-10.5c|~5c|~n", [$a, $b, $c]).</input> | aaaaa|bbbbb |ccccc| ok</pre> - <p>If the Unicode translation modifier ('t') is in effect, + <p>If the Unicode translation modifier (<c>t</c>) is in effect, the integer argument can be any number representing a - valid unicode codepoint, otherwise it should be an integer + valid Unicode codepoint, otherwise it should be an integer less than or equal to 255, otherwise it is masked with 16#FF:</p> <pre> -1> <input>io:fwrite("~tc~n",[1024]).</input> +2> <input>io:fwrite("~tc~n",[1024]).</input> \x{400} ok -2> <input>io:fwrite("~c~n",[1024]).</input> +3> <input>io:fwrite("~c~n",[1024]).</input> ^@ ok</pre> @@ -437,29 +463,32 @@ ok</pre> </item> <tag><c>s</c></tag> <item> - <p>Prints the argument with the <c>string</c> syntax. The + <p>Prints the argument with the string syntax. The argument is, if no Unicode translation modifier is present, an - iolist(), a binary, or an atom. If the Unicode translation modifier ('t') is in effect, the argument is unicode:chardata(), meaning that binaries are in UTF-8. The characters + <c>iolist()</c>, a <c>binary()</c>, or an <c>atom()</c>. + If the Unicode translation modifier (<c>t</c>) is in effect, + the argument is <c>unicode:chardata()</c>, meaning that + binaries are in UTF-8. The characters are printed without quotes. The string is first truncated by the given precision and then padded and justified to the given field width. The default precision is the field width.</p> <p>This format can be used for printing any object and truncating the output so it fits a specified field:</p> <pre> -3> <input>io:fwrite("|~10w|~n", [{hey, hey, hey}]).</input> +1> <input>io:fwrite("|~10w|~n", [{hey, hey, hey}]).</input> |**********| ok -4> <input>io:fwrite("|~10s|~n", [io_lib:write({hey, hey, hey})]).</input> +2> <input>io:fwrite("|~10s|~n", [io_lib:write({hey, hey, hey})]).</input> |{hey,hey,h| -5> <input>io:fwrite("|~-10.8s|~n", [io_lib:write({hey, hey, hey})]).</input> +3> <input>io:fwrite("|~-10.8s|~n", [io_lib:write({hey, hey, hey})]).</input> |{hey,hey | ok</pre> <p>A list with integers larger than 255 is considered an error if the Unicode translation modifier is not given:</p> <pre> -1> <input>io:fwrite("~ts~n",[[1024]]).</input> +4> <input>io:fwrite("~ts~n",[[1024]]).</input> \x{400} ok -2> io:fwrite("~s~n",[[1024]]). +5> <input>io:fwrite("~s~n",[[1024]]).</input> ** exception exit: {badarg,[{io,format,[<0.26.0>,"~s~n",[[1024]]]}, ...</pre> </item> @@ -476,20 +505,22 @@ ok <p>Writes the data with standard syntax in the same way as <c>~w</c>, but breaks terms whose printed representation is longer than one line into many lines and indents each - line sensibly. It also tries to detect lists of printable - characters and to output these as strings. For example:</p> + line sensibly. It also tries to detect lists of + printable characters and to output these as strings. The + Unicode translation modifier is used for determining + what characters are printable. For example:</p> <pre> -5> <input>T = [{attributes,[[{id,age,1.50000},{mode,explicit},</input> +1> <input>T = [{attributes,[[{id,age,1.50000},{mode,explicit},</input> <input>{typename,"INTEGER"}], [{id,cho},{mode,explicit},{typename,'Cho'}]]},</input> <input>{typename,'Person'},{tag,{'PRIVATE',3}},{mode,implicit}].</input> ... -6> <input>io:fwrite("~w~n", [T]).</input> +2> <input>io:fwrite("~w~n", [T]).</input> [{attributes,[[{id,age,1.5},{mode,explicit},{typename, [73,78,84,69,71,69,82]}],[{id,cho},{mode,explicit},{typena me,'Cho'}]]},{typename,'Person'},{tag,{'PRIVATE',3}},{mode ,implicit}] ok -7> <input>io:fwrite("~62p~n", [T]).</input> +3> <input>io:fwrite("~62p~n", [T]).</input> [{attributes,[[{id,age,1.5}, {mode,explicit}, {typename,"INTEGER"}], @@ -505,7 +536,7 @@ ok</pre> <c>io:fwrite</c> or <c>io:format</c>. For example, using <c>T</c> above:</p> <pre> -8> <input>io:fwrite("Here T = ~62p~n", [T]).</input> +4> <input>io:fwrite("Here T = ~62p~n", [T]).</input> Here T = [{attributes,[[{id,age,1.5}, {mode,explicit}, {typename,"INTEGER"}], @@ -516,6 +547,31 @@ Here T = [{attributes,[[{id,age,1.5}, {tag,{'PRIVATE',3}}, {mode,implicit}] ok</pre> + <p>When the modifier <c>l</c> is given no detection of + printable character lists will take place. For example:</p> + <pre> +5> <input>S = [{a,"a"}, {b, "b"}].</input> +6> <input>io:fwrite("~15p~n", [S]).</input> +[{a,"a"}, + {b,"b"}] +ok +7> <input>io:fwrite("~15lp~n", [S]).</input> +[{a,[97]}, + {b,[98]}] +ok</pre> + <p>Binaries that look like UTF-8 encoded strings will be + output with the string syntax if the Unicode translation + modifier is given:</p> + <pre> +9> <input>io:fwrite("~p~n",[[1024]]).</input> +[1024] +10> <input>io:fwrite("~tp~n",[[1024]]).</input> +"\x{400}" +11> <input>io:fwrite("~tp~n", [<<128,128>>]).</input> +<<128,128>> +12> <input>io:fwrite("~tp~n", [<<208,128>>]).</input> +<<"\x{400}"/utf8>> +ok</pre> </item> <tag><c>W</c></tag> <item> @@ -524,7 +580,7 @@ ok</pre> are printed. Anything below this depth is replaced with <c>...</c>. For example, using <c>T</c> above:</p> <pre> -9> <input>io:fwrite("~W~n", [T,9]).</input> +8> <input>io:fwrite("~W~n", [T,9]).</input> [{attributes,[[{id,age,1.5},{mode,explicit},{typename,...}], [{id,cho},{mode,...},{...}]]},{typename,'Person'}, {tag,{'PRIVATE',3}},{mode,implicit}] @@ -541,7 +597,7 @@ ok</pre> are printed. Anything below this depth is replaced with <c>...</c>. For example:</p> <pre> -10> <input>io:fwrite("~62P~n", [T,9]).</input> +9> <input>io:fwrite("~62P~n", [T,9]).</input> [{attributes,[[{id,age,1.5},{mode,explicit},{typename,...}], [{id,cho},{mode,...},{...}]]}, {typename,'Person'}, @@ -555,13 +611,13 @@ ok</pre> 10. A leading dash is printed for negative integers.</p> <p>The precision field selects base. For example:</p> <pre> -11> <input>io:fwrite("~.16B~n", [31]).</input> +1> <input>io:fwrite("~.16B~n", [31]).</input> 1F ok -12> <input>io:fwrite("~.2B~n", [-19]).</input> +2> <input>io:fwrite("~.2B~n", [-19]).</input> -10011 ok -13> <input>io:fwrite("~.36B~n", [5*36+35]).</input> +3> <input>io:fwrite("~.36B~n", [5*36+35]).</input> 5Z ok</pre> </item> @@ -573,22 +629,22 @@ ok</pre> <p>The prefix can be a possibly deep list of characters or an atom.</p> <pre> -14> <input>io:fwrite("~X~n", [31,"10#"]).</input> +1> <input>io:fwrite("~X~n", [31,"10#"]).</input> 10#31 ok -15> <input>io:fwrite("~.16X~n", [-31,"0x"]).</input> +2> <input>io:fwrite("~.16X~n", [-31,"0x"]).</input> -0x1F ok</pre> </item> <tag><c>#</c></tag> <item> <p>Like <c>B</c>, but prints the number with an Erlang style - '#'-separated base prefix.</p> + <c>#</c>-separated base prefix.</p> <pre> -16> <input>io:fwrite("~.10#~n", [31]).</input> +1> <input>io:fwrite("~.10#~n", [31]).</input> 10#31 ok -17> <input>io:fwrite("~.16#~n", [-31]).</input> +2> <input>io:fwrite("~.16#~n", [-31]).</input> -16#1F ok</pre> </item> @@ -622,10 +678,10 @@ ok</pre> </taglist> <p>If an error occurs, there is no output. For example:</p> <pre> -18> <input>io:fwrite("~s ~w ~i ~w ~c ~n",['abc def', 'abc def', {foo, 1},{foo, 1}, 65]).</input> +1> <input>io:fwrite("~s ~w ~i ~w ~c ~n",['abc def', 'abc def', {foo, 1},{foo, 1}, 65]).</input> abc def 'abc def' {foo,1} A ok -19> <input>io:fwrite("~s", [65]).</input> +2> <input>io:fwrite("~s", [65]).</input> ** exception exit: {badarg,[{io,format,[<0.22.0>,"~s","A"]}, {erl_eval,do_apply,5}, {shell,exprs,6}, @@ -633,13 +689,14 @@ ok {shell,eval_loop,3}]} in function io:o_request/2</pre> <p>In this example, an attempt was made to output the single - character '65' with the aid of the string formatting directive + character 65 with the aid of the string formatting directive "~s".</p> </desc> </func> <func> <name name="fread" arity="2"/> <name name="fread" arity="3"/> + <type name="server_no_data"/> <fsummary>Read formatted input</fsummary> <desc> <p>Reads characters from the standard input (<c><anno>IoDevice</anno></c>), @@ -664,7 +721,7 @@ ok return suppression character. It provides a method to specify a field which is to be omitted. <c>F</c> is the <c>field width</c> of the input field, <c>M</c> is an optional - translation modifier (of which 't' is the only currently + translation modifier (of which <c>t</c> is the only currently supported, meaning Unicode translation) and <c>C</c> determines the type of control sequence.</p> @@ -690,8 +747,8 @@ ok <tag><c>-</c></tag> <item> <p>An optional sign character is expected. A sign - character '-' gives the return value <c>-1</c>. Sign - character '+' or none gives <c>1</c>. The field width + character <c>-</c> gives the return value <c>-1</c>. Sign + character <c>+</c> or none gives <c>1</c>. The field width parameter is ignored. Leading white-space characters are not skipped.</p> </item> @@ -713,7 +770,7 @@ ok characters are stripped. An Erlang string (list of characters) is returned.</p> - <p>If Unicode translation is in effect (~ts), + <p>If Unicode translation is in effect (<c>~ts</c>), characters larger than 255 are accepted, otherwise not. With the translation modifier, the list returned may as a consequence also contain @@ -769,10 +826,15 @@ Prompt> <input><Character beyond latin1 range not printable in this medium> <item> <p>End of file was encountered.</p> </item> - <tag><c>{error, <anno>What</anno>}</c></tag> + <tag><c>{error, <anno>FreadError</anno>}</c></tag> + <item> + <p>The reading failed and <c>FreadError</c> gives a + hint about the error.</p> + </item> + <tag><c>{error, <anno>ErrorDescription</anno>}</c></tag> <item> <p>The read operation failed and the parameter - <c><anno>What</anno></c> gives a hint about the error.</p> + <c><anno>ErrorDescription</anno></c> gives a hint about the error.</p> </item> </taglist> </item> @@ -793,11 +855,11 @@ enter><input>:</input> <input>alan</input> <input>:</input> <input>joe</in <func> <name name="rows" arity="0"/> <name name="rows" arity="1"/> - <fsummary>Get the number of rows of a device</fsummary> + <fsummary>Get the number of rows of an IO device</fsummary> <desc> <p>Retrieves the number of rows of the <c><anno>IoDevice</anno></c> (i.e. the height of a terminal). The function - only succeeds for terminal devices, for all other devices + only succeeds for terminal devices, for all other IO devices the function returns <c>{error, enotsup}</c></p> </desc> </func> @@ -805,26 +867,40 @@ enter><input>:</input> <input>alan</input> <input>:</input> <input>joe</in <name name="scan_erl_exprs" arity="1"/> <name name="scan_erl_exprs" arity="2"/> <name name="scan_erl_exprs" arity="3"/> + <name name="scan_erl_exprs" arity="4"/> + <type name="server_no_data"/> <fsummary>Read and tokenize Erlang expressions</fsummary> <desc> <p>Reads data from the standard input (<c>IoDevice</c>), - prompting it with <c>Prompt</c>. Reading starts at line number - <c>StartLine</c> (1). The data is tokenized as if it were a - sequence of Erlang expressions until a final <c>'.'</c> is + prompting it with <c>Prompt</c>. Reading starts at location + <c>StartLocation</c> (<c>1</c>). The argument <c><anno>Options</anno></c> + is passed on as the <c>Options</c> argument of the + <c>erl_scan:tokens/4</c> function. The data is tokenized as if + it were a + sequence of Erlang expressions until a final dot (<c>.</c>) is reached. This token is also returned. It returns:</p> <taglist> - <tag><c>{ok, Tokens, EndLine}</c></tag> + <tag><c>{ok, Tokens, EndLocation}</c></tag> <item> <p>The tokenization succeeded.</p> </item> - <tag><c>{eof, EndLine}</c></tag> + <tag><c>{eof, EndLocation}</c></tag> <item> - <p>End of file was encountered.</p> + <p>End of file was encountered by the tokenizer.</p> </item> - <tag><c>{error, ErrorInfo, ErrorLine}</c></tag> + <tag><c>eof</c></tag> + <item> + <p>End of file was encountered by the I/O-server.</p> + </item> + <tag><c>{error, ErrorInfo, ErrorLocation}</c></tag> <item> - <p>An error occurred.</p> + <p>An error occurred while tokenizing.</p> </item> + <tag><c>{error, <anno>ErrorDescription</anno>}</c></tag> + <item> + <p>Other (rare) error condition, for instance <c>{error, estale}</c> + if reading from an NFS file system.</p> + </item> </taglist> <p>Example:</p> <pre> @@ -840,13 +916,18 @@ enter><input>1.0er.</input> <name name="scan_erl_form" arity="1"/> <name name="scan_erl_form" arity="2"/> <name name="scan_erl_form" arity="3"/> + <name name="scan_erl_form" arity="4"/> + <type name="server_no_data"/> <fsummary>Read and tokenize an Erlang form</fsummary> <desc> <p>Reads data from the standard input (<c><anno>IoDevice</anno></c>), - prompting it with <c><anno>Prompt</anno></c>. Starts reading at line number - <c><anno>StartLine</anno></c> (1). The data is tokenized as if it were an + prompting it with <c><anno>Prompt</anno></c>. Starts reading + at location <c><anno>StartLocation</anno></c> (<c>1</c>). The + argument <c><anno>Options</anno></c> is passed on as the + <c>Options</c> argument of the <c>erl_scan:tokens/4</c> + function. The data is tokenized as if it were an Erlang form - one of the valid Erlang expressions in an - Erlang source file - until a final <c>'.'</c> is reached. + Erlang source file - until a final dot (<c>.</c>) is reached. This last token is also returned. The return values are the same as for <c>scan_erl_exprs/1,2,3</c> above.</p> </desc> @@ -855,27 +936,42 @@ enter><input>1.0er.</input> <name name="parse_erl_exprs" arity="1"/> <name name="parse_erl_exprs" arity="2"/> <name name="parse_erl_exprs" arity="3"/> + <name name="parse_erl_exprs" arity="4"/> <type name="parse_ret"/> + <type name="server_no_data"/> <fsummary>Read, tokenize and parse Erlang expressions</fsummary> <desc> - <p>Reads data from the standard input (<c><anno>IoDevice</anno></c>), - prompting it with <c><anno>Prompt</anno></c>. Starts reading at line number - <c><anno>StartLine</anno></c> (1). The data is tokenized and parsed as if - it were a sequence of Erlang expressions until a final '.' is - reached. It returns:</p> + <p>Reads data from the standard input + (<c><anno>IoDevice</anno></c>), prompting it with + <c><anno>Prompt</anno></c>. Starts reading at location + <c><anno>StartLocation</anno></c> (<c>1</c>). The argument + <c><anno>Options</anno></c> is passed on as the + <c>Options</c> argument of the <c>erl_scan:tokens/4</c> + function. The data is tokenized and parsed as if it were a + sequence of Erlang expressions until a final dot (<c>.</c>) is reached. + It returns:</p> <taglist> - <tag><c>{ok, ExprList, EndLine}</c></tag> + <tag><c>{ok, ExprList, EndLocation}</c></tag> <item> <p>The parsing was successful.</p> </item> - <tag><c>{eof, EndLine}</c></tag> + <tag><c>{eof, EndLocation}</c></tag> <item> - <p>End of file was encountered.</p> + <p>End of file was encountered by the tokenizer.</p> + </item> + <tag><c>eof</c></tag> + <item> + <p>End of file was encountered by the I/O-server.</p> </item> - <tag><c>{error, ErrorInfo, ErrorLine}</c></tag> + <tag><c>{error, ErrorInfo, ErrorLocation}</c></tag> <item> - <p>An error occurred.</p> + <p>An error occurred while tokenizing or parsing.</p> </item> + <tag><c>{error, <anno>ErrorDescription</anno>}</c></tag> + <item> + <p>Other (rare) error condition, for instance <c>{error, estale}</c> + if reading from an NFS file system.</p> + </item> </taglist> <p>Example:</p> <pre> @@ -891,28 +987,42 @@ enter><input>abc("hey".</input> <name name="parse_erl_form" arity="1"/> <name name="parse_erl_form" arity="2"/> <name name="parse_erl_form" arity="3"/> + <name name="parse_erl_form" arity="4"/> <type name="parse_form_ret"/> + <type name="server_no_data"/> <fsummary>Read, tokenize and parse an Erlang form</fsummary> <desc> <p>Reads data from the standard input (<c><anno>IoDevice</anno></c>), - prompting it with <c><anno>Prompt</anno></c>. Starts reading at line number - <c><anno>StartLine</anno></c> (1). The data is tokenized and parsed as if + prompting it with <c><anno>Prompt</anno></c>. Starts reading at + location <c><anno>StartLocation</anno></c> (<c>1</c>). The argument + <c><anno>Options</anno></c> is passed on as the + <c>Options</c> argument of the <c>erl_scan:tokens/4</c> + function. The data is tokenized and parsed as if it were an Erlang form - one of the valid Erlang expressions - in an Erlang source file - until a final '.' is reached. It + in an Erlang source file - until a final dot (<c>.</c>) is reached. It returns:</p> <taglist> - <tag><c>{ok, AbsForm, EndLine}</c></tag> + <tag><c>{ok, AbsForm, EndLocation}</c></tag> <item> <p>The parsing was successful.</p> </item> - <tag><c>{eof, EndLine}</c></tag> + <tag><c>{eof, EndLocation}</c></tag> <item> - <p>End of file was encountered.</p> + <p>End of file was encountered by the tokenizer.</p> + </item> + <tag><c>eof</c></tag> + <item> + <p>End of file was encountered by the I/O-server.</p> </item> - <tag><c>{error, ErrorInfo, ErrorLine}</c></tag> + <tag><c>{error, ErrorInfo, ErrorLocation}</c></tag> <item> - <p>An error occurred.</p> + <p>An error occurred while tokenizing or parsing.</p> </item> + <tag><c>{error, <anno>ErrorDescription</anno>}</c></tag> + <item> + <p>Other (rare) error condition, for instance <c>{error, estale}</c> + if reading from an NFS file system.</p> + </item> </taglist> </desc> </func> @@ -940,7 +1050,7 @@ enter><input>bar.</input> </section> <section> <title>Standard Error</title> - <p>In certain situations, especially when the standard output is redirected, access to an io_server() specific for error messages might be convenient. The io_device 'standard_error' can be used to direct output to whatever the current operating system considers a suitable device for error output. Example on a Unix-like operating system:</p> + <p>In certain situations, especially when the standard output is redirected, access to an I/O-server specific for error messages might be convenient. The IO device <c>standard_error</c> can be used to direct output to whatever the current operating system considers a suitable IO device for error output. Example on a Unix-like operating system:</p> <pre> $ <input>erl -noshell -noinput -eval 'io:format(standard_error,"Error: ~s~n",["error 11"]),'\</input> <input>'init:stop().' > /dev/null</input> @@ -956,7 +1066,7 @@ Error: error 11</pre> <c>ErrorInfo</c> structure which is returned from all IO modules. It has the format:</p> <code type="none"> -{ErrorLine, Module, ErrorDescriptor}</code> +{ErrorLocation, Module, ErrorDescriptor}</code> <p>A string which describes the error is obtained with the following call:</p> <code type="none"> diff --git a/lib/stdlib/doc/src/io_lib.xml b/lib/stdlib/doc/src/io_lib.xml index 506c1792f1..3dac259477 100644 --- a/lib/stdlib/doc/src/io_lib.xml +++ b/lib/stdlib/doc/src/io_lib.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2011</year> + <year>1996</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -50,6 +50,12 @@ <datatype> <name name="depth"/> </datatype> + <datatype> + <name name="fread_error"/> + </datatype> + <datatype> + <name name="latin1_string"/> + </datatype> </datatypes> <funcs> <func> @@ -204,8 +210,25 @@ <name name="write_string" arity="1"/> <fsummary>Write a string</fsummary> <desc> - <p>Returns the list of characters needed to print <c><anno>String</anno></c> - as a string.</p> + <p>Returns the list of characters needed to print + <c><anno>String</anno></c> as a string.</p> + </desc> + </func> + <func> + <name name="write_string_as_latin1" arity="1"/> + <fsummary>Write a string</fsummary> + <desc> + <p>Returns the list of characters needed to print + <c><anno>String</anno></c> as a string. Non-Latin-1 + characters are escaped.</p> + </desc> + </func> + <func> + <name name="write_latin1_string" arity="1"/> + <fsummary>Write an ISO-latin-1 string</fsummary> + <desc> + <p>Returns the list of characters needed to print + <c><anno>Latin1String</anno></c> as a string.</p> </desc> </func> <func> @@ -213,6 +236,23 @@ <fsummary>Write a character</fsummary> <desc> <p>Returns the list of characters needed to print a character + constant in the Unicode character set.</p> + </desc> + </func> + <func> + <name name="write_char_as_latin1" arity="1"/> + <fsummary>Write a character</fsummary> + <desc> + <p>Returns the list of characters needed to print a character + constant in the Unicode character set. Non-Latin-1 characters + are escaped.</p> + </desc> + </func> + <func> + <name name="write_latin1_char" arity="1"/> + <fsummary>Write an ISO-latin-1 character</fsummary> + <desc> + <p>Returns the list of characters needed to print a character constant in the ISO-latin-1 character set.</p> </desc> </func> @@ -229,6 +269,14 @@ <fsummary>Test for a list of characters</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a flat list of + characters in the Unicode range, otherwise it returns <c>false</c>.</p> + </desc> + </func> + <func> + <name name="latin1_char_list" arity="1"/> + <fsummary>Test for a list of ISO-latin-1 characters</fsummary> + <desc> + <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a flat list of characters in the ISO-latin-1 range, otherwise it returns <c>false</c>.</p> </desc> </func> @@ -237,17 +285,45 @@ <fsummary>Test for a deep list of characters</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a, possibly deep, list + of characters in the Unicode range, otherwise it returns <c>false</c>.</p> + </desc> + </func> + <func> + <name name="deep_latin1_char_list" arity="1"/> + <fsummary>Test for a deep list of characters</fsummary> + <desc> + <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a, possibly deep, list of characters in the ISO-latin-1 range, otherwise it returns <c>false</c>.</p> </desc> </func> <func> <name name="printable_list" arity="1"/> + <fsummary>Test for a list of printable characters</fsummary> + <desc> + <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a flat list of + printable characters, otherwise it returns <c>false</c>.</p> + <p>What is a printable character in this case is determined by the + <c>+pc</c> start up flag to the Erlang VM. See + <seealso marker="io#printable_range/0">io:printable_range/0</seealso> + and <seealso marker="erts:erl#erl">erl(1)</seealso>.</p> + </desc> + </func> + <func> + <name name="printable_latin1_list" arity="1"/> <fsummary>Test for a list of printable ISO-latin-1 characters</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a flat list of printable ISO-latin-1 characters, otherwise it returns <c>false</c>.</p> </desc> </func> + <func> + <name name="printable_unicode_list" arity="1"/> + <fsummary>Test for a list of printable Unicode characters</fsummary> + <desc> + <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a flat list of + printable Unicode characters, otherwise it returns <c>false</c>.</p> + </desc> + </func> </funcs> </erlref> diff --git a/lib/stdlib/doc/src/io_protocol.xml b/lib/stdlib/doc/src/io_protocol.xml index 0ff3d5c1ee..d36bf2042f 100644 --- a/lib/stdlib/doc/src/io_protocol.xml +++ b/lib/stdlib/doc/src/io_protocol.xml @@ -5,7 +5,7 @@ <header> <copyright> <year>1999</year> - <year>2011</year> + <year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -35,10 +35,10 @@ <p>The I/O-protocol in Erlang specifies a way for a client to communicate -with an io_server and vice versa. The io_server is a process handling the -requests and that performs the requested task on i.e. a device. The +with an I/O server and vice versa. The I/O server is a process that handles +the requests and performs the requested task on e.g. an IO device. The client is any Erlang process wishing to read or write data from/to the -device.</p> +IO device.</p> <p>The common I/O-protocol has been present in OTP since the beginning, but has been fairly undocumented and has also somewhat @@ -53,85 +53,85 @@ implement than the original. It can certainly be argumented that the current protocol is too complex, but this text describes how it looks today, not how it should have looked.</p> -<p>The basic ideas from the original protocol still hold. The io_server +<p>The basic ideas from the original protocol still hold. The I/O server and client communicate with one single, rather simplistic protocol and -no server state is ever present in the client. Any io_server can be +no server state is ever present in the client. Any I/O server can be used together with any client code and client code need not be aware -of the actual device the io_server communicates with.</p> +of the actual IO device the I/O server communicates with.</p> <section> -<title>Protocol basics</title> +<title>Protocol Basics</title> -<p>As described in Robert's paper, servers and clients communicate using -io_request/io_reply tuples as follows:</p> +<p>As described in Robert's paper, I/O servers and clients communicate using +<c>io_request</c>/<c>io_reply</c> tuples as follows:</p> <p><em>{io_request, From, ReplyAs, Request}</em><br/> <em>{io_reply, ReplyAs, Reply}</em></p> -<p>The client sends an io_request to the io_server and the server -eventually sends a corresponding reply.</p> +<p>The client sends an <c>io_request</c> tuple to the I/O server and +the server eventually sends a corresponding <c>io_reply</c> tuple.</p> <list type="bulleted"> -<item>From is the pid() of the client, the process which the io_server -sends the reply to.</item> - -<item>ReplyAs can be any datum and is simply returned in the corresponding -io_reply. The io-module in the Erlang standard library simply uses the pid() -of the io_server as the ReplyAs datum, but a more complicated client -could have several outstanding io-requests to the same server and -would then use i.e. a reference() or something else to differentiate among -the incoming io_reply's. The ReplyAs element should be considered -opaque by the io_server. Note that the pid() of the server is not -explicitly present in the io_reply. The reply can be sent from any -process, not necessarily the actual io_server. The ReplyAs element is -the only thing that connects one io_request with an io_reply.</item> - -<item>Request and Reply are described below.</item> +<item><c>From</c> is the <c>pid()</c> of the client, the process which +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 +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 +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 +process, not necessarily the actual I/O server. The <c>ReplyAs</c> element is +the only thing that connects one I/O request with an I/O-reply.</item> + +<item><c>Request</c> and <c>Reply</c> are described below.</item> </list> -<p>When an io_server receives an io_request, it acts upon the actual -Request part and eventually sends an io_reply with the corresponding -Reply part.</p> +<p>When an I/O server receives an <c>io_request</c> tuple, it acts upon the actual +<c>Request</c> part and eventually sends an <c>io_reply</c> tuple with the corresponding +<c>Reply</c> part.</p> </section> <section> -<title>Output requests</title> +<title>Output Requests</title> -<p>To output characters on a device, the following Requests exist:</p> +<p>To output characters on an IO device, the following <c>Request</c>s exist:</p> <p> <em>{put_chars, Encoding, Characters}</em><br/> <em>{put_chars, Encoding, Module, Function, Args}</em> </p> <list type="bulleted"> -<item>Encoding is either 'unicode' or 'latin1', meaning that the +<item><c>Encoding</c> is either <c>unicode</c> or <c>latin1</c>, meaning that the characters are (in case of binaries) encoded as either UTF-8 or - iso-latin-1 (pure bytes). A well behaved io_server should also - return error if list elements contain integers > 255 when the - Encoding is set to latin1. Note that this does not in any way tell - how characters should be put on the actual device or how the - io_server should handle them. Different io_servers may handle the - characters however they want, this simply tells the io_server which - format the data is expected to have. In the Module/Function/argument - case, the Encoding tells which format the designated function - produces. Note that byte-oriented data is simplest sent using latin1 - Encoding</item> - -<item>Characters are the data to be put on the device. If Encoding is - latin1, this is an iolist(). If Encoding is unicode, this is an - Erlang standard mixed unicode list (one integer in a list per + ISO-latin-1 (pure bytes). A well behaved I/O server should also + return error if list elements contain integers > 255 when + <c>Encoding</c> is set to <c>latin1</c>. Note that this does not in any way tell + how characters should be put on the actual IO device or how the + I/O server should handle them. Different I/O servers may handle the + characters however they want, this simply tells the I/O server which + format the data is expected to have. In the <c>Module</c>/<c>Function</c>/<c>Args</c> + case, <c>Encoding</c> tells which format the designated function + produces. Note that byte-oriented data is simplest sent using the ISO-latin-1 + encoding.</item> + +<item>Characters are the data to be put on the IO device. If <c>Encoding</c> is + <c>latin1</c>, this is an <c>iolist()</c>. If <c>Encoding</c> is <c>unicode</c>, this is an + Erlang standard mixed Unicode list (one integer in a list per character, characters in binaries represented as UTF-8).</item> -<item>Module, Function, Args denotes a function which will be called to - produce the data (like io_lib:format). Args is a list of arguments +<item><c>Module</c>, <c>Function</c>, and <c>Args</c> denote a function which will be called to + produce the data (like <c>io_lib:format/2</c>). <c>Args</c> is a list of arguments to the function. The function should produce data in the given - Encoding. The io_server should call the function as apply(Mod, Func, - Args) and will put the returned data on the device as if it was sent - in a {put_chars, Encoding, Characters} request. If the function + <c>Encoding</c>. The I/O server should call the function as + <c>apply(Mod, Func, Args)</c> and will put the returned data on the IO device as if it was sent + in a <c>{put_chars, Encoding, Characters}</c> request. If the function returns anything else than a binary or list or throws an exception, an error should be sent back to the client.</item> </list> -<p>The server replies to the client with an io_reply where the Reply +<p>The I/O server replies to the client with an <c>io_reply</c> tuple where the <c>Reply</c> element is one of:</p> <p> <em>ok</em><br/> @@ -139,49 +139,50 @@ element is one of:</p> </p> <list type="bulleted"> -<item>Error describes the error to the client, which may do whatever it - wants with it. The Erlang io-module typically returns it as is.</item> +<item><c>Error</c> describes the error to the client, which may do whatever + it wants with it. The Erlang <seealso marker="stdlib:io">io</seealso> + module typically returns it as is.</item> </list> -<p>For backward compatibility the following Requests should also be -handled by an io_server (these messages should not be present after +<p>For backward compatibility the following <c>Request</c>s should also be +handled by an I/O server (these requests should not be present after R15B of OTP):</p> <p> <em>{put_chars, Characters}</em><br/> <em>{put_chars, Module, Function, Args}</em> </p> -<p>These should behave as {put_chars, latin1, Characters} and {put_chars, -latin1, Module, Function, Args} respectively. </p> +<p>These should behave as <c>{put_chars, latin1, Characters}</c> and +<c>{put_chars, latin1, Module, Function, Args}</c> respectively. </p> </section> <section> <title>Input Requests</title> -<p>To read characters from a device, the following Requests exist:</p> +<p>To read characters from an IO device, the following <c>Request</c>s exist:</p> <p><em>{get_until, Encoding, Prompt, Module, Function, ExtraArgs}</em></p> <list type="bulleted"> -<item>Encoding denotes how data is to be sent back to the client and +<item><c>Encoding</c> denotes how data is to be sent back to the client and what data is sent to the function denoted by - Module/Function/ExtraArgs. If the function supplied returns data as a + <c>Module</c>/<c>Function</c>/<c>ExtraArgs</c>. If the function supplied returns data as a list, the data is converted to this encoding. If however the function supplied returns data in some other format, no conversion - can be done and it's up to the client supplied function to return - data in a proper way. If Encoding is latin1, lists of integers + can be done and it is up to the client supplied function to return + data in a proper way. If <c>Encoding</c> is <c>latin1</c>, lists of integers 0..255 or binaries containing plain bytes are sent back to the - client when possible, if Encoding is unicode, lists with integers in - the whole unicode range or binaries encoded in UTF-8 are sent to the + client when possible; if <c>Encoding</c> is <c>unicode</c>, lists with integers in + the whole Unicode range or binaries encoded in UTF-8 are sent to the client. The user supplied function will always see lists of integers, never - binaries, but the list may contain numbers > 255 if the Encoding is - 'unicode'.</item> + binaries, but the list may contain numbers > 255 if the <c>Encoding</c> is + <c>unicode</c>.</item> -<item>Prompt is a list of characters (not mixed, no binaries) or an atom() - to be output as a prompt for input on the device. The Prompt is - often ignored by the io_server and a Prompt set to '' should always - be ignored (and result in nothing being written to the device).</item> +<item><c>Prompt</c> is a list of characters (not mixed, no binaries) or an atom + to be output as a prompt for input on the IO device. <c>Prompt</c> is + often ignored by the I/O server and if set to <c>''</c> it should always + be ignored (and result in nothing being written to the IO device).</item> -<item><p>Module, Function, ExtraArgs denotes a function and arguments to +<item><p><c>Module</c>, <c>Function</c>, and <c>ExtraArgs</c> denote a function and arguments to determine when enough data is written. The function should take two additional arguments, the last state, and a list of characters. The function should return one of:</p> @@ -189,23 +190,23 @@ latin1, Module, Function, Args} respectively. </p> <em>{done, Result, RestChars}</em><br/> <em>{more, Continuation}</em> </p> - <p>The Result can be any Erlang term, but if it is a list(), the - io_server may convert it to a binary() of appropriate format before - returning it to the client, if the server is set in binary mode (see + <p>The <c>Result</c> can be any Erlang term, but if it is a <c>list()</c>, the + I/O server may convert it to a <c>binary()</c> of appropriate format before + returning it to the client, if the I/O server is set in binary mode (see below).</p> - <p>The function will be called with the data the io_server finds on - its device, returning {done, Result, RestChars} when enough data is - read (in which case Result is sent to the client and RestChars are - kept in the io_server as a buffer for subsequent input) or {more, - Continuation}, indicating that more characters are needed to - complete the request. The Continuation will be sent as the state in + <p>The function will be called with the data the I/O server finds on + its IO device, returning <c>{done, Result, RestChars}</c> when enough data is + read (in which case <c>Result</c> is sent to the client and <c>RestChars</c> is + kept in the I/O server as a buffer for subsequent input) or + <c>{more, Continuation}</c>, indicating that more characters are needed to + complete the request. The <c>Continuation</c> will be sent as the state in subsequent calls to the function when more characters are available. When no more characters are available, the function - shall return {done,eof,Rest}. + shall return <c>{done, eof, Rest}</c>. The initial state is the empty list and the data when an - end of file is reached on the device is the atom 'eof'. An emulation - of the get_line request could be (inefficiently) implemented using + end of file is reached on the IO device is the atom <c>eof</c>. An emulation + of the <c>get_line</c> request could be (inefficiently) implemented using the following functions:</p> <code> -module(demo). @@ -214,7 +215,9 @@ latin1, Module, Function, Args} respectively. </p> until_newline(_ThisFar,eof,_MyStopCharacter) -> {done,eof,[]}; until_newline(ThisFar,CharList,MyStopCharacter) -> - case lists:splitwith(fun(X) -> X =/= MyStopCharacter end, CharList) of + case + lists:splitwith(fun(X) -> X =/= MyStopCharacter end, CharList) + of {L,[]} -> {more,ThisFar++L}; {L2,[MyStopCharacter|Rest]} -> @@ -222,45 +225,47 @@ until_newline(ThisFar,CharList,MyStopCharacter) -> end. get_line(IoServer) -> - IoServer ! {io_request, self(), IoServer, {get_until, unicode, '', - ?MODULE, until_newline, [$\n]}}, + IoServer ! {io_request, + self(), + IoServer, + {get_until, unicode, '', ?MODULE, until_newline, [$\n]}}, receive {io_reply, IoServer, Data} -> Data end. </code> - <p>Note especially that the last element in the Request tuple ([$\n]) + <p>Note especially that the last element in the <c>Request</c> tuple (<c>[$\n]</c>) is appended to the argument list when the function is called. The function should be called like - apply(Module, Function, [ State, Data | ExtraArgs ]) by the io_server</p> + <c>apply(Module, Function, [ State, Data | ExtraArgs ])</c> by the I/O server</p> </item> </list> -<p>A defined number of characters is requested using this Request:</p> +<p>A fixed number of characters is requested using this <c>Request</c>:</p> <p> <em>{get_chars, Encoding, Prompt, N}</em> </p> <list type="bulleted"> -<item>Encoding and Prompt as for get_until.</item> +<item><c>Encoding</c> and <c>Prompt</c> as for <c>get_until</c>.</item> -<item>N is the number of characters to be read from the device.</item> +<item><c>N</c> is the number of characters to be read from the IO device.</item> </list> -<p>A single line (like in the example above) is requested with this Request:</p> +<p>A single line (like in the example above) is requested with this <c>Request</c>:</p> <p> <em>{get_line, Encoding, Prompt}</em> </p> <list type="bulleted"> -<item>Encoding and prompt as above.</item> +<item><c>Encoding</c> and <c>Prompt</c> as above.</item> </list> -<p>Obviously, get_chars and get_line could be implemented with the -get_until request (and indeed was originally), but demands for +<p>Obviously, the <c>get_chars</c> and <c>get_line</c> could be implemented with the +<c>get_until</c> request (and indeed they were originally), but demands for efficiency has made these additions necessary.</p> -<p>The server replies to the client with an io_reply where the Reply +<p>The I/O server replies to the client with an <c>io_reply</c> tuple where the <c>Reply</c> element is one of:</p> <p> <em>Data</em><br/> @@ -269,16 +274,17 @@ element is one of:</p> </p> <list type="bulleted"> -<item>Data is the characters read, in either list or binary form - (depending on the io_server mode, see below).</item> -<item>Error describes the error to the client, which may do whatever it - wants with it. The Erlang io-module typically returns it as is.</item> -<item>eof is returned when input end is reached and no more data is +<item><c>Data</c> is the characters read, in either list or binary form + (depending on the I/O server mode, see below).</item> +<item><c>Error</c> describes the error to the client, which may do whatever it + wants with it. The Erlang <seealso marker="stdlib:io">io</seealso> + module typically returns it as is.</item> +<item><c>eof</c> is returned when input end is reached and no more data is available to the client process.</item> </list> -<p>For backward compatibility the following Requests should also be -handled by an io_server (these messages should not be present after +<p>For backward compatibility the following <c>Request</c>s should also be +handled by an I/O server (these reqeusts should not be present after R15B of OTP):</p> <p> @@ -287,30 +293,30 @@ R15B of OTP):</p> <em>{get_line, Prompt}</em><br/> </p> -<p>These should behave as {get_until, latin1, Prompt, Module, Function, -ExtraArgs}, {get_chars, latin1, Prompt, N} and {get_line, latin1, -Prompt} respectively.</p> +<p>These should behave as <c>{get_until, latin1, Prompt, Module, Function, +ExtraArgs}</c>, <c>{get_chars, latin1, Prompt, N}</c> and <c>{get_line, latin1, +Prompt}</c> respectively.</p> </section> <section> -<title>I/O-server modes</title> - -<p>Demands for efficiency when reading data from an io_server has not -only lead to the addition of the get_line and get_chars requests, but -has also added the concept of io_server options. No options are -mandatory to implement, but all io_servers in the Erlang standard -libraries honor the 'binary' option, which allows the Data in the -io_reply to be binary instead of in list form <em>when possible</em>. +<title>I/O-server Modes</title> + +<p>Demands for efficiency when reading data from an I/O server has not +only lead to the addition of the <c>get_line</c> and <c>get_chars</c> requests, but +has also added the concept of I/O server options. No options are +mandatory to implement, but all I/O servers in the Erlang standard +libraries honor the <c>binary</c> option, which allows the <c>Data</c> element of the +<c>io_reply</c> tuple to be a binary instead of a list <em>when possible</em>. If the data is sent as a binary, Unicode data will be sent in the -standard Erlang unicode -format, i.e. UTF-8 (note that the function in get_until still gets -list data regardless of the io_server mode).</p> +standard Erlang Unicode +format, i.e. UTF-8 (note that the function of the <c>get_until</c> request still gets +list data regardless of the I/O server mode).</p> -<p>Note that i.e. the <c>get_until</c> request allows for a function with the data specified as always being a list. Also the return value data from such a function can be of any type (as is indeed the case when an io:fread request is sent to an io_server). The client has to be prepared for data received as answers to those requests to be in a variety of forms, but the server should convert the results to binaries whenever possible (i.e. when the function supplied to get_until actually returns a list). The example shown later in this text does just that.</p> +<p>Note that i.e. the <c>get_until</c> request allows for a function with the data specified as always being a list. Also the return value data from such a function can be of any type (as is indeed the case when an <c>io:fread</c> request is sent to an I/O server). The client has to be prepared for data received as answers to those requests to be in a variety of forms, but the I/O server should convert the results to binaries whenever possible (i.e. when the function supplied to <c>get_until</c> actually returns a list). The example shown later in this text does just that.</p> <p>An I/O-server in binary mode will affect the data sent to the client, so that it has to be able to handle binary data. For convenience, it -is possible to set and retrieve the modes of an io_server using the -following I/O-requests:</p> +is possible to set and retrieve the modes of an I/O server using the +following I/O requests:</p> <p> <em>{setopts, Opts}</em> @@ -318,72 +324,72 @@ following I/O-requests:</p> <list type="bulleted"> -<item>Opts is a list of options in the format recognized by proplists (and - of course by the io_server itself).</item> +<item><c>Opts</c> is a list of options in the format recognized by <seealso marker="stdlib:proplists">proplists</seealso> (and + of course by the I/O server itself).</item> </list> -<p>As an example, the io_server for the interactive shell (in group.erl) +<p>As an example, the I/O server for the interactive shell (in <c>group.erl</c>) understands the following options:</p> <p> -<em>{binary, bool()} (or 'binary'/'list')</em><br/> -<em>{echo, bool()}</em><br/> +<em>{binary, boolean()}</em> (or <em>binary</em>/<em>list</em>)<br/> +<em>{echo, boolean()}</em><br/> <em>{expand_fun, fun()}</em><br/> -<em>{encoding, 'unicode'/'latin1'} (or 'unicode'/'latin1')</em> +<em>{encoding, unicode/latin1}</em> (or <em>unicode</em>/<em>latin1</em>) </p> -<p>- of which the 'binary' and 'encoding' options are common for all -io_servers in OTP, while 'echo' and 'expand' is valid only for this -io_server. It's worth noting that the 'unicode' option notifies how -characters are actually put on the physical device, i.e. if the -terminal per se is unicode aware, it does not affect how characters +<p>- of which the <c>binary</c> and <c>encoding</c> options are common for all +I/O servers in OTP, while <c>echo</c> and <c>expand</c> are valid only for this +I/O server. It is worth noting that the <c>unicode</c> option notifies how +characters are actually put on the physical IO device, i.e. if the +terminal per se is Unicode aware, it does not affect how characters are sent in the I/O-protocol, where each request contains encoding information for the provided or returned data.</p> -<p>The server should send one of the following as Reply:</p> +<p>The I/O server should send one of the following as <c>Reply</c>:</p> <p> <em>ok</em><br/> <em>{error, Error}</em> </p> -<p>An error (preferably enotsup) is to be expected if the option is -not supported by the io_server (like if an 'echo' option is sent in a -setopt Request to a plain file).</p> +<p>An error (preferably <c>enotsup</c>) is to be expected if the option is +not supported by the I/O server (like if an <c>echo</c> option is sent in a +<c>setopts</c> request to a plain file).</p> -<p>To retrieve options, this message is used:</p> +<p>To retrieve options, this request is used:</p> <p> <em>getopts</em> </p> -<p>The 'getopts' message requests a complete list of all options -supported by the io_server as well as their current values.</p> +<p>The <c>getopts</c> request asks for a complete list of all options +supported by the I/O server as well as their current values.</p> -<p>The server replies:</p> +<p>The I/O server replies:</p> <p> <em>OptList</em><br/> -<em>{error,Error}</em> +<em>{error, Error}</em> </p> <list type="bulleted"> -<item>OptList is a list of tuples {Option, Value} where Option is always +<item><c>OptList</c> is a list of tuples <c>{Option, Value}</c> where <c>Option</c> is always an atom.</item> </list> </section> <section> -<title>Multiple I/O requests</title> +<title>Multiple I/O Requests</title> -<p>The Request element can in itself contain several Requests by using +<p>The <c>Request</c> element can in itself contain several <c>Request</c>s by using the following format:</p> <p> <em>{requests, Requests}</em> </p> <list type="bulleted"> -<item>Requests is a list of valid Request tuples for the protocol, they +<item><c>Requests</c> is a list of valid <c>io_request</c> tuples for the protocol, they shall be executed in the order in which they appear in the list and the execution should continue until one of the requests result in an error or the list is consumed. The result of the last request is sent back to the client.</item> </list> -<p>The server can for a list of requests send any of the valid results in +<p>The I/O server can for a list of requests send any of the valid results in the reply:</p> <p> @@ -395,7 +401,7 @@ the reply:</p> <p>- depending on the actual requests in the list.</p> </section> <section> -<title>Optional I/O-requests</title> +<title>Optional I/O Requests</title> <p>The following I/O request is optional to implement and a client should be prepared for an error return:</p> @@ -403,47 +409,47 @@ should be prepared for an error return:</p> <em>{get_geometry, Geometry}</em> </p> <list type="bulleted"> -<item>Geometry is either the atom 'rows' or the atom 'columns'.</item> +<item><c>Geometry</c> is either the atom <c>rows</c> or the atom <c>columns</c>.</item> </list> -<p>The server should send the Reply as:</p> +<p>The I/O server should send the <c>Reply</c> as:</p> <p> <em>{ok, N}</em><br/> <em>{error, Error}</em> </p> <list type="bulleted"> -<item>N is the number of character rows or columns the device has, if - applicable to the device the io_server handles, otherwise {error, - enotsup} is a good answer.</item> +<item><c>N</c> is the number of character rows or columns the IO device has, if + applicable to the IO device the I/O server handles, otherwise <c>{error, + enotsup}</c> is a good answer.</item> </list> </section> <section> -<title>Unimplemented request types:</title> +<title>Unimplemented Request Types</title> -<p>If an io_server encounters a request it does not recognize (i.e. the -io_request tuple is in the expected format, but the actual Request is -unknown), the server should send a valid reply with the error tuple:</p> +<p>If an I/O server encounters a request it does not recognize (i.e. the +<c>io_request</c> tuple is in the expected format, but the actual <c>Request</c> is +unknown), the I/O server should send a valid reply with the error tuple:</p> <p> <em>{error, request}</em> </p> -<p>This makes it possible to extend the protocol with optional messages +<p>This makes it possible to extend the protocol with optional requests and for the clients to be somewhat backwards compatible.</p> </section> <section> -<title>An annotated and working example io_server:</title> +<title>An Annotated and Working Example I/O Server</title> -<p>An io_server is any process capable of handling the protocol. There is -no generic io_server behavior, but could well be. The framework is +<p>An I/O server is any process capable of handling the I/O protocol. There is +no generic I/O server behavior, but could well be. The framework is simple enough, a process handling incoming requests, usually both -io_requests and other device-specific requests (for i.e. positioning , +I/O-requests and other IO device-specific requests (for i.e. positioning, closing etc.).</p> -<p>Our example io_server stores characters in an ets table, making up a +<p>Our example I/O server stores characters in an ETS table, making up a fairly crude ram-file (it is probably not useful, but working).</p> <p>The module begins with the usual directives, a function to start the -server and a main loop handling the requests:</p> +I/O server and a main loop handling the requests:</p> <code> -module(ets_io_server). @@ -486,18 +492,19 @@ loop(State) -> </code> <p>The main loop receives messages from the client (which might be using -the io-module to send requests). For each request the function -request/2 is called and a reply is eventually sent using the reply/3 +the <seealso marker="stdlib:io">io</seealso> module to send requests). +For each request the function +<c>request/2</c> is called and a reply is eventually sent using the <c>reply/3</c> function.</p> -<p>The "private" message {From, rewind} results in the +<p>The "private" message <c>{From, rewind}</c> results in the current position in the pseudo-file to be reset to 0 (the beginning of -the "file"). This is a typical example of device-specific +the "file"). This is a typical example of IO device-specific messages not being part of the I/O-protocol. It is usually a bad idea -to embed such private messages in io_request tuples, as that might be +to embed such private messages in <c>io_request</c> tuples, as that might be confusing to the reader.</p> -<p>Let's look at the reply function first...</p> +<p>Let us look at the reply function first...</p> <code> @@ -506,8 +513,8 @@ reply(From, ReplyAs, Reply) -> </code> -<p>Simple enough, it sends the io_reply tuple back to the client, -providing the ReplyAs element received in the request along with the +<p>Simple enough, it sends the <c>io_reply</c> tuple back to the client, +providing the <c>ReplyAs</c> element received in the request along with the result of the request, as described above.</p> <p>Now look at the different requests we need to handle. First the @@ -525,18 +532,18 @@ request({put_chars, Encoding, Module, Function, Args}, State) -> end; </code> -<p>The Encoding tells us how the characters in the message are +<p>The <c>Encoding</c> tells us how the characters in the request are represented. We want to store the characters as lists in the -ets-table, so we convert them to lists using the -unicode:characters_to_list/2 function. The conversion function -conveniently accepts the encoding types unicode or latin1, so we can -use the Encoding parameter directly.</p> +ETS table, so we convert them to lists using the +<seealso marker="stdlib:unicode#characters_to_list/2"><c>unicode:characters_to_list/2</c></seealso> function. The conversion function +conveniently accepts the encoding types <c>unicode</c> or <c>latin1</c>, so we can +use <c>Encoding</c> directly.</p> -<p>When Module, Function and Arguments are provided, we simply apply it +<p>When <c>Module</c>, <c>Function</c> and <c>Arguments</c> are provided, we simply apply it and do the same thing with the result as if the data was provided directly.</p> -<p>Let's handle the requests for retrieving data too:</p> +<p>Let us handle the requests for retrieving data too:</p> <code> request({get_until, Encoding, _Prompt, M, F, As}, State) -> @@ -550,11 +557,11 @@ request({get_line, Encoding, _Prompt}, State) -> </code> <p>Here we have cheated a little by more or less only implementing -get_until and using internal helpers to implement get_chars and -get_line. In production code, this might be to inefficient, but that +<c>get_until</c> and using internal helpers to implement <c>get_chars</c> and +<c>get_line</c>. In production code, this might be too inefficient, but that of course depends on the frequency of the different requests. Before -we start actually implementing the functions put_chars/2 and -get_until/5, lets look into the few remaining requests:</p> +we start actually implementing the functions <c>put_chars/2</c> and +<c>get_until/5</c>, let us look into the few remaining requests:</p> <code> request({get_geometry,_}, State) -> @@ -567,18 +574,18 @@ request({requests, Reqs}, State) -> multi_request(Reqs, {ok, ok, State}); </code> -<p>The get_geometry request has no meaning for this io_server, so the -reply will be {error, enotsup}. The only option we handle is the -binary/list option, which is done in separate functions.</p> +<p>The <c>get_geometry</c> request has no meaning for this I/O server, so the +reply will be <c>{error, enotsup}</c>. The only option we handle is the +<c>binary</c>/<c>list</c> option, which is done in separate functions.</p> -<p>The multi-request tag (requests) is handled in a separate loop +<p>The multi-request tag (<c>requests</c>) is handled in a separate loop function applying the requests in the list one after another, returning the last result.</p> -<p>What's left is to handle backward compatibility and the file-module +<p>What is left is to handle backward compatibility and the <seealso marker="kernel:file">file</seealso> module (which uses the old requests until backward compatibility with pre-R13 -nodes is no longer needed). Note that the io_server will not work with -a simple file:write if these are not added:</p> +nodes is no longer needed). Note that the I/O server will not work with +a simple <c>file:write/2</c> if these are not added:</p> <code> request({put_chars,Chars}, State) -> @@ -593,7 +600,7 @@ request({get_until, Prompt,M,F,As}, State) -> request({get_until,latin1,Prompt,M,F,As}, State); </code> -<p>Ok, what's left now is to return {error, request} if the request is +<p>OK, what is left now is to return <c>{error, request}</c> if the request is not recognized:</p> <code> @@ -601,7 +608,7 @@ request(_Other, State) -> {error, {error, request}, State}. </code> -<p>Let's move further and actually handle the different requests, first +<p>Let us move further and actually handle the different requests, first the fairly generic multi-request type:</p> <code> @@ -615,10 +622,10 @@ multi_request([], Result) -> <p>We loop through the requests one at the time, stopping when we either encounter an error or the list is exhausted. The last return value is -sent back to the client (it's first returned to the main loop and then -sent back by the function io_reply).</p> +sent back to the client (it is first returned to the main loop and then +sent back by the function <c>io_reply</c>).</p> -<p>The getopt and setopt requests are also simple to handle, we just +<p>The <c>getopts</c> and <c>setopts</c> requests are also simple to handle, we just change or read our state record:</p> <code> @@ -656,24 +663,24 @@ getopts(#state{mode=M} = S) -> end}],S}. </code> -<p>As a convention, all io_servers handle both {setopts, [binary]}, -{setopts, [list]} and {setopts,[{binary, bool()}]}, hence the trick -with proplists:substitute_negations/2 and proplists:unfold/1. If -invalid options are sent to us, we send {error,enotsup} back to the +<p>As a convention, all I/O servers handle both <c>{setopts, [binary]}</c>, +<c>{setopts, [list]}</c> and <c>{setopts,[{binary, boolean()}]}</c>, hence the trick +with <c>proplists:substitute_negations/2</c> and <c>proplists:unfold/1</c>. If +invalid options are sent to us, we send <c>{error, enotsup}</c> back to the client.</p> -<p>The getopts request should return a list of {Option, Value} tuples, +<p>The <c>getopts</c> request should return a list of <c>{Option, Value}</c> tuples, which has the twofold function of providing both the current values -and the available options of this io_server. We have only one option, +and the available options of this I/O server. We have only one option, and hence return that.</p> -<p>So far our io_server has been fairly generic (except for the rewind -request handled in the main loop and the creation of an ets table). -Most io_servers contain code similar to what's above.</p> +<p>So far our I/O server has been fairly generic (except for the <c>rewind</c> +request handled in the main loop and the creation of an ETS table). +Most I/O servers contain code similar to the one above.</p> <p>To make the example runnable, we now start implementing the actual -reading and writing of the data to/from the ets-table. First the -put_chars function:</p> +reading and writing of the data to/from the ETS table. First the +<c>put_chars/3</c> function:</p> <code> put_chars(Chars, #state{table = T, position = P} = State) -> @@ -686,10 +693,10 @@ put_chars(Chars, #state{table = T, position = P} = State) -> <p>We already have the data as (Unicode) lists and therefore just split the list in runs of a predefined size and put each run in the table at the current position (and forward). The functions -split_data/3 and apply_update/2 are implemented below.</p> +<c>split_data/3</c> and <c>apply_update/2</c> are implemented below.</p> -<p>Now we want to read data from the table. The get_until function reads -data and applies the function until it says it's done. The result is +<p>Now we want to read data from the table. The <c>get_until/5</c> function reads +data and applies the function until it says it is done. The result is sent back to the client:</p> <code> @@ -700,11 +707,12 @@ get_until(Encoding, Mod, Func, As, if M =:= binary -> {ok, - unicode:characters_to_binary(Data,unicode,Encoding), + unicode:characters_to_binary(Data, unicode, Encoding), State#state{position = NewP}}; true -> case check(Encoding, - unicode:characters_to_list(Data, unicode)) of + unicode:characters_to_list(Data, unicode)) + of {error, _} = E -> {error, E, State}; List -> @@ -730,24 +738,24 @@ get_loop(M,F,A,T,P,C) -> end. </code> -<p>Here we also handle the mode (binary or list) that can be set by -the setopts request. By default, all OTP io_servers send data back to -the client as lists, but switching mode to binary might increase -efficiency if the server handles it in an appropriate way. The -implementation of get_until is hard to get efficient as the supplied -function is defined to take lists as arguments, but get_chars and -get_line can be optimized for binary mode. This example does not +<p>Here we also handle the mode (<c>binary</c> or <c>list</c>) that can be set by +the <c>setopts</c> request. By default, all OTP I/O servers send data back to +the client as lists, but switching mode to <c>binary</c> might increase +efficiency if the I/O server handles it in an appropriate way. The +implementation of <c>get_until</c> is hard to get efficient as the supplied +function is defined to take lists as arguments, but <c>get_chars</c> and +<c>get_line</c> can be optimized for binary mode. This example does not optimize anything however. It is important though that the returned data is of the right type depending on the options set, so we convert the lists to binaries in the correct encoding <em>if possible</em> -before returning. The function supplied in the get_until request may, +before returning. The function supplied in the <c>get_until</c> request tuple may, as its final result return anything, so only functions actually returning lists can get them converted to binaries. If the request -contained the encoding tag unicode, the lists can contain all unicode +contained the encoding tag <c>unicode</c>, the lists can contain all Unicode codepoints and the binaries should be in UTF-8, if the encoding tag -was latin1, the client should only get characters in the range -0..255. The function check/2 takes care of not returning arbitrary -unicode codepoints in lists if the encoding was given as latin1. If +was <c>latin1</c>, the client should only get characters in the range +0..255. The function <c>check/2</c> takes care of not returning arbitrary +Unicode codepoints in lists if the encoding was given as <c>latin1</c>. If the function did not return a list, the check cannot be performed and the result will be that of the supplied function untouched.</p> @@ -768,13 +776,13 @@ check(latin1, List) -> end. </code> -<p>The function check takes care of providing an error tuple if unicode +<p>The function check takes care of providing an error tuple if Unicode codepoints above 255 is to be returned if the client requested latin1.</p> -<p>The two functions until_newline/3 and until_enough/3 are helpers used -together with the get_until function to implement get_chars and -get_line (inefficiently):</p> +<p>The two functions <c>until_newline/3</c> and <c>until_enough/3</c> are helpers used +together with the <c>get_until/5</c> function to implement <c>get_chars</c> and +<c>get_line</c> (inefficiently):</p> <code> until_newline([],eof,_MyStopCharacter) -> @@ -782,7 +790,9 @@ until_newline([],eof,_MyStopCharacter) -> until_newline(ThisFar,eof,_MyStopCharacter) -> {done,ThisFar,[]}; until_newline(ThisFar,CharList,MyStopCharacter) -> - case lists:splitwith(fun(X) -> X =/= MyStopCharacter end, CharList) of + case + lists:splitwith(fun(X) -> X =/= MyStopCharacter end, CharList) + of {L,[]} -> {more,ThisFar++L}; {L2,[MyStopCharacter|Rest]} -> @@ -802,10 +812,10 @@ until_enough(ThisFar,CharList,_N) -> </code> <p>As can be seen, the functions above are just the type of functions -that should be provided in get_until requests.</p> +that should be provided in <c>get_until</c> requests.</p> <p>Now we only need to read and write the table in an appropriate way to -complete the server:</p> +complete the I/O server:</p> <code> get(P,Tab) -> @@ -847,13 +857,13 @@ apply_update(Table, {Row, Col, List}) -> end. </code> -<p>The table is read or written in chunks of ?CHARS_PER_REC, overwriting +<p>The table is read or written in chunks of <c>?CHARS_PER_REC</c>, overwriting when necessary. The implementation is obviously not efficient, it is just working.</p> <p>This concludes the example. It is fully runnable and you can read or -write to the io_server by using i.e. the io_module or even the file -module. It's as simple as that to implement a fully fledged io_server +write to the I/O server by using i.e. the <seealso marker="stdlib:io">io</seealso> module or even the <seealso marker="kernel:file">file</seealso> +module. It is as simple as that to implement a fully fledged I/O server in Erlang.</p> </section> </chapter> diff --git a/lib/stdlib/doc/src/lists.xml b/lib/stdlib/doc/src/lists.xml index 8b31f3ac3d..b6c0fa4e05 100644 --- a/lib/stdlib/doc/src/lists.xml +++ b/lib/stdlib/doc/src/lists.xml @@ -248,18 +248,13 @@ flatmap(Fun, List1) -> </desc> </func> <func> - <name>keyfind(Key, N, TupleList) -> Tuple | false</name> + <name name="keyfind" arity="3"/> + <type_desc variable="N">1..tuple_size(<anno>Tuple</anno>)</type_desc> <fsummary>Search for an element in a list of tuples</fsummary> - <type> - <v>Key = term()</v> - <v>N = 1..tuple_size(Tuple)</v> - <v>TupleList = [Tuple]</v> - <v>Tuple = tuple()</v> - </type> - <desc> - <p>Searches the list of tuples <c>TupleList</c> for a - tuple whose <c>N</c>th element compares equal to <c>Key</c>. - Returns <c>Tuple</c> if such a tuple is found, + <desc> + <p>Searches the list of tuples <c><anno>TupleList</anno></c> for a + tuple whose <c><anno>N</anno></c>th element compares equal to <c><anno>Key</anno></c>. + Returns <c><anno>Tuple</anno></c> if such a tuple is found, otherwise <c>false</c>.</p> </desc> </func> @@ -281,17 +276,12 @@ flatmap(Fun, List1) -> </desc> </func> <func> - <name>keymember(Key, N, TupleList) -> boolean()</name> + <name name="keymember" arity="3"/> + <type_desc variable="N">1..tuple_size(<anno>Tuple</anno>)</type_desc> <fsummary>Test for membership of a list of tuples</fsummary> - <type> - <v>Key = term()</v> - <v>N = 1..tuple_size(Tuple)</v> - <v>TupleList = [Tuple]</v> - <v> Tuple = tuple()</v> - </type> - <desc> - <p>Returns <c>true</c> if there is a tuple in <c>TupleList</c> - whose <c>N</c>th element compares equal to <c>Key</c>, otherwise + <desc> + <p>Returns <c>true</c> if there is a tuple in <c><anno>TupleList</anno></c> + whose <c><anno>N</anno></c>th element compares equal to <c><anno>Key</anno></c>, otherwise <c>false</c>.</p> </desc> </func> @@ -321,18 +311,13 @@ flatmap(Fun, List1) -> </desc> </func> <func> - <name>keysearch(Key, N, TupleList) -> {value, Tuple} | false</name> + <name name="keysearch" arity="3"/> + <type_desc variable="N">1..tuple_size(<anno>Tuple</anno>)</type_desc> <fsummary>Search for an element in a list of tuples</fsummary> - <type> - <v>Key = term()</v> - <v>N = 1..tuple_size(Tuple)</v> - <v>TupleList = [Tuple]</v> - <v>Tuple = tuple()</v> - </type> - <desc> - <p>Searches the list of tuples <c>TupleList</c> for a - tuple whose <c>N</c>th element compares equal to <c>Key</c>. - Returns <c>{value, Tuple}</c> if such a tuple is found, + <desc> + <p>Searches the list of tuples <c><anno>TupleList</anno></c> for a + tuple whose <c><anno>N</anno></c>th element compares equal to <c><anno>Key</anno></c>. + Returns <c>{value, <anno>Tuple</anno>}</c> if such a tuple is found, otherwise <c>false</c>.</p> <note><p>This function is retained for backward compatibility. The function <c>lists:keyfind/3</c> (introduced in R13A) @@ -425,15 +410,11 @@ flatmap(Fun, List1) -> </desc> </func> <func> - <name>member(Elem, List) -> boolean()</name> + <name name="member" arity="2"/> <fsummary>Test for membership of a list</fsummary> - <type> - <v>Elem = term()</v> - <v>List = [term()]</v> - </type> <desc> - <p>Returns <c>true</c> if <c>Elem</c> matches some element of - <c>List</c>, otherwise <c>false</c>.</p> + <p>Returns <c>true</c> if <c><anno>Elem</anno></c> matches some element of + <c><anno>List</anno></c>, otherwise <c>false</c>.</p> </desc> </func> <func> @@ -562,14 +543,11 @@ c</pre> </desc> </func> <func> - <name>reverse(List1, Tail) -> List2</name> + <name name="reverse" arity="2"/> <fsummary>Reverse a list appending a tail</fsummary> - <type> - <v>List1 = Tail = List2 = [term()]</v> - </type> <desc> - <p>Returns a list with the elements in <c>List1</c> - in reverse order, with the tail <c>Tail</c> appended. For + <p>Returns a list with the elements in <c><anno>List1</anno></c> + in reverse order, with the tail <c><anno>Tail</anno></c> appended. For example:</p> <pre> > <input>lists:reverse([1, 2, 3, 4], [a, b, c]).</input> diff --git a/lib/stdlib/doc/src/math.xml b/lib/stdlib/doc/src/math.xml index 518457d5d8..0219dcce10 100644 --- a/lib/stdlib/doc/src/math.xml +++ b/lib/stdlib/doc/src/math.xml @@ -5,7 +5,7 @@ <header> <copyright> <year>1996</year> - <year>2011</year> + <year>2012</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> @@ -52,54 +52,47 @@ </desc> </func> <func> - <name>sin(X)</name> - <name>cos(X)</name> - <name>tan(X)</name> - <name>asin(X)</name> - <name>acos(X)</name> - <name>atan(X)</name> - <name>atan2(Y, X)</name> - <name>sinh(X)</name> - <name>cosh(X)</name> - <name>tanh(X)</name> - <name>asinh(X)</name> - <name>acosh(X)</name> - <name>atanh(X)</name> - <name>exp(X)</name> - <name>log(X)</name> - <name>log10(X)</name> - <name>pow(X, Y)</name> - <name>sqrt(X)</name> + <name name="sin" arity="1"/> + <name name="cos" arity="1"/> + <name name="tan" arity="1"/> + <name name="asin" arity="1"/> + <name name="acos" arity="1"/> + <name name="atan" arity="1"/> + <name name="atan2" arity="2"/> + <name name="sinh" arity="1"/> + <name name="cosh" arity="1"/> + <name name="tanh" arity="1"/> + <name name="asinh" arity="1"/> + <name name="acosh" arity="1"/> + <name name="atanh" arity="1"/> + <name name="exp" arity="1"/> + <name name="log" arity="1"/> + <name name="log10" arity="1"/> + <name name="pow" arity="2"/> + <name name="sqrt" arity="1"/> + <type variable="X" name_i="7"/> + <type variable="Y" name_i="7"/> <fsummary>Diverse math functions</fsummary> - <type> - <v>X = Y = number()</v> - </type> <desc> <p>A collection of math functions which return floats. Arguments are numbers. </p> </desc> </func> <func> - <name>erf(X) -> float()</name> + <name name="erf" arity="1"/> <fsummary>Error function.</fsummary> - <type> - <v>X = number()</v> - </type> <desc> - <p>Returns the error function of <c>X</c>, where</p> + <p>Returns the error function of <c><anno>X</anno></c>, where</p> <pre> erf(X) = 2/sqrt(pi)*integral from 0 to X of exp(-t*t) dt. </pre> </desc> </func> <func> - <name>erfc(X) -> float()</name> + <name name="erfc" arity="1"/> <fsummary>Another error function</fsummary> - <type> - <v>X = number()</v> - </type> <desc> <p><c>erfc(X)</c> returns <c>1.0 - erf(X)</c>, computed by - methods that avoid cancellation for large <c>X</c>. </p> + methods that avoid cancellation for large <c><anno>X</anno></c>. </p> </desc> </func> </funcs> diff --git a/lib/stdlib/doc/src/notes.xml b/lib/stdlib/doc/src/notes.xml index 2a308cbe09..2ec0d6a60f 100644 --- a/lib/stdlib/doc/src/notes.xml +++ b/lib/stdlib/doc/src/notes.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2004</year><year>2012</year> + <year>2004</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -30,6 +30,281 @@ </header> <p>This document describes the changes made to the STDLIB application.</p> +<section><title>STDLIB 1.19.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> Bugs related to Unicode have been fixed in the + <c>erl_eval</c> module. </p> + <p> + Own Id: OTP-10622 Aux Id: kunagi-351 [262] </p> + </item> + <item> + <p><c>filelib:wildcard("some/relative/path/*.beam", + Path)</c> would fail to match any file. That is, + filelib:wildcard/2 would not work if the first component + of the pattern did not contain any wildcard characters. + (A previous attempt to fix the problem in R15B02 seems to + have made matters worse.)</p> + <p>(Thanks to Samuel Rivas and Tuncer Ayaz.)</p> + <p>There is also an incompatible change to the + <c>Path</c> argument. It is no longer allowed to be a + binary.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-10812</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> The new STDLIB application variable + <c>shell_strings</c> can be used for determining how the + Erlang shell outputs lists of integers. The new function + <c>shell:strings/1</c> toggles the value of the variable. + </p> <p> The control sequence modifier <c>l</c> can be + used for turning off the string recognition of <c>~p</c> + and <c>~P</c>. </p> + <p> + Own Id: OTP-10755</p> + </item> + <item> + <p> Miscellaneous updates due to Unicode support. </p> + <p> + Own Id: OTP-10820</p> + </item> + <item> + <p> Extend <c>~ts</c> to handle binaries with characters + coded in ISO-latin-1 </p> + <p> + Own Id: OTP-10836</p> + </item> + <item> + <p> + The +pc flag to erl can be used to set the range of + characters considered printable. This affects how the + shell and io:format("~tp",...) functionality does + heuristic string detection. More can be read in STDLIB + users guide.</p> + <p> + Own Id: OTP-10884</p> + </item> + </list> + </section> + +</section> + +<section><title>STDLIB 1.19</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Wildcards such as "some/path/*" passed to + <c>filelib:wildcard/2</c> would fail to match any file. + (Thanks to Samuel Rivas for reporting this bug.)</p> + <p> + Own Id: OTP-6874 Aux Id: kunagi-190 [101] </p> + </item> + <item> + <p> + Fixed error handling in proc_lib:start which could hang + if the spawned process died in init.</p> + <p> + Own Id: OTP-9803 Aux Id: kunagi-209 [120] </p> + </item> + <item> + <p> + Allow ** in filelib:wildcard</p> + <p> + Two adjacent * used as a single pattern will match all + files and zero or more directories and subdirectories. + (Thanks to Jos� Valim)</p> + <p> + Own Id: OTP-10431</p> + </item> + <item> + <p> + Add the \gN and \g{N} syntax for back references in + re:replace/3,4 to allow use with numeric replacement + strings. (Thanks to Vance Shipley)</p> + <p> + Own Id: OTP-10455</p> + </item> + <item> + <p> + Export ets:match_pattern/0 type (Thanks to Joseph Wayne + Norton)</p> + <p> + Own Id: OTP-10472</p> + </item> + <item> + <p> + Fix printing the empty binary at depth 1 with ~W (Thanks + to Andrew Thompson)</p> + <p> + Own Id: OTP-10504</p> + </item> + <item> + <p> The type <c>ascii_string()</c> in the <c>base64</c> + module has been corrected. The type + <c>file:file_info()</c> has been cleaned up. The type + <c>file:fd()</c> has been made opaque in the + documentation. </p> + <p> + Own Id: OTP-10624 Aux Id: kunagi-352 [263] </p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> Dets tables are no longer fixed while traversing with + a bound key (when only the objects with the right key are + matched). This optimization affects the functions + <c>match/2</c>, <c>match_object/2</c>, <c>select/2</c>, + <c>match_delete/2</c>, and <c>select_delete/2</c>. </p> + <p> + Own Id: OTP-10097</p> + </item> + <item> + <p> Support for Unicode has been implemented. </p> + <p> + Own Id: OTP-10302</p> + </item> + <item> + <p> The linter now warns for opaque types that are not + exported, as well as for under-specified opaque types. + </p> + <p> + Own Id: OTP-10436</p> + </item> + <item> + <p> The type <c>file:name()</c> has been substituted for + the type <c>file:filename()</c> in the following + functions in the <c>filename</c> module: + <c>absname/2</c>, <c>absname_join/2</c>, <c>join/1,2</c>, + and <c>split/1</c>. </p> + <p> + Own Id: OTP-10474</p> + </item> + <item> + <p> + If a child process fails in its start function, then the + error reason was earlier only reported as an error report + from the error_handler, and supervisor:start_link would + only return <c>{error,shutdown}</c>. This has been + changed so the supervisor will now return + <c>{error,{shutdown,Reason}}</c>, where <c>Reason</c> + identifies the failing child and its error reason. + (Thanks to Tomas Pihl)</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-10490</p> + </item> + <item> + <p>Where necessary a comment stating encoding has been + added to Erlang files. The comment is meant to be removed + in Erlang/OTP R17B when UTF-8 becomes the default + encoding. </p> + <p> + Own Id: OTP-10630</p> + </item> + <item> + <p> The contracts and types of the modules + <c>erl_scan</c> and <c>sys</c> have been corrected and + improved. (Thanks to Kostis Sagonas.) </p> + <p> + Own Id: OTP-10658</p> + </item> + <item> + <p> The Erlang shell now skips the rest of the line when + it encounters an Erlang scanner error. </p> + <p> + Own Id: OTP-10659</p> + </item> + <item> + <p> + Clean up some specs in the proplists module. (Thanks to + Kostis Sagonas.)</p> + <p> + Own Id: OTP-10663</p> + </item> + <item> + <p> Some examples overflowing the width of PDF pages have + been corrected. </p> + <p> + Own Id: OTP-10665</p> + </item> + <item> + <p> + Enable escript to accept emulator arguments when script + file has no shebang. Thanks to Magnus Henoch</p> + <p> + Own Id: OTP-10691</p> + </item> + <item> + <p> + Fix bug in queue:out/1, queue:out_r/1 that makes it + O(N^2) in worst case. Thanks to Aleksandr Erofeev.</p> + <p> + Own Id: OTP-10722</p> + </item> + <item> + <p> There are new functions in the <c>epp</c> module + which read the character encoding from files. See + <c>epp(3)</c> for more information. </p> + <p> + Own Id: OTP-10742 Aux Id: OTP-10302 </p> + </item> + <item> + <p> The functions in <c>io_lib</c> have been adjusted for + Unicode. The existing functions <c>write_string()</c> and + so on now take Unicode strings, while the old behavior + has been taken over by new functions + <c>write_latin1_string()</c> and so on. There are also + new functions to write Unicode strings as Latin-1 + strings, mainly targetted towards the Erlang pretty + printer (<c>erl_pp</c>). </p> + <p> + Own Id: OTP-10745 Aux Id: OTP-10302 </p> + </item> + <item> + <p> The new functions <c>proc_lib:format/2</c> and + <c>erl_parse:abstract/2</c> accept an encoding as second + argument. </p> + <p> + Own Id: OTP-10749 Aux Id: OTP-10302 </p> + </item> + <item> + <p> + Increased potential concurrency in ETS for + <c>write_concurrency</c> option. The number of internal + table locks has increased from 16 to 64. This makes it + four times less likely that two concurrent processes + writing to the same table would collide and thereby + serialized. The cost is an increased constant memory + footprint for tables using write_concurrency. The memory + consumption per inserted record is not affected. The + increased footprint can be particularly large if + <c>write_concurrency</c> is combined with + <c>read_concurrency</c>.</p> + <p> + Own Id: OTP-10787</p> + </item> + </list> + </section> + +</section> + <section><title>STDLIB 1.18.3</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/stdlib/doc/src/proc_lib.xml b/lib/stdlib/doc/src/proc_lib.xml index abc17c4a91..b597074044 100644 --- a/lib/stdlib/doc/src/proc_lib.xml +++ b/lib/stdlib/doc/src/proc_lib.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2011</year> + <year>1996</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -209,6 +209,13 @@ init(Parent) -> <name name="format" arity="1"/> <fsummary>Format a crash report.</fsummary> <desc> + <p>Equivalent to <c>format(<anno>CrashReport</anno>, latin1)</c>.</p> + </desc> + </func> + <func> + <name name="format" arity="2"/> + <fsummary>Format a crash report.</fsummary> + <desc> <p>This function can be used by a user defined event handler to format a crash report. The crash report is sent using <c>error_logger:error_report(crash_report, <anno>CrashReport</anno>)</c>. diff --git a/lib/stdlib/doc/src/proplists.xml b/lib/stdlib/doc/src/proplists.xml index 225c5e97eb..a0063a58d3 100644 --- a/lib/stdlib/doc/src/proplists.xml +++ b/lib/stdlib/doc/src/proplists.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2002</year><year>2011</year> + <year>2002</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -70,7 +70,7 @@ <fsummary></fsummary> <desc> <p>Minimizes the representation of all entries in the list. This is - equivalent to <c><![CDATA[[property(P) || P <- List]]]></c>.</p> + equivalent to <c><![CDATA[[property(P) || P <- ListIn]]]></c>.</p> <p>See also: <c>property/1</c>, <c>unfold/1</c>.</p> </desc> </func> @@ -88,11 +88,11 @@ <desc> <p>Expands particular properties to corresponding sets of properties (or other terms). For each pair <c>{<anno>Property</anno>, <anno>Expansion</anno>}</c> in <c><anno>Expansions</anno></c>, if <c>E</c> is - the first entry in <c><anno>List</anno></c> with the same key as + the first entry in <c><anno>ListIn</anno></c> with the same key as <c><anno>Property</anno></c>, and <c>E</c> and <c><anno>Property</anno></c> have equivalent normal forms, then <c>E</c> is replaced with the terms in <c><anno>Expansion</anno></c>, and any following entries with - the same key are deleted from <c><anno>List</anno></c>.</p> + the same key are deleted from <c><anno>ListIn</anno></c>.</p> <p>For example, the following expressions all return <c>[fie, bar, baz, fum]</c>:</p> <code type="none"> expand([{foo, [bar, baz]}], @@ -198,7 +198,7 @@ <name name="normalize" arity="2"/> <fsummary></fsummary> <desc> - <p>Passes <c><anno>List</anno></c> through a sequence of + <p>Passes <c><anno>ListIn</anno></c> through a sequence of substitution/expansion stages. For an <c>aliases</c> operation, the function <c>substitute_aliases/2</c> is applied using the given list of aliases; for a <c>negations</c> operation, @@ -221,9 +221,9 @@ <fsummary></fsummary> <desc> <p>Creates a normal form (minimal) representation of a property. If - <c><anno>Property</anno></c> is <c>{Key, true}</c> where <c>Key</c> is - an atom, this returns <c>Key</c>, otherwise the whole term - <c><anno>Property</anno></c> is returned.</p> + <c><anno>PropertyIn</anno></c> is <c>{Key, true}</c> where + <c>Key</c> is an atom, this returns <c>Key</c>, otherwise + the whole term <c><anno>PropertyIn</anno></c> is returned.</p> <p>See also: <c>property/2</c>.</p> </desc> </func> @@ -260,7 +260,7 @@ <fsummary></fsummary> <desc> <p>Substitutes keys of properties. For each entry in - <c><anno>List</anno></c>, if it is associated with some key <c>K1</c> + <c><anno>ListIn</anno></c>, if it is associated with some key <c>K1</c> such that <c>{K1, K2}</c> occurs in <c><anno>Aliases</anno></c>, the key of the entry is changed to <c>K2</c>. If the same <c>K1</c> occurs more than once in <c><anno>Aliases</anno></c>, only @@ -278,13 +278,13 @@ <desc> <p>Substitutes keys of boolean-valued properties and simultaneously negates their values. For each entry in - <c><anno>List</anno></c>, if it is associated with some key <c>K1</c> + <c><anno>ListIn</anno></c>, if it is associated with some key <c>K1</c> such that <c>{K1, K2}</c> occurs in <c><anno>Negations</anno></c>, then if the entry was <c>{K1, true}</c> it will be replaced with <c>{K2, false}</c>, otherwise it will be replaced with <c>{K2, true}</c>, thus changing the name of the option and simultaneously negating the value given by - <c>get_bool(List)</c>. If the same <c>K1</c> occurs more + <c>get_bool(ListIn)</c>. If the same <c>K1</c> occurs more than once in <c><anno>Negations</anno></c>, only the first occurrence is used.</p> <p>Example: <c>substitute_negations([{no_foo, foo}], L)</c> @@ -300,7 +300,7 @@ <name name="unfold" arity="1"/> <fsummary></fsummary> <desc> - <p>Unfolds all occurrences of atoms in <c><anno>List</anno></c> to tuples + <p>Unfolds all occurrences of atoms in <c><anno>ListIn</anno></c> to tuples <c>{Atom, true}</c>.</p> </desc> </func> diff --git a/lib/stdlib/doc/src/re.xml b/lib/stdlib/doc/src/re.xml index 6d5336796c..71a6e34513 100644 --- a/lib/stdlib/doc/src/re.xml +++ b/lib/stdlib/doc/src/re.xml @@ -5,7 +5,7 @@ <header> <copyright> <year>2007</year> - <year>2011</year> + <year>2012</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> @@ -78,28 +78,15 @@ </datatypes> <funcs> <func> - <name>compile(Regexp) -> {ok, MP} | {error, ErrSpec}</name> + <name name="compile" arity="1"/> <fsummary>Compile a regular expression into a match program</fsummary> - <type> - <v>Regexp = iodata()</v> - </type> <desc> - <p>The same as <c>compile(Regexp,[])</c></p> + <p>The same as <c>compile(<anno>Regexp</anno>,[])</c></p> </desc> </func> <func> - <name>compile(Regexp,Options) -> {ok, MP} | {error, ErrSpec}</name> + <name name="compile" arity="2"/> <fsummary>Compile a regular expression into a match program</fsummary> - <type> - <v>Regexp = iodata() | <seealso marker="unicode#type-charlist">io:charlist()</seealso></v> - <v>Options = [ Option ]</v> - <v>Option = <seealso marker="#type-compile_option">compile_option()</seealso></v> - <v>NLSpec = <seealso marker="#type-nl_spec">nl_spec()</seealso></v> - <v>MP = <seealso marker="#type-mp">mp()</seealso></v> - <v>ErrSpec = {ErrString, Position}</v> - <v>ErrString = string()</v> - <v>Position = non_neg_integer()</v> - </type> <desc> <p>This function compiles a regular expression with the syntax described below into an internal format to be used later as a @@ -109,12 +96,12 @@ subjects during the program's lifetime. Compiling once and executing many times is far more efficient than compiling each time one wants to match.</p> - <p>When the unicode option is given, the regular expression should be given as a valid unicode <c>charlist()</c>, otherwise as any valid <c>iodata()</c>.</p> + <p>When the unicode option is given, the regular expression should be given as a valid Unicode <c>charlist()</c>, otherwise as any valid <c>iodata()</c>.</p> <p><marker id="compile_options"/>The options have the following meanings:</p> <taglist> <tag><c>unicode</c></tag> - <item>The regular expression is given as a unicode <c>charlist()</c> and the resulting regular expression code is to be run against a valid unicode <c>charlist()</c> subject.</item> + <item>The regular expression is given as a Unicode <c>charlist()</c> and the resulting regular expression code is to be run against a valid Unicode <c>charlist()</c> subject.</item> <tag><c>anchored</c></tag> <item>The pattern is forced to be "anchored", that is, it is constrained to match only at the first matching point in the string that is being searched (the "subject string"). This effect can also be achieved by appropriate constructs in the pattern itself.</item> <tag><c>caseless</c></tag> @@ -165,44 +152,23 @@ This option makes it possible to include comments inside complicated patterns. N </func> <func> - <name>run(Subject,RE) -> {match, Captured} | nomatch</name> + <name name="run" arity="2"/> <fsummary>Match a subject against regular expression and capture subpatterns</fsummary> - <type> - <v>Subject = iodata() | <seealso marker="unicode#type-charlist">io:charlist()</seealso></v> - <v>RE = <seealso marker="#type-mp">mp()</seealso> | iodata()</v> - <v>Captured = [ CaptureData ]</v> - <v>CaptureData = {integer(),integer()}</v> - </type> <desc> - <p>The same as <c>run(Subject,RE,[])</c>.</p> + <p>The same as <c>run(<anno>Subject</anno>,<anno>RE</anno>,[])</c>.</p> </desc> </func> <func> - <name>run(Subject,RE,Options) -> {match, Captured} | match | nomatch</name> + <name name="run" arity="3"/> + <type_desc variable="CompileOpt">See <seealso marker="#compile_options">compile/2</seealso> above.</type_desc> <fsummary>Match a subject against regular expression and capture subpatterns</fsummary> - <type> - <v>Subject = iodata() | <seealso marker="unicode#type-charlist">io:charlist()</seealso></v> - <v>RE = <seealso marker="#type-mp">mp()</seealso> | iodata() | <seealso marker="unicode#type-charlist">io:charlist()</seealso></v> - <v>Options = [ Option ]</v> - <v>Option = anchored | global | notbol | noteol | notempty | {offset, integer() >= 0} | {newline, NLSpec} | bsr_anycrlf | bsr_unicode | {capture, ValueSpec} | {capture, ValueSpec, Type} | CompileOpt</v> - <v>Type = index | list | binary</v> - <v>ValueSpec = all | all_but_first | first | none | ValueList</v> - <v>ValueList = [ ValueID ]</v> - <v>ValueID = integer() | string() | atom()</v> - <v>CompileOpt = <seealso marker="#type-compile_option">compile_option()</seealso></v> - <d>See <seealso marker="#compile_options">compile/2</seealso> above.</d> - <v>NLSpec = <seealso marker="#type-nl_spec">nl_spec()</seealso></v> - <v>Captured = [ CaptureData ] | [ [ CaptureData ] ... ]</v> - <v>CaptureData = {integer(),integer()} | ListConversionData | binary()</v> - <v>ListConversionData = string() | {error, string(), binary()} | {incomplete, string(), binary()}</v> - </type> <desc> <p>Executes a regexp matching, returning <c>match/{match, - Captured}</c> or <c>nomatch</c>. The regular expression can be + <anno>Captured</anno>}</c> or <c>nomatch</c>. The regular expression can be given either as <c>iodata()</c> in which case it is automatically compiled (as by <c>re:compile/2</c>) and executed, - or as a pre compiled <c>mp()</c> in which case it is executed + or as a pre-compiled <c>mp()</c> in which case it is executed against the subject directly.</p> <p>When compilation is involved, the exception <c>badarg</c> is @@ -214,23 +180,23 @@ This option makes it possible to include comments inside complicated patterns. N list can only contain the options <c>anchored</c>, <c>global</c>, <c>notbol</c>, <c>noteol</c>, <c>notempty</c>, <c>{offset, integer() >= 0}</c>, <c>{newline, - NLSpec}</c> and <c>{capture, ValueSpec}/{capture, ValueSpec, - Type}</c>. Otherwise all options valid for the + <anno>NLSpec</anno>}</c> and <c>{capture, <anno>ValueSpec</anno>}/{capture, <anno>ValueSpec</anno>, + <anno>Type</anno>}</c>. Otherwise all options valid for the <c>re:compile/2</c> function are allowed as well. Options allowed both for compilation and execution of a match, namely - <c>anchored</c> and <c>{newline, NLSpec}</c>, will affect both + <c>anchored</c> and <c>{newline, <anno>NLSpec</anno>}</c>, will affect both the compilation and execution if present together with a non pre-compiled regular expression.</p> <p>If the regular expression was previously compiled with the - option <c>unicode</c>, the <c>Subject</c> should be provided as + option <c>unicode</c>, the <c><anno>Subject</anno></c> should be provided as a valid Unicode <c>charlist()</c>, otherwise any <c>iodata()</c> will do. If compilation is involved and the option - <c>unicode</c> is given, both the <c>Subject</c> and the regular + <c>unicode</c> is given, both the <c><anno>Subject</anno></c> and the regular expression should be given as valid Unicode <c>charlists()</c>.</p> - <p>The <c>{capture, ValueSpec}/{capture, ValueSpec, Type}</c> + <p>The <c>{capture, <anno>ValueSpec</anno>}/{capture, <anno>ValueSpec</anno>, <anno>Type</anno>}</c> defines what to return from the function upon successful matching. The <c>capture</c> tuple may contain both a value specification telling which of the captured @@ -244,9 +210,9 @@ This option makes it possible to include comments inside complicated patterns. N at all is to be done (<c>{capture, none}</c>), the function will return the single atom <c>match</c> upon successful matching, otherwise the tuple - <c>{match, ValueList}</c> is returned. Disabling capturing can + <c>{match, <anno>ValueList</anno>}</c> is returned. Disabling capturing can be done either by specifying <c>none</c> or an empty list as - <c>ValueSpec</c>.</p> + <c><anno>ValueSpec</anno></c>.</p> <p>The options relevant for execution are:</p> @@ -266,7 +232,7 @@ This option makes it possible to include comments inside complicated patterns. N Perl). Each match is returned as a separate <c>list()</c> containing the specific match as well as any matching subexpressions (or as specified by the <c>capture - option</c>). The <c>Captured</c> part of the return value will + option</c>). The <c><anno>Captured</anno></c> part of the return value will hence be a <c>list()</c> of <c>list()</c>s when this option is given.</p> @@ -362,7 +328,7 @@ This option makes it possible to include comments inside complicated patterns. N subject string. The offset is zero-based, so that the default is <c>{offset,0}</c> (all of the subject string).</item> - <tag><c>{newline, NLSpec}</c></tag> + <tag><c>{newline, <anno>NLSpec</anno>}</c></tag> <item> <p>Override the default definition of a newline in the subject string, which is LF (ASCII 10) in Erlang.</p> <taglist> @@ -383,7 +349,7 @@ This option makes it possible to include comments inside complicated patterns. N <tag><c>bsr_unicode</c></tag> <item>Specifies specifically that \R is to match all the Unicode newline characters (including crlf etc, the default).(overrides compilation option)</item> - <tag><c>{capture, ValueSpec}</c>/<c>{capture, ValueSpec, Type}</c></tag> + <tag><c>{capture, <anno>ValueSpec</anno>}</c>/<c>{capture, <anno>ValueSpec</anno>, <anno>Type</anno>}</c></tag> <item> <p>Specifies which captured substrings are returned and in what @@ -392,7 +358,7 @@ This option makes it possible to include comments inside complicated patterns. N substring as well as all capturing subpatterns (all of the pattern is automatically captured). The default return type is (zero-based) indexes of the captured parts of the string, given as - <c>{Offset,Length}</c> pairs (the <c>index</c> <c>Type</c> of + <c>{Offset,Length}</c> pairs (the <c>index</c> <c><anno>Type</anno></c> of capturing).</p> <p>As an example of the default behavior, the following call:</p> @@ -422,8 +388,8 @@ This option makes it possible to include comments inside complicated patterns. N <p>The capture tuple is built up as follows:</p> <taglist> - <tag><c>ValueSpec</c></tag> - <item><p>Specifies which captured (sub)patterns are to be returned. The ValueSpec can either be an atom describing a predefined set of return values, or a list containing either the indexes or the names of specific subpatterns to return.</p> + <tag><c><anno>ValueSpec</anno></c></tag> + <item><p>Specifies which captured (sub)patterns are to be returned. The <c><anno>ValueSpec</anno></c> can either be an atom describing a predefined set of return values, or a list containing either the indexes or the names of specific subpatterns to return.</p> <p>The predefined sets of subpatterns are:</p> <taglist> <tag><c>all</c></tag> @@ -437,7 +403,7 @@ This option makes it possible to include comments inside complicated patterns. N </taglist> <p>The value list is a list of indexes for the subpatterns to return, where index 0 is for all of the pattern, and 1 is for the first explicit capturing subpattern in the regular expression, and so forth. When using named captured subpatterns (see below) in the regular expression, one can use <c>atom()</c>s or <c>string()</c>s to specify the subpatterns to be returned. For example, consider the regular expression:</p> <code> ".*(abcd).*"</code> - <p>matched against the string ""ABCabcdABC", capturing only the "abcd" part (the first explicit subpattern):</p> + <p>matched against the string "ABCabcdABC", capturing only the "abcd" part (the first explicit subpattern):</p> <code> re:run("ABCabcdABC",".*(abcd).*",[{capture,[1]}]).</code> <p>The call will yield the following result:</p> <code> {match,[{3,4}]}</code> @@ -460,8 +426,8 @@ This option makes it possible to include comments inside complicated patterns. N or list respectively.</p> </item> - <tag><c>Type</c></tag> - <item><p>Optionally specifies how captured substrings are to be returned. If omitted, the default of <c>index</c> is used. The <c>Type</c> can be one of the following:</p> + <tag><c><anno>Type</anno></c></tag> + <item><p>Optionally specifies how captured substrings are to be returned. If omitted, the default of <c>index</c> is used. The <c><anno>Type</anno></c> can be one of the following:</p> <taglist> <tag><c>index</c></tag> <item>Return captured substrings as pairs of byte indexes into the subject string and length of the matching string in the subject (as if the subject string was flattened with <c>iolist_to_binary/1</c> or <c>unicode:characters_to_binary/2</c> prior to matching). Note that the <c>unicode</c> option results in <em>byte-oriented</em> indexes in a (possibly virtual) <em>UTF-8 encoded</em> binary. A byte index tuple <c>{0,2}</c> might therefore represent one or two characters when <c>unicode</c> is in effect. This might seem counter-intuitive, but has been deemed the most effective and useful way to way to do it. To return lists instead might result in simpler code if that is desired. This return type is the default.</item> @@ -478,7 +444,7 @@ This option makes it possible to include comments inside complicated patterns. N <code> "ABCabcdABC"</code> <p>the subpattern at index 2 won't match, as "abdd" is not present in the string, but the complete pattern matches (due to the alternative <c>a(..d)</c>. The subpattern at index 2 is therefore unassigned and the default return value will be:</p> <code> {match,[{0,10},{3,4},{-1,0},{4,3}]}</code> - <p>Setting the capture <c>Type</c> to <c>binary</c> would give the following:</p> + <p>Setting the capture <c><anno>Type</anno></c> to <c>binary</c> would give the following:</p> <code> {match,[<<"ABCabcdABC">>,<<"abcd">>,<<>>,<<"bcd">>]}</code> <p>where the empty binary (<c><<>></c>) represents the unassigned subpattern. In the <c>binary</c> case, some information about the matching is therefore lost, the <c><<>></c> might just as well be an empty string captured.</p> <p>If differentiation between empty matches and non existing subpatterns is necessary, use the <c>type</c> <c>index</c> @@ -512,7 +478,7 @@ This option makes it possible to include comments inside complicated patterns. N <p>Replaces the matched part of the <c><anno>Subject</anno></c> string with the contents of <c><anno>Replacement</anno></c>.</p> <p>The permissible options are the same as for <c>re:run/3</c>, except that the <c>capture</c> option is not allowed. Instead a <c>{return, <anno>ReturnType</anno>}</c> is present. The default return type is <c>iodata</c>, constructed in a - way to minimize copying. The <c>iodata</c> result can be used directly in many i/o-operations. If a flat <c>list()</c> is + way to minimize copying. The <c>iodata</c> result can be used directly in many I/O-operations. If a flat <c>list()</c> is desired, specify <c>{return, list}</c> and if a binary is preferred, specify <c>{return, binary}</c>.</p> <p>As in the <c>re:run/3</c> function, an <c>mp()</c> compiled @@ -524,8 +490,8 @@ This option makes it possible to include comments inside complicated patterns. N <p>The replacement string can contain the special character <c>&</c>, which inserts the whole matching expression in the - result, and the special sequence <c>\</c>N (where N is an - integer > 0), resulting in the subexpression number N will be + result, and the special sequence <c>\</c>N (where N is an integer > 0), + <c>\g</c>N or <c>\g{</c>N<c>}</c> resulting in the subexpression number N will be inserted in the result. If no subexpression with that number is generated by the regular expression, nothing is inserted.</p> <p>To insert an <c>&</c> or <c>\</c> in the result, precede it diff --git a/lib/stdlib/doc/src/shell.xml b/lib/stdlib/doc/src/shell.xml index bc2120c37d..7f251c863e 100644 --- a/lib/stdlib/doc/src/shell.xml +++ b/lib/stdlib/doc/src/shell.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2011</year> + <year>1996</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -781,7 +781,7 @@ loop(N) -> </desc> </func> <func> - <name>catch_exception(Bool) -> Bool</name> + <name>catch_exception(Bool) -> boolean()</name> <fsummary>Sets the exception handling of the shell</fsummary> <type> <v>Bool = boolean()</v> @@ -801,8 +801,8 @@ loop(N) -> <name name="prompt_func" arity="1"/> <fsummary>Sets the shell prompt</fsummary> <desc> - <p>Sets the shell prompt function to <c>PromptFunc</c>. The - previous prompt function is returned.</p> + <p>Sets the shell prompt function to <c><anno>PromptFunc</anno></c>. + The previous prompt function is returned.</p> </desc> </func> <func> @@ -827,6 +827,20 @@ loop(N) -> is meant to be called from the shell.</p> </desc> </func> + <func> + <name name="strings" arity="1"/> + <fsummary>Sets the shell's string recognition flag.</fsummary> + <desc> + <p>Sets pretty printing of lists to <c><anno>Strings</anno></c>. + The previous value of the flag is returned.</p> + <p>The flag can also be set by the STDLIB application variable + <c>shell_strings</c>. The default is + <c>true</c> which means that lists of integers will be + printed using the string syntax, when possible. The value + <c>false</c> means that no lists will be printed using the + string syntax.</p> + </desc> + </func> </funcs> </erlref> diff --git a/lib/stdlib/doc/src/stdlib_app.xml b/lib/stdlib/doc/src/stdlib_app.xml index a615c1bf88..2391bb6f03 100644 --- a/lib/stdlib/doc/src/stdlib_app.xml +++ b/lib/stdlib/doc/src/stdlib_app.xml @@ -4,7 +4,7 @@ <appref> <header> <copyright> - <year>2005</year><year>2010</year> + <year>2005</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -51,7 +51,7 @@ <p>This parameter can be used to run the Erlang shell in restricted mode.</p> </item> - <tag><c>shell_catch_exception = bool()</c></tag> + <tag><c>shell_catch_exception = boolean()</c></tag> <item> <p>This parameter can be used to set the exception handling of the Erlang shell's evaluator process.</p> @@ -76,6 +76,11 @@ <p>This parameter can be used to determine how many results are saved by the Erlang shell.</p> </item> + <tag><c>shell_strings = boolean()</c></tag> + <item> + <p>This parameter can be used to determine how the Erlang + shell outputs lists of integers.</p> + </item> </taglist> </section> diff --git a/lib/stdlib/doc/src/string.xml b/lib/stdlib/doc/src/string.xml index 48867ffe72..549c871aed 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>2011</year> + <year>1996</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -255,18 +255,12 @@ sub_string("Hello World", 4, 8). </desc> </func> <func> - <name>to_float(String) -> {Float,Rest} | {error,Reason} </name> + <name name="to_float" arity="1"/> <fsummary>Returns a float whose text representation is the integers (ASCII values) in String.</fsummary> - <type> - <v>String = string()</v> - <v>Float = float()</v> - <v>Rest = string()</v> - <v>Reason = no_float | not_a_list</v> - </type> <desc> - <p>Argument <c>String</c> is expected to start with a valid text + <p>Argument <c><anno>String</anno></c> is expected to start with a valid text represented float (the digits being ASCII values). Remaining characters - in the string after the float are returned in <c>Rest</c>.</p> + in the string after the float are returned in <c><anno>Rest</anno></c>.</p> <p>Example:</p> <code type="none"> > {F1,Fs} = string:to_float("1.0-1.0e-1"), @@ -280,18 +274,12 @@ sub_string("Hello World", 4, 8). </desc> </func> <func> - <name>to_integer(String) -> {Int,Rest} | {error,Reason} </name> + <name name="to_integer" arity="1"/> <fsummary>Returns an integer whose text representation is the integers (ASCII values) in String.</fsummary> - <type> - <v>String = string()</v> - <v>Int = integer()</v> - <v>Rest = string()</v> - <v>Reason = no_integer | not_a_list</v> - </type> <desc> - <p>Argument <c>String</c> is expected to start with a valid text + <p>Argument <c><anno>String</anno></c> is expected to start with a valid text represented integer (the digits being ASCII values). Remaining characters - in the string after the integer are returned in <c>Rest</c>.</p> + in the string after the integer are returned in <c><anno>Rest</anno></c>.</p> <p>Example:</p> <code type="none"> > {I1,Is} = string:to_integer("33+22"), diff --git a/lib/stdlib/doc/src/supervisor.xml b/lib/stdlib/doc/src/supervisor.xml index f9a5e245b4..9021d02ade 100644 --- a/lib/stdlib/doc/src/supervisor.xml +++ b/lib/stdlib/doc/src/supervisor.xml @@ -294,10 +294,10 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules} is a term with information about the error, and the supervisor terminates with reason <c>Term</c>.</p> <p>If any child process start function fails or returns an error - tuple or an erroneous value, the function returns - <c>{error,shutdown}</c> and the supervisor terminates all - started child processes and then itself with reason - <c>shutdown</c>.</p> + tuple or an erroneous value, the supervisor will first terminate + all already started child processes with reason <c>shutdown</c> + and then terminate itself and return + <c>{error, {shutdown, Reason}}</c>.</p> </desc> </func> <func> diff --git a/lib/stdlib/doc/src/unicode.xml b/lib/stdlib/doc/src/unicode.xml index 1001ebbae4..deba6adb11 100644 --- a/lib/stdlib/doc/src/unicode.xml +++ b/lib/stdlib/doc/src/unicode.xml @@ -5,7 +5,7 @@ <header> <copyright> <year>1996</year> - <year>2011</year> + <year>2013</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> @@ -32,9 +32,9 @@ <module>unicode</module> <modulesummary>Functions for converting Unicode characters</modulesummary> <description> - <p>This module contains functions for converting between different character representations. Basically it converts between iso-latin-1 characters and Unicode ditto, but it can also convert between different Unicode encodings (like UTF-8, UTF-16 and UTF-32).</p> + <p>This module contains functions for converting between different character representations. Basically it converts between ISO-latin-1 characters and Unicode ditto, but it can also convert between different Unicode encodings (like UTF-8, UTF-16 and UTF-32).</p> <p>The default Unicode encoding in Erlang is in binaries UTF-8, which is also the format in which built in functions and libraries in OTP expect to find binary Unicode data. In lists, Unicode data is encoded as integers, each integer representing one character and encoded simply as the Unicode codepoint for the character.</p> - <p>Other Unicode encodings than integers representing codepoints or UTF-8 in binaries are referred to as "external encodings". The iso-latin-1 encoding is in binaries and lists referred to as latin1-encoding.</p> + <p>Other Unicode encodings than integers representing codepoints or UTF-8 in binaries are referred to as "external encodings". The ISO-latin-1 encoding is in binaries and lists referred to as latin1-encoding.</p> <p>It is recommended to only use external encodings for communication with external entities where this is required. When working inside the Erlang/OTP environment, it is recommended to keep binaries in UTF-8 when representing Unicode characters. Latin1 encoding is supported both for backward compatibility and for communication with external entities not supporting Unicode character sets.</p> </description> @@ -48,13 +48,7 @@ <datatype> <name name="unicode_binary"/> <desc> - <p>A binary() with characters encoded in the UTF-8 coding standard.</p> - </desc> - </datatype> - <datatype> - <name name="unicode_char"/> - <desc> - <p>An integer() representing a valid unicode codepoint.</p> + <p>A <c>binary()</c> with characters encoded in the UTF-8 coding standard.</p> </desc> </datatype> <datatype> @@ -62,9 +56,6 @@ </datatype> <datatype> <name name="charlist"/> - <desc> - <p>A unicode_binary is allowed as the tail of the list.</p> - </desc> </datatype> <datatype> <name name="external_unicode_binary"/> @@ -78,14 +69,10 @@ </datatype> <datatype> <name name="external_charlist"/> - <desc> - <p>An <c>external_unicode_binary()</c> is allowed as the tail - of the list.</p> - </desc> </datatype> <datatype> <name name="latin1_binary"/> - <desc><p>A <c>binary()</c> with characters coded in iso-latin-1.</p> + <desc><p>A <c>binary()</c> with characters coded in ISO-latin-1.</p> </desc> </datatype> <datatype> @@ -96,11 +83,12 @@ </datatype> <datatype> <name name="latin1_chardata"/> + <desc><p>The same as <c>iodata()</c>.</p> + </desc> </datatype> <datatype> <name name="latin1_charlist"/> - <desc><p>A <c>latin1_binary()</c> is allowed as the tail of - the list.</p> + <desc><p>The same as <c>iolist()</c>.</p> </desc> </datatype> </datatypes> @@ -110,7 +98,9 @@ <name name="bom_to_encoding" arity="1"/> <fsummary>Identify UTF byte order marks in a binary.</fsummary> <type name="endian"/> - <type_desc variable="Bin">A binary() of byte_size 4 or more.</type_desc> + <type_desc variable="Bin"> + A <c>binary()</c> such that <c>byte_size(<anno>Bin</anno>) >= 4</c>. + </type_desc> <desc> <p>Check for a UTF byte order mark (BOM) in the beginning of a @@ -126,50 +116,40 @@ <name name="characters_to_list" arity="1"/> <fsummary>Convert a collection of characters to list of Unicode characters</fsummary> <desc> - <p>Same as characters_to_list(<anno>Data</anno>,unicode).</p> + <p>Same as <c>characters_to_list(<anno>Data</anno>, unicode)</c>.</p> </desc> </func> <func> - <name>characters_to_list(Data, InEncoding) -> Result</name> + <name name="characters_to_list" arity="2"/> <fsummary>Convert a collection of characters to list of Unicode characters</fsummary> - <type> - <v>Data = <seealso marker="#type-latin1_chardata">latin1_chardata()</seealso> - | <seealso marker="#type-chardata">chardata()</seealso> - | <seealso marker="#type-external_chardata">external_chardata()</seealso></v> - <v>Result = list() | {error, list(), RestData} | {incomplete, list(), binary()}</v> - <v>RestData = <seealso marker="#type-latin1_chardata">latin1_chardata()</seealso> - | <seealso marker="#type-chardata">chardata()</seealso> - | <seealso marker="#type-external_chardata">external_chardata()</seealso></v> - <v>InEncoding = <seealso marker="#type-encoding">encoding()</seealso></v> - </type> <desc> - <p>This function converts a possibly deep list of integers and - binaries into a list of integers representing unicode + <p>Converts a possibly deep list of integers and + binaries into a list of integers representing Unicode characters. The binaries in the input may have characters encoded as latin1 (0 - 255, one character per byte), in which - case the <c>InEncoding</c> parameter should be given as + case the <c><anno>InEncoding</anno></c> parameter should be given as <c>latin1</c>, or have characters encoded as one of the - UTF-encodings, which is given as the <c>InEncoding</c> - parameter. Only when the <c>InEncoding</c> is one of the UTF + UTF-encodings, which is given as the <c><anno>InEncoding</anno></c> + parameter. Only when the <c><anno>InEncoding</anno></c> is one of the UTF encodings, integers in the list are allowed to be grater than 255.</p> - <p>If <c>InEncoding</c> is <c>latin1</c>, the <c>Data</c> parameter + <p>If <c><anno>InEncoding</anno></c> is <c>latin1</c>, the <c><anno>Data</anno></c> parameter corresponds to the <c>iodata()</c> type, but for <c>unicode</c>, - the <c>Data</c> parameter can contain integers greater than 255 - (unicode characters beyond the iso-latin-1 range), which would + the <c><anno>Data</anno></c> parameter can contain integers greater than 255 + (Unicode characters beyond the ISO-latin-1 range), which would make it invalid as <c>iodata()</c>.</p> <p>The purpose of the function is mainly to be able to convert - combinations of unicode characters into a pure unicode + combinations of Unicode characters into a pure Unicode string in list representation for further processing. For writing the data to an external entity, the reverse function <seealso - marker="#characters_to_binary/3">characters_to_binary/3</seealso> + marker="#characters_to_binary/3"><c>characters_to_binary/3</c></seealso> comes in handy.</p> - <p>The option <c>unicode</c> is an alias for <c>utf8</c>, as this is the + <p>The option <c>unicode</c> is an alias for <c>utf8</c>, as this is the preferred encoding for Unicode characters in binaries. <c>utf16</c> is an alias for <c>{utf16,big}</c> and <c>utf32</c> is an alias for <c>{utf32,big}</c>. The <c>big</c> @@ -177,7 +157,7 @@ encoding.</p> <p>If for some reason, the data cannot be converted, either - because of illegal unicode/latin1 characters in the list, or + because of illegal Unicode/latin1 characters in the list, or because of invalid UTF encoding in any binaries, an error tuple is returned. The error tuple contains the tag <c>error</c>, a list representing the characters that could be @@ -186,43 +166,43 @@ last part is mostly for debugging as it still constitutes a possibly deep and/or mixed list, not necessarily of the same depth as the original data. The error occurs when traversing the - list and whatever's left to decode is simply returned as is.</p> + list and whatever is left to decode is simply returned as is.</p> - <p>However, if the input <c>Data</c> is a pure binary, the third + <p>However, if the input <c><anno>Data</anno></c> is a pure binary, the third part of the error tuple is guaranteed to be a binary as well.</p> <p>Errors occur for the following reasons:</p> <list type="bulleted"> - <item>Integers out of range - If <c>InEncoding</c> is + <item>Integers out of range - If <c><anno>InEncoding</anno></c> is <c>latin1</c>, an error occurs whenever an integer greater - than 255 is found in the lists. If <c>InEncoding</c> is + than 255 is found in the lists. If <c><anno>InEncoding</anno></c> is of a Unicode type, an error occurs whenever an integer <list type="bulleted"> <item>greater than <c>16#10FFFF</c> - (the maximum unicode character),</item> + (the maximum Unicode character),</item> <item>in the range <c>16#D800</c> to <c>16#DFFF</c> (invalid range reserved for UTF-16 surrogate pairs)</item> </list> is found. </item> - <item>UTF encoding incorrect - If <c>InEncoding</c> is + <item>UTF encoding incorrect - If <c><anno>InEncoding</anno></c> is one of the UTF types, the bytes in any binaries have to be valid in that encoding. Errors can occur for various reasons, including "pure" decoding errors (like the upper bits of the bytes being wrong), the bytes are decoded to a too large number, the bytes are decoded to a code-point in the - invalid unicode - range or encoding is "overlong", meaning that a + invalid Unicode + range, or encoding is "overlong", meaning that a number should have been encoded in fewer bytes. The case of a truncated UTF is handled specially, see the paragraph about incomplete binaries below. If - <c>InEncoding</c> is <c>latin1</c>, binaries are always valid + <c><anno>InEncoding</anno></c> is <c>latin1</c>, binaries are always valid as long as they contain whole bytes, - as each byte falls into the valid iso-latin-1 range.</item> + as each byte falls into the valid ISO-latin-1 range.</item> </list> @@ -238,7 +218,7 @@ the first part of a (so far) valid UTF character.</p> <p>If one UTF characters is split over two consecutive - binaries in the <c>Data</c>, the conversion succeeds. This means + binaries in the <c><anno>Data</anno></c>, the conversion succeeds. This means that a character can be decoded from a range of binaries as long as the whole range is given as input without errors occurring. Example:</p> @@ -260,7 +240,7 @@ ever be decoded.</p> <p>If any parameters are of the wrong type, the list structure - is invalid (a number as tail) or the binaries does not contain + is invalid (a number as tail) or the binaries do not contain whole bytes (bit-strings), a <c>badarg</c> exception is thrown.</p> @@ -268,38 +248,27 @@ </func> <func> <name name="characters_to_binary" arity="1"/> - <fsummary>Convert a collection of characters to an UTF-8 binary</fsummary> + <fsummary>Convert a collection of characters to a UTF-8 binary</fsummary> <desc> - <p>Same as characters_to_binary(Data, unicode, unicode).</p> + <p>Same as <c>characters_to_binary(<anno>Data</anno>, unicode, unicode)</c>.</p> </desc> </func> <func> - <name>characters_to_binary(Data,InEncoding) -> Result</name> - <fsummary>Convert a collection of characters to an UTF-8 binary</fsummary> + <name name="characters_to_binary" arity="2"/> + <fsummary>Convert a collection of characters to a UTF-8 binary</fsummary> - <type> - <v>Data = <seealso marker="#type-latin1_chardata">latin1_chardata()</seealso> - | <seealso marker="#type-chardata">chardata()</seealso> - | <seealso marker="#type-external_chardata">external_chardata()</seealso></v> - <v>Result = binary() | {error, binary(), RestData} | {incomplete, binary(), binary()}</v> - <v>RestData = <seealso marker="#type-latin1_chardata">latin1_chardata()</seealso> - | <seealso marker="#type-chardata">chardata()</seealso> - | <seealso marker="#type-external_chardata">external_chardata()</seealso></v> - <v>InEncoding = <seealso marker="#type-encoding">encoding()</seealso></v> - </type> <desc> - <p>Same as characters_to_binary(Data, InEncoding, unicode).</p> + <p>Same as <c>characters_to_binary(<anno>Data</anno>, <anno>InEncoding</anno>, unicode)</c>.</p> </desc> </func> <func> <name name="characters_to_binary" arity="3"/> - <fsummary>Convert a collection of characters to an UTF-8 binary</fsummary> + <fsummary>Convert a collection of characters to a UTF-8 binary</fsummary> <desc> - <p>This function behaves as <seealso - marker="#characters_to_list/2"> - characters_to_list/2</seealso>, but produces an binary - instead of a unicode list. The + <p>Behaves as <seealso marker="#characters_to_list/2"> + <c>characters_to_list/2</c></seealso>, but produces an binary + instead of a Unicode list. The <c><anno>InEncoding</anno></c> defines how input is to be interpreted if binaries are present in the <c>Data</c>, while <c><anno>OutEncoding</anno></c> defines in what format output is to be @@ -314,7 +283,7 @@ <p>Errors and exceptions occur as in <seealso marker="#characters_to_list/2"> - characters_to_list/2</seealso>, but the second element + <c>characters_to_list/2</c></seealso>, but the second element in the <c>error</c> or <c>incomplete</c> tuple will be a <c>binary()</c> and not a <c>list()</c>.</p> @@ -324,16 +293,18 @@ <func> <name name="encoding_to_bom" arity="1"/> <fsummary>Create a binary UTF byte order mark from encoding.</fsummary> - <type_desc variable="Bin">A binary() of byte_size 4 or more.</type_desc> + <type_desc variable="Bin"> + A <c>binary()</c> such that <c>byte_size(<anno>Bin</anno>) >= 4</c>. + </type_desc> <desc> - <p>Create an UTF byte order mark (BOM) as a binary from the + <p>Create a UTF byte order mark (BOM) as a binary from the supplied <c><anno>InEncoding</anno></c>. The BOM is, if supported at all, expected to be placed first in UTF encoded files or messages.</p> <p>The function returns <c><<>></c> for the - <c>latin1</c> encoding, there is no BOM for ISO-latin-1.</p> + <c>latin1</c> encoding as there is no BOM for ISO-latin-1.</p> <p>It can be noted that the BOM for UTF-8 is seldom used, and it is really not a <em>byte order</em> mark. There are obviously no diff --git a/lib/stdlib/doc/src/unicode_usage.xml b/lib/stdlib/doc/src/unicode_usage.xml index bbcd49a934..c5d476e54b 100644 --- a/lib/stdlib/doc/src/unicode_usage.xml +++ b/lib/stdlib/doc/src/unicode_usage.xml @@ -1,11 +1,11 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="utf8" ?> <!DOCTYPE chapter SYSTEM "chapter.dtd"> <chapter> <header> <copyright> <year>1999</year> - <year>2012</year> + <year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -32,226 +32,1084 @@ <rev>PA1</rev> <file>unicode_usage.xml</file> </header> -<p>Implementing support for Unicode character sets is an ongoing process. The Erlang Enhancement Proposal (EEP) 10 outlines the basics of Unicode support and also specifies a default encoding in binaries that all Unicode-aware modules should handle in the future.</p> -<p>The functionality described in EEP10 is implemented in Erlang/OTP as of R13A, but that's by no means the end of it. More functionality will be needed in the future and more OTP-libraries might need updating to cope with Unicode data. One example of future development is obvious when reading this manual, our documentation format is limited to the ISO-latin-1 character range, why no Unicode characters beyond that range will occur in this document.</p> -<p>This guide outlines the current Unicode support and gives a couple of recipes for working with Unicode data.</p> <section> -<title>What Unicode is</title> -<p>Unicode is a standard defining codepoints (numbers) for all known, living or dead, scripts. In principle, every known symbol used in any language has a Unicode codepoint.</p> -<p>Unicode codepoints are defined and published by the <em>Unicode Consortium</em>, which is a non profit organization.</p> -<p>Support for Unicode is increasing throughout the world of computing, as the benefits of one common character set are overwhelming when programs are used in a global environment.</p> -<p>Along with the base of the standard, the codepoints for all the scripts, there are a couple of encoding standards available. Different operating systems and tools support different encodings. For example Linux and MacOS X has chosen the UTF-8 encoding, which is backwards compatible with 7-bit ASCII and therefore affects programs written in plain English the least. Windows® on the other hand supports a limited version of UTF-16, namely all the code planes where the characters can be stored in one single 16-bit entity, which includes most living languages.</p> -<p>The most widely spread encodings are:</p> -<taglist> -<tag>UTF-8</tag> -<item>Each character is stored in one to four bytes depending on codepoint. The encoding is backwards compatible with 7-bit ASCII as all 7-bit characters are stored in one single byte as is. The characters beyond codepoint 127 are stored in more bytes, letting the most significant bit in the first character indicate a multi-byte character. For details on the encoding, the RFC is publicly available.</item> -<tag>UTF-16</tag> -<item>This encoding has many similarities to UTF-8, but the basic unit is a 16-bit number. This means that all characters occupy at least two bytes, some high numbers even four bytes. Some programs and operating systems claiming to use UTF-16 only allows for characters that can be stored in one 16-bit entity, which is usually sufficient to handle living languages. As the basic unit is more than one byte, byte-order issues occur, why UTF-16 exists in both a big-endian and little-endian variant.</item> -<tag>UTF-32</tag> -<item>The most straight forward representation, each character is stored in one single 32-bit number. There is no need for escapes or any variable amount of entities for one character, all Unicode codepoints can be stored in one single 32-bit entity. As with UTF-16, there are byte-order issues, UTF-32 can be both big- and little-endian.</item> -<tag>UCS-4</tag> -<item>Basically the same as UTF-32, but without some Unicode semantics, defined by IEEE and has little use as a separate encoding standard. For all normal (and possibly abnormal) usages, UTF-32 and UCS-4 are interchangeable.</item> -</taglist> -<p>Certain ranges of characters are left unused and certain ranges are even deemed invalid. The most notable invalid range is 16#D800 - 16#DFFF, as the UTF-16 encoding does not allow for encoding of these numbers. It can be speculated that the UTF-16 encoding standard was, from the beginning, expected to be able to hold all Unicode characters in one 16-bit entity, but then had to be extended, leaving a hole in the Unicode range to cope with backward compatibility.</p> -<p>Additionally, the codepoint 16#FEFF is used for byte order marks (BOM's) and use of that character is not encouraged in other contexts than that. It actually is valid though, as the character "ZWNBS" (Zero Width Non Breaking Space). BOM's are used to identify encodings and byte order for programs where such parameters are not known in advance. Byte order marks are more seldom used than one could expect, but their use is becoming more widely spread as they provide the means for programs to make educated guesses about the Unicode format of a certain file.</p> +<title>Unicode Implementation</title> + <p>Implementing support for Unicode character sets is an ongoing + process. The Erlang Enhancement Proposal (EEP) 10 outlined the + basics of Unicode support and also specified a default encoding in + binaries that all Unicode-aware modules should handle in the + future.</p> + + <p>The functionality described in EEP10 was implemented in Erlang/OTP + as of R13A, but that was by no means the end of it. In R14B01 support + for Unicode file names was added, although it was in no way complete + and was by default disabled on platforms where no guarantee was given + for the file name encoding. With R16A came support for UTF-8 encoded + source code, among with enhancements to many of the applications to + support both Unicode encoded file names as well as support for UTF-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 R17, the encoding default for Erlang source files will be + switched to UTF-8 and in R18 Erlang will support atoms in the full + Unicode range, meaning full Unicode function and module + names</p> + + <p>This guide outlines the current Unicode support and gives a couple + of recipes for working with Unicode data.</p> +</section> +<section> +<title>Understanding Unicode</title> + <p>Experience with the Unicode support in Erlang has made it + painfully clear that understanding Unicode characters and encodings + is not as easy as one would expect. The complexity of the field as + well as the implications of the standard requires thorough + understanding of concepts rarely before thought of.</p> + + <p>Furthermore the Erlang implementation requires understanding of + concepts that never were an issue for many (Erlang) programmers. To + understand and use Unicode characters requires that you study the + subject thoroughly, even if you're an experienced programmer.</p> + + <p>As an example, one could contemplate the issue of converting + between upper and lower case letters. Reading the standard will make + you realize that, to begin with, there's not a simple one to one + mapping in all scripts. Take German as an example, where there's a + letter "ß" (Sharp s) in lower case, but the uppercase equivalent is + "SS". Or Greek, where "Σ" has two different lowercase forms: "ς" in + word-final position and "σ" elsewhere. Or Turkish where dotted and + dot-less "i" both exist in lower case and upper case forms, or + Cyrillic "I" which usually has no lowercase form. Or of course + languages that have no concept of upper case (or lower case). So, a + conversion function will need to know not only one character at a + time, but possibly the whole sentence, maybe the natural language + the translation should be in and also take into account differences + in input and output string length and so on. There is at the time of + writing no Unicode to_upper/to_lower functionality in Erlang/OTP, but + there are publicly available libraries that address these issues.</p> + + <p>Another example is the accented characters where the same glyph + has two different representations. Let's look at the Swedish + "ö". There's a code point for that in the Unicode standard, but you + can also write it as "o" followed by U+0308 (Combining Diaeresis, + with the simplified meaning that the last letter should have a "¨" + above). They have exactly the same glyph. They are for most + purposes the same, but they have completely different + representations. For example MacOS X converts all file names to use + Combining Diaeresis, while most other programs (including Erlang) + try to hide that by doing the opposite when for example listing + directories. However it's done, it's usually important to normalize + such characters to avoid utter confusion.</p> + + <p>The list of examples can be made as long as the Unicode standard, I + suspect. The point is that one need a kind of knowledge that was + never needed when programs only took one or two languages into + account. The complexity of human languages and scripts, certainly + has made this a challenge when constructing a universal + standard. Supporting Unicode properly in your program <em>will</em> require + effort.</p> + </section> <section> -<title>Standard Unicode representation in Erlang</title> -<p>In Erlang, strings are actually lists of integers. A string is defined to be encoded in the ISO-latin-1 (ISO8859-1) character set, which is, codepoint by codepoint, a sub-range of the Unicode character set.</p> -<p>The standard list encoding for strings is therefore easily extendible to cope with the whole Unicode range: A Unicode string in Erlang is simply a list containing integers, each integer being a valid Unicode codepoint and representing one character in the Unicode character set.</p> -<p>Regular Erlang strings in ISO-latin-1 are a subset of their Unicode strings.</p> +<title>What Unicode Is</title> + <p>Unicode is a standard defining code points (numbers) for all + known, living or dead, scripts. In principle, every known symbol + used in any language has a Unicode code point.</p> + <p>Unicode code points are defined and published by the <em>Unicode + Consortium</em>, which is a non profit organization.</p> + <p>Support for Unicode is increasing throughout the world of + computing, as the benefits of one common character set are + overwhelming when programs are used in a global environment.</p> + <p>Along with the base of the standard: the code points for all the + scripts, there are a couple of <em>encoding standards</em> available.</p> + <p>It is vital to understand the difference between encodings and + Unicode characters. Unicode characters are code points according to + the Unicode standard, while the encodings are ways to represent such + code points. An encoding is just a standard for representation, + UTF-8 can for example be used to represent a very limited part of + the Unicode character set (e.g. ISO-Latin-1), or the full Unicode + range. It's just an encoding format.</p> + <p>As long as all character sets were limited to 256 characters, + each character could be stored in one single byte, so there was more + or less only one practical encoding for the characters. Encoding + each character in one byte was so common that the encoding wasn't + even named. When we now, with the Unicode system, have a lot more + than 256 characters, we need a common way to represent these. The + common ways of representing the code points are the encodings. This + means a whole new concept to the programmer, the concept of + character representation, which was before a non-issue.</p> -<p>Binaries on the other hand are more troublesome. For performance reasons, programs often store textual data in binaries instead of lists, mainly because they are more compact (one byte per character instead of two words per character, as is the case with lists). Using erlang:list_to_binary/1, an regular Erlang string can be converted into a binary, effectively using the ISO-latin-1 encoding in the binary - one byte per character. This is very convenient for those regular Erlang strings, but cannot be done for Unicode lists.</p> -<p>As the UTF-8 encoding is widely spread and provides the most compact storage, it is selected as the standard encoding of Unicode characters in binaries for Erlang.</p> -<p>The standard binary encoding is used whenever a library function in Erlang should cope with Unicode data in binaries, but is of course not enforced when communicating externally. Functions and bit-syntax exist to encode and decode both UTF-8, UTF-16 and UTF-32 in binaries. Library functions dealing with binaries and Unicode in general, however, only deal with the default encoding.</p> + <p>Different operating systems and tools support different + encodings. For example Linux and MacOS X has chosen the UTF-8 + encoding, which is backwards compatible with 7-bit ASCII and + therefore affects programs written in plain English the + least. Windows on the other hand supports a limited version of + UTF-16, namely all the code planes where the characters can be + stored in one single 16-bit entity, which includes most living + languages.</p> -<p>Character data may be combined from several sources, sometimes available in a mix of strings and binaries. Erlang has for long had the concept of iodata or iolists, where binaries and lists can be combined to represent a sequence of bytes. In the same way, the Unicode aware modules often allow for combinations of binaries and lists where the binaries have characters encoded in UTF-8 and the lists contain such binaries or numbers representing Unicode codepoints:</p> -<code type="none"> + <p>The most widely spread encodings are:</p> + <taglist> + <tag>Bytewise representation</tag> + <item>This is not a proper Unicode representation, but the + representation used for characters before the Unicode standard. It + can still be used to represent character code points in the Unicode + standard that have numbers below 256, which corresponds exactly to + the ISO-Latin-1 character set. In Erlang, this is commonly denoted + <c>latin1</c> encoding, which is slightly misleading as ISO-Latin-1 is + a character code range, not an encoding.</item> + <tag>UTF-8</tag> + <item>Each character is stored in one to four bytes depending on + code point. The encoding is backwards compatible with bytewise + representation of 7-bit ASCII as all 7-bit characters are stored + in one single byte in UTF-8. The characters beyond code point 127 + are stored in more bytes, letting the most significant bit in the + first character indicate a multi-byte character. For details on + the encoding, the RFC is publicly available. Note that UTF-8 is + <em>not</em> compatible with bytewise representation for + code points between 128 and 255, so a ISO-Latin-1 bytewise + representation is not generally compatible with UTF-8.</item> + <tag>UTF-16</tag> + <item>This encoding has many similarities to UTF-8, but the basic + unit is a 16-bit number. This means that all characters occupy at + least two bytes, some high numbers even four bytes. Some programs, + libraries and operating systems claiming to use UTF-16 only allows + for characters that can be stored in one 16-bit entity, which is + usually sufficient to handle living languages. As the basic unit + is more than one byte, byte-order issues occur, why UTF-16 exists + in both a big-endian and little-endian variant. In Erlang, the + full UTF-16 range is supported when applicable, like in the + <c>unicode</c> module and in the bit syntax.</item> + <tag>UTF-32</tag> + <item>The most straight forward representation. Each character is + stored in one single 32-bit number. There is no need for escapes + or any variable amount of entities for one character, all Unicode + code points can be stored in one single 32-bit entity. As with + UTF-16, there are byte-order issues, UTF-32 can be both big- and + little-endian.</item> + <tag>UCS-4</tag> + <item>Basically the same as UTF-32, but without some Unicode + semantics, defined by IEEE and has little use as a separate + encoding standard. For all normal (and possibly abnormal) usages, + UTF-32 and UCS-4 are interchangeable.</item> + </taglist> + <p>Certain ranges of numbers are left unused in the Unicode standard + and certain ranges are even deemed invalid. The most notable invalid + range is 16#D800 - 16#DFFF, as the UTF-16 encoding does not allow + for encoding of these numbers. It can be speculated that the UTF-16 + encoding standard was, from the beginning, expected to be able to + hold all Unicode characters in one 16-bit entity, but then had to be + extended, leaving a hole in the Unicode range to cope with backward + compatibility.</p> + <p>Additionally, the code point 16#FEFF is used for byte order marks + (BOM's) and use of that character is not encouraged in other + contexts than that. It actually is valid though, as the character + "ZWNBS" (Zero Width Non Breaking Space). BOM's are used to identify + encodings and byte order for programs where such parameters are not + known in advance. Byte order marks are more seldom used than one + could expect, but their use might become more widely spread as they + provide the means for programs to make educated guesses about the + Unicode format of a certain file.</p> +</section> +<section> + <title>Areas of Unicode Support</title> + <p>To support Unicode in Erlang, problems in several areas have been + addressed. Each area is described briefly in this section and more + thoroughly further down in this document:</p> + <taglist> + <tag>Representation</tag> + <item>To handle Unicode characters in Erlang, we have to have a + common representation both in lists and binaries. The EEP (10) and + the subsequent initial implementation in R13A settled a standard + representation of Unicode characters in Erlang.</item> + <tag>Manipulation</tag> + <item>The Unicode characters need to be processed by the Erlang + program, why library functions need to be able to handle them. In + some cases functionality was added to already existing interfaces + (as the string module now can handle lists with arbitrary code points), + in some cases new functionality or options need to be added (as in + the <c>io</c>-module, the file handling, the <c>unicode</c> module + and the bit syntax). Today most modules in kernel and STDLIB, as + well as the VM are Unicode aware.</item> + <tag>File I/O</tag> + <item>I/O is by far the most problematic area for Unicode. A file + is an entity where bytes are stored and the lore of programming + has been to treat characters and bytes as interchangeable. With + Unicode characters, you need to decide on an encoding as soon as + you want to store the data in a file. In Erlang you can open a + text file with an encoding option, so that you can read characters + from it rather than bytes, but you can also open a file for + bytewise I/O. The I/O-system of Erlang has been designed (or at + least used) in a way where you expect any I/O-server to be + able to cope with any string data, but that is no longer the case + when you work with Unicode characters. Handling the fact that you + need to know the capabilities of the device where your data ends + up is something new to the Erlang programmer. Furthermore, ports + in Erlang are byte oriented, so an arbitrary string of (Unicode) + characters can not be sent to a port without first converting it + to an encoding of choice.</item> + <tag>Terminal I/O</tag> + <item>Terminal I/O is slightly easier than file I/O. The output is + meant for human reading and is usually Erlang syntax (e.g. in the + shell). There exists syntactic representation of any Unicode + character without actually displaying the glyph (instead written + as <c>\x{</c>HHH<c>}</c>), so Unicode data can usually be displayed + even if the terminal as such do not support the whole Unicode + range.</item> + <tag>File names</tag> + <item>File names can be stored as Unicode strings, in different + ways depending on the underlying OS and file system. This can be + handled fairly easy by a program. The problems arise when the file + system is not consistent in it's encodings, like for example + Linux. Linux allows files to be named with any sequence of bytes, + leaving to each program to interpret those bytes. On systems where + these "transparent" file names are used, Erlang has to be informed + about the file name encoding by a startup flag. The default is + bytewise interpretation, which is actually usually wrong, but + allows for interpretation of <em>all</em> file names. The concept + of "raw file names" can be used to handle wrongly encoded + file names if one enables Unicode file name translation + (<c>+fnu</c>) on platforms where this is not the default.</item> + <tag>Source code encoding</tag> + <item>When it comes to the Erlang source code, there is support + for the UTF-8 encoding and bytewise encoding. The default in R16B + is bytewise (or latin1) encoding. You can control the encoding by + a comment like: +<code> +%% -*- coding: utf-8 -*- +</code> + in the beginning of the file. This of course requires your editor to + support UTF-8 as well. The same comment is also interpreted by + functions like <c>file:consult/1</c>, the release handler etc, so that + you can have all text files in your source directories in UTF-8 + encoding. + </item> + <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 R18 release. 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 + operating systems with inconsistent file naming schemes, and might + also hurt portability, so it's not really recommended. It is + suggested in EEP 40 that the language should also allow for + Unicode characters > 255 in variable names. Whether to + implement that EEP or not is yet to be decided.</item> + </taglist> +</section> +<section> + <title>Standard Unicode Representation</title> + <p>In Erlang, strings are actually lists of integers. A string was + up until R13 defined to be encoded in the ISO-latin-1 (ISO8859-1) + character set, which is, code point by code point, a sub-range of + the Unicode character set.</p> + <p>The standard list encoding for strings was therefore easily + extended to cope with the whole Unicode range: A Unicode string in + Erlang is simply a list containing integers, each integer being a + valid Unicode code point and representing one character in the + Unicode character set.</p> + <p>Erlang strings in ISO-latin-1 are a subset of Unicode + strings.</p> + <p>Only if a string contains code points < 256, can it be + directly converted to a binary by using + i.e. <c>erlang:iolist_to_binary/1</c> or can be sent directly to a + port. If the string contains Unicode characters > 255, an + encoding has to be decided upon and the string should be converted + to a binary in the preferred encoding using + <c>unicode:characters_to_binary/{1,2,3}</c>. Strings are not + generally lists of bytes, as they were before R13. They are lists of + characters. Characters are not generally bytes, they are Unicode + code points.</p> + + <p>Binaries are more troublesome. For performance reasons, programs + often store textual data in binaries instead of lists, mainly + because they are more compact (one byte per character instead of two + words per character, as is the case with lists). Using + <c>erlang:list_to_binary/1</c>, an ISO-Latin-1 Erlang string could + be converted into a binary, effectively using bytewise encoding - + one byte per character. This was very convenient for those limited + Erlang strings, but cannot be done for arbitrary Unicode lists.</p> + <p>As the UTF-8 encoding is widely spread and provides some backward + compatibility in the 7-bit ASCII range, it is selected as the + standard encoding for Unicode characters in binaries for Erlang.</p> + <p>The standard binary encoding is used whenever a library function + in Erlang should cope with Unicode data in binaries, but is of + course not enforced when communicating externally. Functions and + bit-syntax exist to encode and decode both UTF-8, UTF-16 and UTF-32 + in binaries. Library functions dealing with binaries and Unicode in + general, however, only deal with the default encoding.</p> + + <p>Character data may be combined from several sources, sometimes + available in a mix of strings and binaries. Erlang has for long had + the concept of <c>iodata</c> or <c>iolist</c>s, where binaries and + lists can be combined to represent a sequence of bytes. In the same + way, the Unicode aware modules often allow for combinations of + binaries and lists where the binaries have characters encoded in + UTF-8 and the lists contain such binaries or numbers representing + Unicode code points:</p> + <code type="none"> unicode_binary() = binary() with characters encoded in UTF-8 coding standard -unicode_char() = integer() representing valid unicode codepoint chardata() = charlist() | unicode_binary() -charlist() = [unicode_char() | unicode_binary() | charlist()] - a unicode_binary is allowed as the tail of the list</code> -<p>The module <c>unicode</c> in stdlib even supports similar mixes with binaries containing other encodings than UTF-8, but that is a special case to allow for conversions to and from external data:</p> - <code type="none"> -external_unicode_binary() = binary() with characters coded in a user specified Unicode - encoding other than UTF-8 (UTF-16 or UTF-32) +charlist() = maybe_improper_list(char() | unicode_binary() | charlist(), + unicode_binary() | nil())</code> + <p>The module <seealso + marker="stdlib:unicode"><c>unicode</c></seealso> in STDLIB even + supports similar mixes with binaries containing other encodings than + UTF-8, but that is a special case to allow for conversions to and + from external data:</p> + <code type="none"> +external_unicode_binary() = binary() with characters coded in + a user specified Unicode encoding other than UTF-8 (UTF-16 or UTF-32) external_chardata() = external_charlist() | external_unicode_binary() -external_charlist() = [unicode_char() | external_unicode_binary() | external_charlist()] - an external_unicode_binary is allowed as the tail of the list</code> +external_charlist() = maybe_improper_list(char() | + external_unicode_binary() | + external_charlist(), + external_unicode_binary() | nil())</code> </section> <section> -<title>Basic language support for Unicode</title> -<p>First of all, Erlang is still defined to be written in the ISO-latin-1 character set. Functions have to be named in that character set, atoms are restricted to ISO-latin-1 and regular strings are still lists of characters 0..255 in the ISO-latin-1 encoding. This has not (yet) changed, but the language has been slightly extended to cope with Unicode characters and encodings.</p> - -<section> -<title>Bit-syntax</title> -<p>The bit-syntax contains types for coping with binary data in the three main encodings. The types are named <c>utf8</c>, <c>utf16</c> and <c>utf32</c> respectively. The <c>utf16</c> and <c>utf32</c> types can be in a big- or little-endian variant:</p> -<code> + <title>Basic Language Support</title> + <p><marker id="unicode_in_erlang"/>As of Erlang/OTP R16 Erlang + source files can be written in either UTF-8 or bytewise encoding + (a.k.a. <c>latin1</c> encoding). The details on how to state the encoding + of an Erlang source file can be found in + <seealso marker="stdlib:epp#encoding"><c>epp(3)</c></seealso>. Strings and comments + can be written using Unicode, but functions still have to be named + 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 R18 is expected to handle functions named in + Unicode as well as Unicode atoms.</p> + <section> + <title>Bit-syntax</title> + <p>The bit-syntax contains types for coping with binary data in the + three main encodings. The types are named <c>utf8</c>, <c>utf16</c> + and <c>utf32</c> respectively. The <c>utf16</c> and <c>utf32</c> types + can be in a big- or little-endian variant:</p> + <code> <<Ch/utf8,_/binary>> = Bin1, <<Ch/utf16-little,_/binary>> = Bin2, Bin3 = <<$H/utf32-little, $e/utf32-little, $l/utf32-little, $l/utf32-little, - $o/utf32-little>>,</code> -<p>For convenience, literal strings can be encoded with a Unicode encoding in binaries using the following (or similar) syntax:</p> -<code> +$o/utf32-little>>,</code> + <p>For convenience, literal strings can be encoded with a Unicode + encoding in binaries using the following (or similar) syntax:</p> + <code> Bin4 = <<"Hello"/utf16>>,</code> -</section> -<section> -<title>String- and character-literals</title> -<warning> -<p>The literal syntax described here may be subject to change in R13B, it has not yet passed the usual process for language changes approval.</p> -</warning> -<p>It is convenient to be able to write a list of Unicode characters in the string syntax. However, the language specifies strings as being in the ISO-latin-1 character set which the compiler tool chain as well as many other tools expect.</p> -<p>Also the source code is (for now) still expected to be written using the ISO-latin-1 character set, why Unicode characters beyond that range cannot be entered in string literals.</p> -<p>To make it easier to enter Unicode characters in the shell, it allows strings with Unicode characters on input, immediately converting them to regular lists of integers. They will, by the evaluator etc be viewed as if they were input using the regular list syntax, which is - in the end - how the language actually treats them. They will in the same way not be output as strings by i.e <c>io:write/2</c> or <c>io:format/3</c> unless the format string supplied to <c>io:format</c> uses the Unicode translation modifier (which we will talk about later).</p> -<p>For source code, there is an extension to the \OOO (backslash followed by three octal numbers) and \xHH (backslash followed by 'x', followed by two hexadecimal characters) syntax, namely \x{H ...} (a backslash followed by an 'x', followed by left curly bracket, any number of hexadecimal digits and a terminating right curly bracket). This allows for entering characters of any codepoint literally in a string. The string is immediately converted into a list by the scanner however, which is obvious when calling it directly:</p> + </section> + <section> + <title>String and Character Literals</title> + <p>For source code, there is an extension to the <c>\</c>OOO + (backslash followed by three octal numbers) and <c>\x</c>HH + (backslash followed by <c>x</c>, followed by two hexadecimal + characters) syntax, namely <c>\x{</c>H ...<c>}</c> (a backslash + followed by an <c>x</c>, followed by left curly bracket, any + number of hexadecimal digits and a terminating right curly + bracket). This allows for entering characters of any code point + literally in a string even when the encoding of the source file is + bytewise (<c>latin1</c>).</p> + <p>In the shell, if using a Unicode input device, or in source + code stored in UTF-8, <c>$</c> can be followed directly by a + Unicode character producing an integer. In the following example + the code point of a Cyrillic <c>с</c> is output:</p> + <pre> +7> <input>$с.</input> +1089</pre> + </section> + <section> + <title>Heuristic String Detection</title> + <p>In certain output functions and in the output of return values + in the shell, Erlang tries to heuristically detect string data in + lists and binaries. Typically you will see heuristic detection in + a situation like this:</p> + <pre> +1> <input>[97,98,99].</input> +"abc" +2> <input><<97,98,99>>.</input> +<<"abc">> +3> <input><<195,165,195,164,195,182>>.</input> +<<"åäö"/utf8>></pre> + <p>Here the shell will detect lists containing printable + characters or binaries containing printable characters either in + bytewise or UTF-8 encoding. The question here is: what is a + printable character? One view would be that anything the Unicode + standard thinks is printable, will also be printable according to + the heuristic detection. The result would be that almost any list + of integers will be deemed a string, resulting in all sorts of + characters being printed, maybe even characters your terminal does + not have in its font set (resulting in some generic output you + probably will not appreciate). Another way is to keep it backwards + compatible so that only the ISO-Latin-1 character set is used to + detect a string. A third way would be to let the user decide + exactly what Unicode ranges are to be viewed as characters. In + R16B you can select either the whole Unicode range or the + ISO-Latin-1 range by supplying the startup flag <c>+pc + </c><i>Range</i>, where <i>Range</i> is either <c>latin1</c> or + <c>unicode</c>. For backwards compatibility, the default is + <c>latin1</c>. This only controls how heuristic string detection + is done. In the future, more ranges are expected to be added, so + that one can tailor the heuristics to the language and region + relevant to the user.</p> + <p>Lets look at an example with the two different startup options:</p> <pre> -1> <input>erl_scan:string("\"X\".").</input> -{ok,[{string,1,"X"},{dot,1}],1} -2> <input>erl_scan:string("\"\x{400}\".").</input> -{ok,[{'[',1},{integer,1,1024},{']',1},{dot,1}],1}</pre> -<p>Character literals, or rather integers representing Unicode codepoints can be expressed in a similar way using $\x{H ...}:</p> +$ <input>erl +pc latin1</input> +Erlang R16B (erts-5.10.1) [source] [async-threads:0] [hipe] [kernel-poll:false] + +Eshell V5.10.1 (abort with ^G) +1> <input>[1024].</input> +[1024] +2> <input>[1070,1085,1080,1082,1086,1076].</input> +[1070,1085,1080,1082,1086,1076] +3> <input>[229,228,246].</input> +"åäö" +4> <input><<208,174,208,189,208,184,208,186,208,190,208,180>>.</input> +<<208,174,208,189,208,184,208,186,208,190,208,180>> +5> <input><<229/utf8,228/utf8,246/utf8>>.</input> +<<"åäö"/utf8>> +</pre> <pre> -4> <input>$\x{400}.</input> -1024</pre> -<p>This also is a translation by the scanner:</p> +$ <input>erl +pc unicode</input> +Erlang R16B (erts-5.10.1) [source] [async-threads:0] [hipe] [kernel-poll:false] + +Eshell V5.10.1 (abort with ^G) +1> <input>[1024].</input> +"Ѐ" +2> <input>[1070,1085,1080,1082,1086,1076].</input> +"Юникод" +3> <input>[229,228,246].</input> +"åäö" +4> <input><<208,174,208,189,208,184,208,186,208,190,208,180>>.</input> +<<"Юникод"/utf8>> +5> <input><<229/utf8,228/utf8,246/utf8>>.</input> +<<"åäö"/utf8>> +</pre> + <p>In the examples, we can see that the default Erlang shell will + only interpret characters from the ISO-Latin1 range as printable + and will only detect lists or binaries with those "printable" + characters as containing string data. The valid UTF-8 binary + containing "Юникод", will not be printed as a string. When, on the + other hand, started with all Unicode characters printable (<c>+pc + unicode</c>), the shell will output anything containing printable + Unicode data (in binaries either UTF-8 or bytewise encoded) as + string data.</p> + + <p>These heuristics are also used by + <c>io</c>(<c>_lib</c>)<c>:format/2</c> and friends when the + <c>t</c> modifier is used in conjunction with <c>~p</c> or + <c>~P</c>:</p> <pre> -5> <input>erl_scan:string("$Y.").</input> -{ok,[{char,1,89},{dot,1}],1} -6> <input>erl_scan:string("$\x{400}.").</input> -{ok,[{integer,1,1024},{dot,1}],1}</pre> -<p>In the shell, if using a Unicode input device, '$' can be followed directly by a Unicode character producing an integer. In the following example, let's imagine the character 'c' is actually a Cyrillic 's' (looking fairly similar):</p> +$ <input>erl +pc latin1</input> +Erlang R16B (erts-5.10.1) [source] [async-threads:0] [hipe] [kernel-poll:false] + +Eshell V5.10.1 (abort with ^G) +1> <input>io:format("~tp~n",[{<<"åäö">>, <<"åäö"/utf8>>, <<208,174,208,189,208,184,208,186,208,190,208,180>>}]).</input> +{<<"åäö">>,<<"åäö"/utf8>>,<<208,174,208,189,208,184,208,186,208,190,208,180>>} +ok +</pre> <pre> -7> <input>$c.</input> -1089</pre> +$ <input>erl +pc unicode</input> +Erlang R16B (erts-5.10.1) [source] [async-threads:0] [hipe] [kernel-poll:false] + +Eshell V5.10.1 (abort with ^G) +1> <input>io:format("~tp~n",[{<<"åäö">>, <<"åäö"/utf8>>, <<208,174,208,189,208,184,208,186,208,190,208,180>>}]).</input> +{<<"åäö">>,<<"åäö"/utf8>>,<<"Юникод"/utf8>>} +ok +</pre> + <p>Please observe that this only affects <i>heuristic</i> interpretation + of lists and binaries on output. For example the <c>~ts</c> format + sequence does always output a valid lists of characters, + regardless of the <c>+pc</c> setting, as the programmer has + explicitly requested string output.</p> + </section> </section> -<p>The literal syntax allowing Unicode characters is to be viewed as "syntactic sugar", but is, as such, fairly useful.</p> -</section> <section> -<title>The interactive shell</title> -<p>The interactive Erlang shell, when started towards a terminal or started using the <c>werl</c> command on windows, can support Unicode input and output.</p> -<p>On Windows®, proper operation requires that a suitable font is installed and selected for the Erlang application to use. If no suitable font is available on your system, try installing the DejaVu fonts (dejavu-fonts.org), which are freely available and then select that font in the Erlang shell application.</p> -<p>On Unix®-like operating systems, the terminal should be able to handle UTF-8 on input and output (modern versions of XTerm, KDE konsole and the Gnome terminal do for example) and your locale settings have to be proper. As an example, my LANG environment variable is set as this:</p> -<pre> + <title>The Interactive Shell</title> + <p>The interactive Erlang shell, when started towards a terminal or + started using the <c>werl</c> command on windows, can support + Unicode input and output.</p> + <p>On Windows, proper operation requires that a suitable font + is installed and selected for the Erlang application to use. If no + suitable font is available on your system, try installing the DejaVu + fonts (<c>dejavu-fonts.org</c>), which are freely available and then + select that font in the Erlang shell application.</p> + <p>On Unix-like operating systems, the terminal should be able + to handle UTF-8 on input and output (modern versions of XTerm, KDE + konsole and the Gnome terminal do for example) and your locale + settings have to be proper. As an example, my <c>LANG</c> + environment variable is set as this:</p> + <pre> $ <input>echo $LANG</input> en_US.UTF-8</pre> -<p>Actually, most systems handle the LC_CTYPE variable before LANG, so if that is set, it has to be set to UTF-8:</p> -<pre> + <p>Actually, most systems handle the <c>LC_CTYPE</c> variable before + <c>LANG</c>, so if that is set, it has to be set to + <c>UTF-8</c>:</p> + <pre> $ echo <input>$LC_CTYPE</input> en_US.UTF-8</pre> -<p>The LANG or LC_CTYPE setting should be consistent with what the terminal is capable of, there is no portable way for Erlang to ask the actual terminal about its UTF-8 capacity, we have to rely on the language and character type settings.</p> -<p>To investigate what Erlang thinks about the terminal, the <c>io:getopts()</c> call can be used when the shell is started:</p> -<pre> + <p>The <c>LANG</c> or <c>LC_CTYPE</c> setting should be consistent + with what the terminal is capable of, there is no portable way for + Erlang to ask the actual terminal about its UTF-8 capacity, we have + to rely on the language and character type settings.</p> + <p>To investigate what Erlang thinks about the terminal, the + <c>io:getopts()</c> call can be used when the shell is started:</p> + <pre> $ <input>LC_CTYPE=en_US.ISO-8859-1 erl</input> -Erlang R13A (erts-5.7) [source] [64-bit] [smp:4:4] [rq:4] [async-threads:0] [kernel-poll:false] +Erlang R16B (erts-5.10.1) [source] [async-threads:0] [hipe] [kernel-poll:false] -Eshell V5.7 (abort with ^G) -1> <input>lists:keyfind(encoding,1,io:getopts()).</input> +Eshell V5.10.1 (abort with ^G) +1> <input>lists:keyfind(encoding, 1, io:getopts()).</input> {encoding,latin1} 2> <input>q().</input> ok $ <input>LC_CTYPE=en_US.UTF-8 erl</input> -Erlang R13A (erts-5.7) [source] [64-bit] [smp:4:4] [rq:4] [async-threads:0] [kernel-poll:false] +Erlang R16B (erts-5.10.1) [source] [async-threads:0] [hipe] [kernel-poll:false] -Eshell V5.7 (abort with ^G) -1> <input>lists:keyfind(encoding,1,io:getopts()).</input> +Eshell V5.10.1 (abort with ^G) +1> <input>lists:keyfind(encoding, 1, io:getopts()).</input> {encoding,unicode} 2></pre> -<p>When (finally?) everything is in order with the locale settings, fonts and the terminal emulator, you probably also have discovered a way to input characters in the script you desire. For testing, the simplest way is to add some keyboard mappings for other languages, usually done with some applet in your desktop environment. In my KDE environment, I start the KDE Control Center (Personal Settings), select "Regional and Accessibility" and then "Keyboard Layout". On Windows XP®, I start Control Panel->Regional and Language Options, select the Language tab and click the Details... button in the square named "Text services and input Languages". Your environment probably provides similar means of changing the keyboard layout. Make sure you have a way to easily switch back and forth between keyboards if you are not used to this, entering commands using a Cyrillic character set is, as an example, not easily done in the Erlang shell.</p> -<p>Now you are set up for some Unicode input and output. The simplest thing to do is of course to enter a string in the shell:</p> -<image file="ushell1.gif"><icaption>Cyrillic characters in an Erlang shell</icaption></image> -<p>While strings can be input as Unicode characters, the language elements are still limited to the ISO-latin-1 character set. Only character constants and strings are allowed to be beyond that range:</p> -<image file="ushell2.gif"><icaption>Unicode characters in allowed and disallowed context</icaption></image> + + <p>When (finally?) everything is in order with the locale settings, + fonts and the terminal emulator, you probably also have discovered a + way to input characters in the script you desire. For testing, the + simplest way is to add some keyboard mappings for other languages, + usually done with some applet in your desktop environment. In my KDE + environment, I start the KDE Control Center (Personal Settings), + select "Regional and Accessibility" and then "Keyboard Layout". On + Windows XP, I start Control Panel->Regional and Language + Options, select the Language tab and click the Details... button in + the square named "Text services and input Languages". Your + environment probably provides similar means of changing the keyboard + layout. Make sure you have a way to easily switch back and forth + between keyboards if you are not used to this, entering commands + using a Cyrillic character set is, as an example, not easily done in + the Erlang shell.</p> + + <p>Now you are set up for some Unicode input and output. The + simplest thing to do is of course to enter a string in the + shell:</p> + + <pre> +$ <input>erl</input> +Erlang R16B (erts-5.10.1) [source] [async-threads:0] [hipe] [kernel-poll:false] + +Eshell V5.10.1 (abort with ^G) +1> <input>lists:keyfind(encoding, 1, io:getopts()).</input> +{encoding,unicode} +2> <input>"Юникод".</input> +"Юникод" +3> <input>io:format("~ts~n", [v(2)]).</input> +Юникод +ok +4> </pre> + <p>While strings can be input as Unicode characters, the language + elements are still limited to the ISO-latin-1 character set. Only + character constants and strings are allowed to be beyond that + range:</p> + <pre> +$ <input>erl</input> +Erlang R16B (erts-5.10.1) [source] [async-threads:0] [hipe] [kernel-poll:false] + +Eshell V5.10.1 (abort with ^G) +1> <input>$ξ.</input> +958 +2> <input>Юникод.</input> +* 1: illegal character +2> </pre> </section> <section> -<title>Unicode file names</title> -<p>Most modern operating systems support Unicode file names in some way or another. There are several different ways to do this and Erlang by default treats the different approaches differently:</p> -<taglist> -<tag>Mandatory Unicode file naming</tag> -<item> -<p>Windows and, for most common uses, MacOSX enforces Unicode support for file names. All files created in the filesystem have names that can consistently be interpreted. In MacOSX, all file names are retrieved in UTF-8 encoding, while Windows has selected an approach where each system call handling file names has a special Unicode aware variant, giving much the same effect. There are no file names on these systems that are not Unicode file names, why the default behavior of the Erlang VM is to work in "Unicode file name translation mode", meaning that a file name can be given as a Unicode list and that will be automatically translated to the proper name encoding for the underlying operating and file system.</p> -<p>Doing i.e. a <c>file:list_dir/1</c> on one of these systems may return Unicode lists with codepoints beyond 255, depending on the content of the actual filesystem.</p> -<p>As the feature is fairly new, you may still stumble upon non core applications that cannot handle being provided with file names containing characters with codepoints larger than 255, but the core Erlang system should have no problems with Unicode file names.</p> -</item> -<tag>Transparent file naming</tag> -<item> -<p>Most Unix operating systems have adopted a simpler approach, namely that Unicode file naming is not enforced, but by convention. Those systems usually use UTF-8 encoding for Unicode file names, but do not enforce it. On such a system, a file name containing characters having codepoints between 128 and 255 may be named either as plain ISO-latin-1 or using UTF-8 encoding. As no consistency is enforced, the Erlang VM can do no consistent translation of all file names. If the VM would automatically select encoding based on heuristics, one could get unexpected behavior on these systems, therefore file names not being encoded in UTF-8 are returned as "raw file names" if Unicode file naming support is turned on.</p> -<p>A raw file name is not a list, but a binary. Many non core applications still do not handle file names given as binaries, why such raw names are avoided by default. This means that systems having implemented Unicode file naming through transparent file systems and an UTF-8 convention, do not by default have Unicode file naming turned on. Explicitly turning Unicode file name handling on for these types of systems is considered experimental.</p> -</item> -</taglist> -<p>The Unicode file naming support was introduced with OTP release R14B01. A VM operating in Unicode file mode can work with files having names in any language or character set (as long as it's supported by the underlying OS and file system). The Unicode character list is used to denote file or directory names and if the file system content is listed, you will also be able to get Unicode lists as return value. The support lies in the kernel and stdlib modules, why most applications (that does not explicitly require the file names to be in the ISO-latin-1 range) will benefit from the Unicode support without change.</p> + <title>Unicode File Names</title> + <p>Most modern operating systems support Unicode file names in some + way or another. There are several different ways to do this and + Erlang by default treats the different approaches differently:</p> + <taglist> + <tag>Mandatory Unicode file naming</tag> + <item> + <p>Windows and, for most common uses, MacOS X enforces Unicode + support for file names. All files created in the file system have + names that can consistently be interpreted. In MacOS X, all file + names are retrieved in UTF-8 encoding, while Windows has + selected an approach where each system call handling file names + has a special Unicode aware variant, giving much the same + effect. There are no file names on these systems that are not + Unicode file names, why the default behavior of the Erlang VM is + to work in "Unicode file name translation mode", + meaning that a file name can be given as a Unicode list and that + will be automatically translated to the proper name encoding for + the underlying operating and file system.</p> + <p>Doing i.e. a <c>file:list_dir/1</c> on one of these systems + may return Unicode lists with code points beyond 255, depending + on the content of the actual file system.</p> + <p>As the feature is fairly new, you may still stumble upon non + core applications that cannot handle being provided with file + names containing characters with code points larger than 255, but + the core Erlang system should have no problems with Unicode file + names.</p> + </item> + <tag>Transparent file naming</tag> + <item> + <p>Most Unix operating systems have adopted a simpler approach, + namely that Unicode file naming is not enforced, but by + convention. Those systems usually use UTF-8 encoding for Unicode + file names, but do not enforce it. On such a system, a file name + containing characters having code points between 128 and 255 may + be named either as plain ISO-latin-1 or using UTF-8 encoding. As + no consistency is enforced, the Erlang VM can do no consistent + translation of all file names. If the VM would automatically + select encoding based on heuristics, one could get unexpected + behavior on these systems. By default, Erlang starts in "latin1" + file name mode on such systems, meaning bytewise encoding in file + names. This allows for list representation of all file names in + the system, but, for example, a file named "Östersund.txt", will + appear in <c>file:list_dir/1</c> as either "Östersund.txt" (if + the file name was encoded in bytewise ISO-Latin-1 by the program + creating the file, or more probably as + <c>[195,150,115,116,101,114,115,117,110,100]</c>, which is a + list containing UTF-8 bytes - not what you would want... If you + on the other hand use Unicode file name translation on such a + system, non-UTF-8 file names will simply be ignored by functions + like <c>file:list_dir/1</c>. They can be retrieved with + <c>file:list_dir_all/1</c>, but wrongly encoded file names will + appear as "raw file names".</p> + + </item> + </taglist> + + <p>The Unicode file naming support was introduced with OTP release + R14B01. A VM operating in Unicode file name translation mode can + work with files having names in any language or character set (as + long as it is supported by the underlying OS and file system). The + Unicode character list is used to denote file or directory names and + if the file system content is listed, you will also get + Unicode lists as return value. The support lies in the Kernel and + STDLIB modules, why most applications (that does not explicitly + require the file names to be in the ISO-latin-1 range) will benefit + from the Unicode support without change.</p> -<p>On Operating systems with mandatory Unicode file names, this means that you more easily conform to the file names of other (non Erlang) applications, and you can also process file names that, at least on Windows, were completely inaccessible (due to having names that could not be represented in ISO-latin-1). Also you will avoid creating incomprehensible file names on MacOSX as the vfs layer of the OS will accept all your file names as UTF-8 and will not rewrite them.</p> + <p>On operating systems with mandatory Unicode file names, this + means that you more easily conform to the file names of other (non + Erlang) applications, and you can also process file names that, at + least on Windows, were completely inaccessible (due to having names + that could not be represented in ISO-latin-1). Also you will avoid + creating incomprehensible file names on MacOS X as the vfs layer of + the OS will accept all your file names as UTF-8 and will not rewrite + them.</p> -<p>For most systems, turning on Unicode file name translation is no problem even if it uses transparent file naming. Very few systems have mixed file name encodings. A consistent UTF-8 named system will work perfectly in Unicode file name mode. It is still however considered experimental in R14B01. Unicode file name translation is turned on with the <c>+fnu</c> switch to the <c>erl</c> program. If the VM is started in Unicode file name translation mode, <c>file:native_name_encoding/0</c> will return the atom <c>utf8</c>.</p> + <p>For most systems, turning on Unicode file name translation is no + problem even if it uses transparent file naming. Very few systems + have mixed file name encodings. A consistent UTF-8 named system will + work perfectly in Unicode file name mode. It was still however + considered experimental in R14B01 and is still not the default on + such systems. Unicode file name translation is turned on with the + <c>+fnu</c> switch to the On Linux, a VM started without explicitly + stating the file name translation mode will default to <c>latin1</c> + as the native file name encoding. On Windows and MacOS X, the + default behavior is that of Unicode file name translation, why the + <c>file:native_name_encoding/0</c> by default returns <c>utf8</c> on + those systems (the fact that Windows actually does not use UTF-8 on + the file system level can safely be ignored by the Erlang + programmer). The default behavior can, as stated before, be + changed using the <c>+fnu</c> or <c>+fnl</c> options to the VM, see + the <seealso marker="erts:erl"><c>erl</c></seealso> program. If the + VM is started in Unicode file name translation mode, + <c>file:native_name_encoding/0</c> will return the atom + <c>utf8</c>. The <c>+fnu</c> switch can be followed by <c>w</c>, + <c>i</c> or <c>e</c>, to control how wrongly encoded file names are + to be reported. <c>w</c> means that a warning is sent to the + <c>error_logger</c> whenever a wrongly encoded file name is + "skipped" in directory listings, <c>i</c> means that those wrongly + encoded file names are silently ignored and <c>e</c> means that the + API function will return an error whenever a wrongly encoded file + (or directory) name is encountered. <c>w</c> is the default. Note + that <c>file:read_link/1</c> will always return an error if the link + points to an invalid file name.</p> -<p>In Unicode file name mode, file names given to the BIF <c>open_port/2</c> with the option <c>{spawn_executable,...}</c> are also interpreted as Unicode. So is the parameter list given in the <c>args</c> option available when using <c>spawn_executable</c>. The UTF-8 translation of arguments can be avoided using binaries, see the discussion about raw file names below.</p> + <p>In Unicode file name mode, file names given to the BIF + <c>open_port/2</c> with the option <c>{spawn_executable,...}</c> are + also interpreted as Unicode. So is the parameter list given in the + <c>args</c> option available when using <c>spawn_executable</c>. The + UTF-8 translation of arguments can be avoided using binaries, see + the discussion about raw file names below.</p> -<p>It is worth noting that the file <c>encoding</c> options given when opening a file has nothing to do with the file <em>name</em> encoding convention. You can very well open files containing UTF-8 but having file names in ISO-latin-1 or vice versa.</p> + <p>It is worth noting that the file <c>encoding</c> options given + when opening a file has nothing to do with the file <em>name</em> + encoding convention. You can very well open files containing data + encoded in UTF-8 but having file names in bytewise (<c>latin1</c>) encoding + or vice versa.</p> -<note>Erlang drivers and NIF shared objects still can not be named with names containing codepoints beyond 127. This is a known limitation to be removed in a future release. Erlang modules however can, but it is definitely not a good idea and is still considered experimental.</note> + <note><p>Erlang drivers and NIF shared objects still can not be + named with names containing code points beyond 127. This is a known + limitation to be removed in a future release. Erlang modules however + can, but it is definitely not a good idea and is still considered + experimental.</p></note> <section> -<title>Notes about raw file names and automatic file name conversion</title> -<p>Raw file names is introduced together with Unicode file name support in erts-5.8.2 (OTP R14B01). The reason "raw file names" is introduced in the system is to be able to consistently represent file names given in different encodings on the same system. Having the VM automatically translate a file name that is not in UTF-8 to a list of Unicode characters might seem practical, but this would open up for both duplicate file names and other inconsistent behavior. Consider a directory containing a file named "bj�rn" in ISO-latin-1, while the Erlang VM is operating in Unicode file name mode (and therefore expecting UTF-8 file naming). The ISO-latin-1 name is not valid UTF-8 and one could be tempted to think that automatic conversion in for example <c>file:list_dir/1</c> is a good idea. But what would happen if we later tried to open the file and have the name as a Unicode list (magically converted from the ISO-latin-1 file name)? The VM will convert the file name given to UTF-8, as this is the encoding expected. Effectively this means trying to open the file named <<"bj�rn"/utf8>>. This file does not exist, and even if it existed it would not be the same file as the one that was listed. We could even create two files named "bj�rn", one named in the UTF-8 encoding and one not. If <c>file:list_dir/1</c> would automatically convert the ISO-latin-1 file name to a list, we would get two identical file names as the result. To avoid this, we need to differentiate between file names being properly encoded according to the Unicode file naming convention (i.e. UTF-8) and file names being invalid under the encoding. This is done by representing invalid encoding as "raw" file names, i.e. as binaries.</p> -<p>The core system of Erlang (kernel and stdlib) accepts raw file names except for loadable drivers and executables invoked using <c>open_port({spawn, ...} ...)</c>. <c>open_port({spawn_executable, ...} ...)</c> however does accept them. As mentioned earlier, the arguments given in the option list to <c>open_port({spawn_executable, ...} ...)</c> undergo the same conversion as the file names, meaning that the executable will be provided with arguments in UTF-8 as well. This translation is avoided consistently with how the file names are treated, by giving the argument as a binary.</p> -<p>To force Unicode file name translation mode on systems where this is not the default is considered experimental in OTP R14B01 due to the raw file names possibly being a new experience to the programmer and that the non core applications of OTP are not tested for compliance with raw file names yet. Unicode file name translation is expected to be default in future releases.</p> -<p>If working with raw file names, one can still conform to the encoding convention of the Erlang VM by using the <c>file:native_name_encoding/0</c> function, which returns either the atom <c>latin1</c> or the atom <c>utf8</c> depending on the file name translation mode. On Linux, a VM started without explicitly stating the file name translation mode will default to <c>latin1</c> as the native file name encoding, why file names on the disk encoded as UTF-8 will be returned as a list of the names interpreted as ISO-latin-1. The "UTF-8 list" is not a practical type for displaying or operating on in Erlang, but it is backward compatible and usable in all functions requiring a file name. On Windows and MacOSX, the default behavior is that of file name translation, why the <c>file:native_name_encoding/0</c> by default returns <c>utf8</c> on those systems (the fact that Windows actually does not use UTF-8 on the file system level can safely be ignored by the Erlang programmer). The default behavior can be changed using the <c>+fnu</c> or <c>+fnl</c> options to the VM, see the <c>erl</c> command manual page.</p> -<p>Even if you are operating without Unicode file naming translation automatically done by the VM, you can access and create files with names in UTF-8 encoding by using raw file names encoded as UTF-8. Enforcing the UTF-8 encoding regardless of the mode the Erlang VM is started in might, in some circumstances be a good idea, as the convention of using UTF-8 file names is spreading.</p> + <title>Notes About Raw File Names</title> + + <p>Raw file names were introduced together with Unicode file name + support in erts-5.8.2 (OTP R14B01). The reason "raw file + names" was introduced in the system was to be able to + consistently represent file names given in different encodings on + the same system. Having the VM automatically translate a file name + that is not in UTF-8 to a list of Unicode characters might seem + practical, but this would open up for both duplicate file names and + other inconsistent behavior. Consider a directory containing a file + named "björn" in ISO-latin-1, while the Erlang VM is + operating in Unicode file name mode (and therefore expecting UTF-8 + file naming). The ISO-latin-1 name is not valid UTF-8 and one could + be tempted to think that automatic conversion in for example + <c>file:list_dir/1</c> is a good idea. But what would happen if we + later tried to open the file and have the name as a Unicode list + (magically converted from the ISO-latin-1 file name)? The VM will + convert the file name given to UTF-8, as this is the encoding + expected. Effectively this means trying to open the file named + <<"björn"/utf8>>. This file does not exist, + and even if it existed it would not be the same file as the one that + was listed. We could even create two files named "björn", + one named in the UTF-8 encoding and one not. If + <c>file:list_dir/1</c> would automatically convert the ISO-latin-1 + file name to a list, we would get two identical file names as the + result. To avoid this, we need to differentiate between file names + being properly encoded according to the Unicode file naming + convention (i.e. UTF-8) and file names being invalid under the + encoding. By the common <c>file:list_dir/1</c> function, the wrongly + encoded file names are simply ignored in Unicode file name + translation mode, but by the <c>file:list_dir_all/1</c> function, + the file names with invalid encoding are returned as "raw" + file names, i.e. as binaries.</p> + + <p>The Erlang <c>file</c> module accepts raw file names as + input. <c>open_port({spawn_executable, ...} ...)</c> also accepts + them. As mentioned earlier, the arguments given in the option list + to <c>open_port({spawn_executable, ...} ...)</c> undergo the same + conversion as the file names, meaning that the executable will be + provided with arguments in UTF-8 as well. This translation is + avoided consistently with how the file names are treated, by giving + the argument as a binary.</p> + + <p>To force Unicode file name translation mode on systems where this + is not the default was considered experimental in OTP R14B01 due to + the fact that the initial implementation did not ignore wrongly + encoded file names, so that raw file names could spread unexpectedly + throughout the system. Beginning with R16B, the wrongly encoded file + names are only retrieved by special functions + (e.g. <c>file:list_dir_all/1</c>), so the impact on existing code is + much lower, why it is now supported. Unicode file name translation + is expected to be default in future releases.</p> + + <p>Even if you are operating without Unicode file naming translation + automatically done by the VM, you can access and create files with + names in UTF-8 encoding by using raw file names encoded as + UTF-8. Enforcing the UTF-8 encoding regardless of the mode the + Erlang VM is started in might, in some circumstances be a good idea, + as the convention of using UTF-8 file names is spreading.</p> </section> <section> -<title>Notes about MacOSX</title> -<p>MacOSXs vfs layer enforces UTF-8 file names in a quite aggressive way. Older versions did this by simply refusing to create non UTF-8 conforming file names, while newer versions replace offending bytes with the sequence "%HH", where HH is the original character in hexadecimal notation. As Unicode translation is enabled by default on MacOSX, the only way to come up against this is to either start the VM with the <c>+fnl</c> flag or to use a raw file name in <c>latin1</c> encoding. In that case, the file can not be opened with the same name as the one used to create this. The problem is by design in newer versions of MacOSX.</p> -<p>MacOSX also reorganizes the names of files so that the representation of accents etc is denormalized, i.e. the character <c>�</c> is represented as the codepoints [111,776], where 111 is the character <c>o</c> and 776 is a special accent character. This type of denormalized Unicode is otherwise very seldom used and Erlang normalizes those file names on retrieval, so that denormalized file names is not passed up to the Erlang application. In Erlang the file name "bj�rn" is retrieved as [98,106,246,114,110], not as [98,106,117,776,114,110], even though the file system might think differently.</p> + <title>Notes About MacOS X</title> + <p>MacOS X's vfs layer enforces UTF-8 file names in a quite + aggressive way. Older versions did this by simply refusing to create + non UTF-8 conforming file names, while newer versions replace + offending bytes with the sequence "%HH", where HH is the + original character in hexadecimal notation. As Unicode translation + is enabled by default on MacOS X, the only way to come up against + this is to either start the VM with the <c>+fnl</c> flag or to use a + raw file name in bytewise (<c>latin1</c>) encoding. If using a raw + filename, with a bytewise encoding containing characters between 127 + and 255, to create a file, the file can not be opened using the same + name as the one used to create it. There is no remedy for this + behaviour, other than keeping the file names in the right + encoding.</p> + + <p>MacOS X also reorganizes the names of files so that the + representation of accents etc is using the "combining characters", + i.e. the character <c>ö</c> is represented as the code points + [111,776], where 111 is the character <c>o</c> and 776 is the + special accent character "combining diaeresis". This way of + normalizing Unicode is otherwise very seldom used and Erlang + normalizes those file names in the opposite way upon retrieval, so + that file names using combining accents are not passed up to the + Erlang application. In Erlang the file name "björn" is + retrieved as [98,106,246,114,110], not as [98,106,117,776,114,110], + even though the file system might think differently. The + normalization into combining accents are redone when actually + accessing files, so this can usually be ignored by the Erlang + programmer.</p> </section> </section> <section> -<title>Unicode in environment variables and parameters</title> -<p>Environment variables and their interpretation is handled much in the same way as file names. If Unicode file names are enabled, environment variables as well as parameters to the Erlang VM are expected to be in Unicode.</p> -<p>If Unicode file names are enabled, the calls to <seealso marker="kernel:os#getenv/0"><c>os:getenv/0</c></seealso>, <seealso marker="kernel:os#getenv/1"><c>os:getenv/1</c></seealso> and <seealso marker="kernel:os#putenv/2"><c>os:putenv/2</c></seealso> will handle Unicode strings. On Unix-like platforms, the built-in functions will translate environment variables in UTF-8 to/from Unicode strings, possibly with codepoints > 255. On Windows the Unicode versions of the environment system API will be used, also allowing for codepoints > 255.</p> -<p>On Unix-like operating systems, parameters are expected to be UTF-8 without translation if Unicode file names are enabled.</p> + <title>Unicode in Environment and Parameters</title> + <p>Environment variables and their interpretation is handled much in + the same way as file names. If Unicode file names are enabled, + environment variables as well as parameters to the Erlang VM are + expected to be in Unicode.</p> + <p>If Unicode file names are enabled, the calls to + <seealso marker="kernel:os#getenv/0"><c>os:getenv/0</c></seealso>, + <seealso marker="kernel:os#getenv/1"><c>os:getenv/1</c></seealso> and + <seealso marker="kernel:os#putenv/2"><c>os:putenv/2</c></seealso> + will handle Unicode strings. On Unix-like platforms, the built-in + functions will translate environment variables in UTF-8 to/from + Unicode strings, possibly with code points > 255. On Windows the + Unicode versions of the environment system API will be used, also + allowing for code points > 255.</p> + <p>On Unix-like operating systems, parameters are expected to be + UTF-8 without translation if Unicode file names are enabled.</p> </section> <section> -<title>Unicode-aware modules</title> -<p>Most of the modules in Erlang/OTP are of course Unicode-unaware in the sense that they have no notion of Unicode and really shouldn't have. Typically they handle non-textual or byte-oriented data (like <c>gen_tcp</c> etc).</p> -<p>Modules that actually handle textual data (like <c>io_lib</c>, <c>string</c> etc) are sometimes subject to conversion or extension to be able to handle Unicode characters.</p> -<p>Fortunately, most textual data has been stored in lists and range checking has been sparse, why modules like <c>string</c> works well for Unicode lists with little need for conversion or extension.</p> -<p>Some modules are however changed to be explicitly Unicode-aware. These modules include:</p> -<taglist> -<tag><c>unicode</c></tag> -<item> -<p>The module <seealso marker="stdlib:unicode">unicode</seealso> is obviously Unicode-aware. It contains functions for conversion between different Unicode formats as well as some utilities for identifying byte order marks. Few programs handling Unicode data will survive without this module.</p> -</item> -<tag><c>io</c></tag> -<item> -<p>The <seealso marker="stdlib:io">io</seealso> module has been extended along with the actual I/O-protocol to handle Unicode data. This means that several functions require binaries to be in UTF-8 and there are modifiers to formatting control sequences to allow for outputting of Unicode strings.</p> -</item> -<tag><c>file</c>, <c>group</c> and <c>user</c></tag> -<item> -<p>I/O-servers throughout the system are able both to handle Unicode data and has options for converting data upon actual output or input to/from the device. As shown earlier, the <seealso marker="stdlib:shell">shell</seealso> has support for Unicode terminals and the <seealso marker="kernel:file">file</seealso> module allows for translation to and from various Unicode formats on disk.</p> -<p>The actual reading and writing of files with Unicode data is however not best done with the <c>file</c> module as its interface is byte oriented. A file opened with a Unicode encoding (like UTF-8), is then best read or written using the <seealso marker="stdlib:io">io</seealso> module.</p> -</item> -<tag><c>re</c></tag> -<item> -<p>The <seealso marker="stdlib:re">re</seealso> module allows for matching Unicode strings as a special option. As the library is actually centered on matching in binaries, the Unicode support is UTF-8-centered.</p> -</item> -<tag><c>wx</c></tag> -<item> -<p>The <seealso marker="wx:wx">wx</seealso> graphical library has extensive support for Unicode text</p> -</item> -</taglist> -<p>The module <seealso marker="stdlib:string">string</seealso> works perfect for Unicode strings as well as for ISO-latin-1 strings with the exception of the language-dependent <seealso marker="stdlib:string#to_upper/1">to_upper</seealso> and <seealso marker="stdlib:string#to_lower/1">to_lower</seealso> functions, which are only correct for the ISO-latin-1 character set. Actually they can never function correctly for Unicode characters in their current form, there are language and locale issues as well as multi-character mappings to consider when conversion text between cases. Converting case in an international environment is a big subject not yet addressed in OTP.</p> + <title>Unicode-aware Modules</title> + <p>Most of the modules in Erlang/OTP are of course Unicode-unaware + in the sense that they have no notion of Unicode and really should + not have. Typically they handle non-textual or byte-oriented data + (like <c>gen_tcp</c> etc).</p> + <p>Modules that actually handle textual data (like <c>io_lib</c>, + <c>string</c> etc) are sometimes subject to conversion or extension + to be able to handle Unicode characters.</p> + <p>Fortunately, most textual data has been stored in lists and range + checking has been sparse, why modules like <c>string</c> works well + for Unicode lists with little need for conversion or extension.</p> + <p>Some modules are however changed to be explicitly + Unicode-aware. These modules include:</p> + <taglist> + <tag><c>unicode</c></tag> + <item> + <p>The module <seealso marker="stdlib:unicode"><c>unicode</c></seealso> + is obviously Unicode-aware. It contains functions for conversion + between different Unicode formats as well as some utilities for + identifying byte order marks. Few programs handling Unicode data + will survive without this module.</p> + </item> + <tag><c>io</c></tag> + <item> + <p>The <seealso marker="stdlib:io"><c>io</c></seealso> module has been + extended along with the actual I/O-protocol to handle Unicode + data. This means that several functions require binaries to be + in UTF-8 and there are modifiers to formatting control sequences + to allow for outputting of Unicode strings.</p> + </item> + <tag><c>file</c>, <c>group</c>, <c>user</c></tag> + <item> + <p>I/O-servers throughout the system are able to handle + Unicode data and has options for converting data upon actual + output or input to/from the device. As shown earlier, the + <seealso marker="stdlib:shell"><c>shell</c></seealso> has support for + Unicode terminals and the <seealso + marker="kernel:file"><c>file</c></seealso> module allows for + translation to and from various Unicode formats on disk.</p> + <p>The actual reading and writing of files with Unicode data is + however not best done with the <c>file</c> module as its + interface is byte oriented. A file opened with a Unicode + encoding (like UTF-8), is then best read or written using the + <seealso marker="stdlib:io"><c>io</c></seealso> module.</p> + </item> + <tag><c>re</c></tag> + <item> + <p>The <seealso marker="stdlib:re"><c>re</c></seealso> module allows + for matching Unicode strings as a special option. As the library + is actually centered on matching in binaries, the Unicode + support is UTF-8-centered.</p> + </item> + <tag><c>wx</c></tag> + <item> + <p>The <seealso marker="wx:wx"><c>wx</c></seealso> graphical library + has extensive support for Unicode text</p> + </item> + </taglist> + <p>The module <seealso + marker="stdlib:string"><c>string</c></seealso> works perfectly for + Unicode strings as well as for ISO-latin-1 strings with the + exception of the language-dependent <seealso + marker="stdlib:string#to_upper/1"><c>to_upper</c></seealso> and + <seealso marker="stdlib:string#to_lower/1"><c>to_lower</c></seealso> + functions, which are only correct for the ISO-latin-1 character + set. Actually they can never function correctly for Unicode + characters in their current form, as there are language and locale + issues as well as multi-character mappings to consider when + converting text between cases. Converting case in an international + environment is a big subject not yet addressed in OTP.</p> </section> <section> -<title>Unicode recipes</title> -<p>When starting with Unicode, one often stumbles over some common issues. I try to outline some methods of dealing with Unicode data in this section.</p> + <title>Unicode Data in Files</title> + <p>The fact that Erlang as such can handle Unicode data in many forms + does not automatically mean that the content of any file can be + Unicode text. The external entities such as ports or I/O-servers are + not generally Unicode capable.</p> + <p>Ports are always byte oriented, so before sending data that you + are not sure is bytewise encoded to a port, make sure to encode it + in a proper Unicode encoding. Sometimes this will mean that only + part of the data shall be encoded as e.g. UTF-8, some parts may be + binary data (like a length indicator) or something else that shall + not undergo character encoding, so no automatic translation is + present.</p> + <p>I/O-servers behave a little differently. The I/O-servers connected + to terminals (or stdout) can usually cope with Unicode data + regardless of the <c>encoding</c> option. This is convenient when + one expects a modern environment but do not want to crash when + writing to a archaic terminal or pipe. Files on the other hand are + more picky. A file can have an encoding option which makes it + generally usable by the io-module (e.g. <c>{encoding,utf8}</c>), but + is by default opened as a byte oriented file. The <seealso + marker="kernel:file"><c>file</c></seealso> module is byte oriented, why only + ISO-Latin-1 characters can be written using that module. The + <seealso marker="stdlib:io"><c>io</c></seealso> module is the one to use if + Unicode data is to be output to a file with other <c>encoding</c> + than <c>latin1</c> (a.k.a. bytewise encoding). It is slightly + confusing that a file opened with + e.g. <c>file:open(Name,[read,{encoding,utf8}])</c>, cannot be + properly read using <c>file:read(File,N)</c> but you have to use the + <c>io</c> module to retrieve the Unicode data from it. The reason is + that <c>file:read</c> and <c>file:write</c> (and friends) are purely + byte oriented, and should so be, as that is the way to access + files other than text files - byte by byte. Just as with ports, you + can of course write encoded data into a file by "manually" converting + the data to the encoding of choice (using the <seealso + marker="stdlib:unicode"><c>unicode</c></seealso> module or the bit syntax) + and then output it on a bytewise encoded (<c>latin1</c>) file.</p> + <p>The rule of thumb is that the <seealso + marker="kernel:file"><c>file</c></seealso> module should be used for files + opened for bytewise access (<c>{encoding,latin1}</c>) and the + <seealso marker="stdlib:io"><c>io</c></seealso> module should be used when + accessing files with any other encoding + (e.g. <c>{encoding,uf8}</c>).</p> + + <p>Functions reading Erlang syntax from files generally recognize + the <c>coding:</c> comment and can therefore handle Unicode data on + input. When writing Erlang Terms to a file, you should insert + such comments when applicable:</p> + <pre> +$ <input>erl +fna +pc unicode</input> +Erlang R16B (erts-5.10.1) [source] [async-threads:0] [hipe] [kernel-poll:false] + +Eshell V5.10.1 (abort with ^G) +1> <input>file:write_file("test.term",<<"%% coding: utf-8\n[{\"Юникод\",4711}].\n"/utf8>>).</input> +ok +2> <input>file:consult("test.term").</input> +{ok,[[{"Юникод",4711}]]} + </pre> +</section> +<section> + <title><marker id="unicode_options_summary"/>Summary of Options</title> + <p>The Unicode support is controlled by both command line switches, + some standard environment variables and the version of OTP you are + using. Most options affect mainly the way Unicode data is displayed, + not the actual functionality of the API's in the standard + libraries. This means that Erlang programs usually do not + need to concern themselves with these options, they are more for the + development environment. An Erlang program can be written so that it + works well regardless of the type of system or the Unicode options + that are in effect.</p> + + <p>Here follows a summary of the settings affecting Unicode:</p> + <taglist> + <tag>The <c>LANG</c> and <c>LC_CTYPE</c> environment variables</tag> + <item> + <p>The language setting in the OS mainly affects the shell. The + terminal (i.e. the group leader) will operate with <c>{encoding, + unicode}</c> only if the environment tells it that UTF-8 is + allowed. This setting should correspond to the actual terminal + you are using.</p> + <p>The environment can also affect file name interpretation, if + Erlang is started with the <c>+fna</c> flag.</p> + <p>You can check the setting of this by calling + <c>io:getopts()</c>, which will give you an option list + containing <c>{encoding,unicode}</c> or + <c>{encoding,latin1}</c>.</p> + </item> + <tag>The <c>+pc </c>{<c>unicode</c>|<c>latin1</c>} flag to + <seealso marker="erts:erl"><c>erl(1)</c></seealso></tag> + <item> + <p>This flag affects what is interpreted as string data when + doing heuristic string detection in the shell and in + <c>io</c>/<c>io_lib:format</c> with the <c>"~tp"</c> and + <c>~tP</c> formatting instructions, as described above.</p> + <p>You can check this option by calling io:printable_range/0, + which in R16B will return <c>unicode</c> or <c>latin1</c>. To be + compatible with future (expected) extensions to the settings, + one should rather use <c>io_lib:printable_list/1</c> to check if + a list is printable according to the setting. That function will + take into account new possible settings returned from + <c>io:printable_range/0</c>.</p> + </item> + <tag>The <c>+fn</c>{<c>l</c>|<c>a</c>|<c>u</c>} + [{<c>w</c>|<c>i</c>|<c>e</c>}] + flag to <seealso marker="erts:erl"><c>erl(1)</c></seealso></tag> + <item> + <p>This flag affects how the file names are to be interpreted. On + operating systems with transparent file naming, this has to be + specified to allow for file naming in Unicode characters (and + for correct interpretation of file names containing characters + > 255.</p> + <p><c>+fnl</c> means bytewise interpretation of file names, which + was the usual way to represent ISO-Latin-1 file names before + UTF-8 file naming got widespread. This is the default on all + Unix-like operating systems except MacOS X.</p> + <p><c>+fnu</c> means that file names are encoded in UTF-8, which + is nowadays the common scheme (although not enforced).</p> + <p><c>+fna</c> means that you automatically select between + <c>+fnl</c> and <c>+fnu</c>, based on the <c>LANG</c> and + <c>LC_CTYPE</c> environment variables. This is optimistic + heuristics indeed, nothing enforces a user to have a terminal + with the same encoding as the file system, but usually, this is + the case. This might be the default behavior in a future + release.</p> + + <p>The file name translation mode can be read with the + <c>file:native_name_encoding/0</c> function, which returns + <c>latin1</c> (meaning bytewise encoding) or <c>utf8</c>.</p> + </item> + <tag><seealso marker="stdlib:epp#default_encoding/0"> + <c>epp:default_encoding/0</c></seealso></tag> + <item> + <p>This function returns the default encoding for Erlang source + files (if no encoding comment is present) in the currently + running release. For R16 this returns <c>latin1</c> (meaning + bytewise encoding). In R17 and forward it is expected to return + <c>utf8</c>.</p> + <p>The encoding of each file can be specified using comments as + described in + <seealso marker="stdlib:epp#encoding"><c>epp(3)</c></seealso>.</p> + </item> + <tag><seealso marker="stdlib:io#setopts/1"><c>io:setopts/</c>{<c>1</c>,<c>2</c>}</seealso> and the <c>-oldshell</c>/<c>-noshell</c> flags.</tag> + <item> + <p>When Erlang is started with <c>-oldshell</c> or + <c>-noshell</c>, the I/O-server for <c>standard_io</c> is default + set to bytewise encoding, while an interactive shell defaults to + what the environment variables says.</p> + <p>With the <c>io:setopts/2</c> function you can set the + encoding of a file or other I/O-server. This can also be set when + opening a file. Setting the terminal (or other + <c>standard_io</c> server) unconditionally to the option + <c>{encoding,utf8}</c> will for example make UTF-8 encoded characters + being written to the device regardless of how Erlang was started or + the users environment.</p> + <p>Opening files with <c>encoding</c> option is convenient when + writing or reading text files in a known encoding.</p> + <p>You can retrieve the <c>encoding</c> setting for an I/O-server + using <seealso + marker="stdlib:io#getopts/1"><c>io:getopts()</c></seealso>.</p> + </item> + </taglist> +</section> <section> -<title>Byte order marks</title> -<p>A common method of identifying encoding in text-files is to put a byte order mark (BOM) first in the file. The BOM is the codepoint 16#FEFF encoded in the same way as the rest of the file. If such a file is to be read, the first few bytes (depending on encoding) is not part of the actual text. This code outlines how to open a file which is believed to have a BOM and set the files encoding and position for further sequential reading (preferably using the <seealso marker="stdlib:io">io</seealso> module). Note that error handling is omitted from the code:</p> + <title>Recipes</title> + <p>When starting with Unicode, one often stumbles over some common + issues. I try to outline some methods of dealing with Unicode data + in this section.</p> + <section> + <title>Byte Order Marks</title> + <p>A common method of identifying encoding in text-files is to put + a byte order mark (BOM) first in the file. The BOM is the + code point 16#FEFF encoded in the same way as the rest of the + file. If such a file is to be read, the first few bytes (depending + on encoding) is not part of the actual text. This code outlines + how to open a file which is believed to have a BOM and set the + files encoding and position for further sequential reading + (preferably using the <seealso marker="stdlib:io"><c>io</c></seealso> + module). Note that error handling is omitted from the code:</p> <code> open_bom_file_for_reading(File) -> {ok,F} = file:open(File,[read,binary]), @@ -261,8 +1119,15 @@ open_bom_file_for_reading(File) -> io:setopts(F,[{encoding,Type}]), {ok,F}. </code> -<p>The <c>unicode:bom_to_encoding/1</c> function identifies the encoding from a binary of at least four bytes. It returns, along with an term suitable for setting the encoding of the file, the actual length of the BOM, so that the file position can be set accordingly. Note that <c>file:position</c> always works on byte-offsets, so that the actual byte-length of the BOM is needed.</p> -<p>To open a file for writing and putting the BOM first is even simpler:</p> + <p>The <c>unicode:bom_to_encoding/1</c> function identifies the + encoding from a binary of at least four bytes. It returns, along + with an term suitable for setting the encoding of the file, the + actual length of the BOM, so that the file position can be set + accordingly. Note that <c>file:position/2</c> always works on + byte-offsets, so that the actual byte-length of the BOM is + needed.</p> + <p>To open a file for writing and putting the BOM first is even + simpler:</p> <code> open_bom_file_for_writing(File,Encoding) -> {ok,F} = file:open(File,[write,binary]), @@ -270,29 +1135,95 @@ open_bom_file_for_writing(File,Encoding) -> io:setopts(F,[{encoding,Encoding}]), {ok,F}. </code> -<p>In both cases the file is then best processed using the <c>io</c> module, as the functions in <c>io</c> can handle codepoints beyond the ISO-latin-1 range.</p> -</section> -<section> -<title>Formatted input and output</title> -<p>When reading and writing to Unicode-aware entities, like the User or a file opened for Unicode translation, you will probably want to format text strings using the functions in <seealso marker="stdlib:io">io</seealso> or <seealso marker="stdlib:io_lib">io_lib</seealso>. For backward compatibility reasons, these functions don't accept just any list as a string, but require e special "translation modifier" when working with Unicode texts. The modifier is "t". When applied to the "s" control character in a formatting string, it accepts all Unicode codepoints and expect binaries to be in UTF-8:</p> -<pre> -1> <input>io:format("~ts~n",[<<"���"/utf8>>]).</input> -��� -ok -2> <input>io:format("~s~n",[<<"���"/utf8>>]).</input> + <p>In both cases the file is then best processed using the + <c>io</c> module, as the functions in <c>io</c> can handle code + points beyond the ISO-latin-1 range.</p> + </section> + <section> + <title>Formatted I/O</title> + <p>When reading and writing to Unicode-aware entities, like the + User or a file opened for Unicode translation, you will probably + want to format text strings using the functions in <seealso + marker="stdlib:io"><c>io</c></seealso> or <seealso + marker="stdlib:io_lib"><c>io_lib</c></seealso>. For backward + compatibility reasons, these functions do not accept just any list + as a string, but require a special <em>translation modifier</em> + when working with Unicode texts. The modifier is <c>t</c>. When + applied to the <c>s</c> control character in a formatting string, + it accepts all Unicode code points and expect binaries to be in + UTF-8:</p> + <pre> +1> <input>io:format("~ts~n",[<<"åäö"/utf8>>]).</input> åäö +ok +2> <input>io:format("~s~n",[<<"åäö"/utf8>>]).</input> +åäö ok</pre> -<p>Obviously the second <c>io:format</c> gives undesired output because the UTF-8 binary is not in latin1. Because ISO-latin-1 is still the defined character set of Erlang, the non prefixed "s" control character expects ISO-latin-1 in binaries as well as lists.</p> -<p>As long as the data is always lists, the "t" modifier can be used for any string, but when binary data is involved, care must be taken to make the tight choice of formatting characters.</p> -<p>The function <c>format</c> in <c>io_lib</c> behaves similarly. This function is defined to return a deep list of characters and the output could easily be converted to binary data for outputting on a device of any kind by a simple <c>erlang:list_to_binary</c>. When the translation modifier is used, the list can however contain characters that cannot be stored in one byte. The call to <c>erlang:list_to_binary</c> will in that case fail. However, if the io_server you want to communicate with is Unicode-aware, the list returned can still be used directly:</p> -<image file="ushell3.gif"><icaption>io_lib:format with Unicode translation</icaption></image> -<p>The Unicode string is returned as a Unicode list, why the return value of <c>io_lib:format</c> no longer qualifies as a regular Erlang string (the function <seealso marker="stdlib:io_lib#deep_char_list/1">io_lib:deep_char_list</seealso> will, as an example, return <c>false</c>). The Unicode list is however valid input to the <seealso marker="stdlib:io#put_chars/2">io:put_chars</seealso> function, so data can be output on any Unicode capable device anyway. If the device is a terminal, characters will be output in the \x{H ...} format if encoding is <c>latin1</c> otherwise in UTF-8 (for the non-interactive terminal - "oldshell" or "noshell") or whatever is suitable to show the character properly (for an interactive terminal - the regular shell). The bottom line is that you can always send Unicode data to the <c>standard_io</c> device. Files will however only accept Unicode codepoints beyond ISO-latin-1 if <c>encoding</c> is set to something else than <c>latin1</c>.</p> -</section> -<section> -<title>Heuristic identification of UTF-8</title> -<p>While it's strongly encouraged that the actual encoding of characters in binary data is known prior to processing, that is not always possible. On a typical Linux® system, there is a mix of UTF-8 and ISO-latin-1 text files and there are seldom any BOM's in the files to identify them.</p> -<p>UTF-8 is designed in such a way that ISO-latin-1 characters with numbers beyond the 7-bit ASCII range are seldom considered valid when decoded as UTF-8. Therefore one can usually use heuristics to determine if a file is in UTF-8 or if it is encoded in ISO-latin-1 (one byte per character) encoding. The <c>unicode</c> module can be used to determine if data can be interpreted as UTF-8:</p> -<code> + <p>Obviously the second <c>io:format/2</c> gives undesired output + because the UTF-8 binary is not in latin1. For backward + compatibility, the non prefixed <c>s</c> control character expects + bytewise encoded ISO-latin-1 characters in binaries and lists + containing only code points < 256.</p> + <p>As long as the data is always lists, the <c>t</c> modifier can + be used for any string, but when binary data is involved, care + must be taken to make the right choice of formatting characters. A + bytewise encoded binary will also be interpreted as a string and + printed even when using <c>~ts</c>, but it might be mistaken for a + valid UTF-8 string and one should therefore avoid using the + <c>~ts</c> control if the binary contains bytewise encoded + characters and not UTF-8.</p> + <p>The function <c>format/2</c> in <c>io_lib</c> behaves + similarly. This function is defined to return a deep list of + characters and the output could easily be converted to binary data + for outputting on a device of any kind by a simple + <c>erlang:list_to_binary/1</c>. When the translation modifier is + used, the list can however contain characters that cannot be + stored in one byte. The call to <c>erlang:list_to_binary/1</c> + will in that case fail. However, if the I/O server you want to + communicate with is Unicode-aware, the list returned can still be + used directly:</p> +<pre> +$ <input>erl +pc unicode</input> +Erlang R16B (erts-5.10.1) [source] [async-threads:0] [hipe] [kernel-poll:false] + +Eshell V5.10.1 (abort with ^G) +1> <input>io_lib:format("~ts~n", ["Γιούνικοντ"]).</input> +["Γιούνικοντ","\n"] +2> <input>io:put_chars(io_lib:format("~ts~n", ["Γιούνικοντ"])).</input> +Γιούνικοντ +ok</pre> + <p>The Unicode string is returned as a Unicode list, which is + recognized as such since the Erlang shell uses the Unicode + encoding (and is started with all Unicode characters considered + printable). The Unicode list is valid input to the <seealso + marker="stdlib:io#put_chars/2"><c>io:put_chars/2</c></seealso> function, + so data can be output on any Unicode capable device. If the device + is a terminal, characters will be output in the <c>\x{</c>H + ...<c>}</c> format if encoding is <c>latin1</c> otherwise in UTF-8 + (for the non-interactive terminal - "oldshell" or "noshell") or + whatever is suitable to show the character properly (for an + interactive terminal - the regular shell). The bottom line is that + you can always send Unicode data to the <c>standard_io</c> + device. Files will however only accept Unicode code points beyond + ISO-latin-1 if <c>encoding</c> is set to something else than + <c>latin1</c>.</p> + </section> + <section> + <title>Heuristic Identification of UTF-8</title> + <p>While it is + strongly encouraged that the actual encoding of characters in + binary data is known prior to processing, that is not always + possible. On a typical Linux system, there is a mix of UTF-8 + and ISO-latin-1 text files and there are seldom any BOM's in the + files to identify them.</p> + <p>UTF-8 is designed in such a way that ISO-latin-1 characters + with numbers beyond the 7-bit ASCII range are seldom considered + valid when decoded as UTF-8. Therefore one can usually use + heuristics to determine if a file is in UTF-8 or if it is encoded + in ISO-latin-1 (one byte per character) encoding. The + <c>unicode</c> module can be used to determine if data can be + interpreted as UTF-8:</p> + <code> heuristic_encoding_bin(Bin) when is_binary(Bin) -> case unicode:characters_to_binary(Bin,utf8,utf8) of Bin -> @@ -300,9 +1231,16 @@ heuristic_encoding_bin(Bin) when is_binary(Bin) -> _ -> latin1 end. -</code> -<p>If one does not have a complete binary of the file content, one could instead chunk through the file and check part by part. The return-tuple <c>{incomplete,Decoded,Rest}</c> from <c>unicode:characters_to_binary/{1,2,3}</c> comes in handy. The incomplete rest from one chunk of data read from the file is prepended to the next chunk and we therefore circumvent the problem of character boundaries when reading chunks of bytes in UTF-8 encoding:</p> -<code> + </code> + <p>If one does not have a complete binary of the file content, one + could instead chunk through the file and check part by part. The + return-tuple <c>{incomplete,Decoded,Rest}</c> from + <c>unicode:characters_to_binary/{1,2,3}</c> comes in handy. The + incomplete rest from one chunk of data read from the file is + prepended to the next chunk and we therefore circumvent the + problem of character boundaries when reading chunks of bytes in + UTF-8 encoding:</p> + <code> heuristic_encoding_file(FileName) -> {ok,F} = file:open(FileName,[read,binary]), loop_through_file(F,<<>>,file:read(F,1024)). @@ -320,9 +1258,12 @@ loop_through_file(F,Acc,{ok,Bin}) when is_binary(Bin) -> Res when is_binary(Res) -> loop_through_file(F,<<>>,file:read(F,1024)) end. -</code> -<p>Another option is to try to read the whole file in utf8 encoding and see if it fails. Here we need to read the file using <c>io:get_chars/3</c>, as we have to succeed in reading characters with a codepoint over 255:</p> -<code> + </code> + <p>Another option is to try to read the whole file in UTF-8 + encoding and see if it fails. Here we need to read the file using + <c>io:get_chars/3</c>, as we have to succeed in reading characters + with a code point over 255:</p> + <code> heuristic_encoding_file2(FileName) -> {ok,F} = file:open(FileName,[read,binary,{encoding,utf8}]), loop_through_file2(F,io:get_chars(F,'',1024)). @@ -333,7 +1274,68 @@ loop_through_file2(_,{error,_Err}) -> latin1; loop_through_file2(F,Bin) when is_binary(Bin) -> loop_through_file2(F,io:get_chars(F,'',1024)). -</code> -</section> + </code> + </section> + <section> + <title>Lists of UTF-8 Bytes</title> + <p>For various reasons, you may find yourself having a list of + UTF-8 bytes. This is not a regular string of Unicode characters as + each element in the list does not contain one character. Instead + you get the "raw" UTF-8 encoding that you have in binaries. This + is easily converted to a proper Unicode string by first converting + byte per byte into a binary and then converting the binary of + UTF-8 encoded characters back to a Unicode string:</p> + <code> + utf8_list_to_string(StrangeList) -> + unicode:characters_to_list(list_to_binary(StrangeList)). + </code> + </section> + <section> + <title>Double UTF-8 Encoding</title> + <p>When working with binaries, you may get the horrible "double + UTF-8 encoding", where strange characters are encoded in your + binaries or files that you did not expect. What you may have got, + is a UTF-8 encoded binary that is for the second time encoded as + UTF-8. A common situation is where you read a file, byte by byte, + but the actual content is already UTF-8. If you then convert the + bytes to UTF-8, using i.e. the <c>unicode</c> module or by + writing to a file opened with the <c>{encoding,utf8}</c> + option. You will have each <i>byte</i> in the in the input file + encoded as UTF-8, not each character of the original text (one + character may have been encoded in several bytes). There is no + real remedy for this other than being very sure of which data is + actually encoded in which format, and never convert UTF-8 data + (possibly read byte by byte from a file) into UTF-8 again.</p> + <p>The by far most common situation where this happens, is when + you get lists of UTF-8 instead of proper Unicode strings, and then + convert them to UTF-8 in a binary or on a file:</p> + <code> + wrong_thing_to_do() -> + {ok,Bin} = file:read_file("an_utf8_encoded_file.txt"), + MyList = binary_to_list(Bin), %% Wrong! It is an utf8 binary! + {ok,C} = file:open("catastrophe.txt",[write,{encoding,utf8}]), + io:put_chars(C,MyList), %% Expects a Unicode string, but get UTF-8 + %% bytes in a list! + file:close(C). %% The file catastrophe.txt contains more or less unreadable + %% garbage! + </code> + <p>Make very sure you know what a binary contains before + converting it to a string. If no other option exists, try + heuristics:</p> + <code> + if_you_can_not_know() -> + {ok,Bin} = file:read_file("maybe_utf8_encoded_file.txt"), + MyList = case unicode:characters_to_list(Bin) of + L when is_list(L) -> + L; + _ -> + binary_to_list(Bin) %% The file was bytewise encoded + end, + %% Now we know that the list is a Unicode string, not a list of UTF-8 bytes + {ok,G} = file:open("greatness.txt",[write,{encoding,utf8}]), + io:put_chars(G,MyList), %% Expects a Unicode string, which is what it gets! + file:close(G). %% The file contains valid UTF-8 encoded Unicode characters! + </code> + </section> </section> </chapter> diff --git a/lib/stdlib/doc/src/ushell1.gif b/lib/stdlib/doc/src/ushell1.gif Binary files differdeleted file mode 100644 index 7c46464fd2..0000000000 --- a/lib/stdlib/doc/src/ushell1.gif +++ /dev/null diff --git a/lib/stdlib/doc/src/ushell1.ps b/lib/stdlib/doc/src/ushell1.ps deleted file mode 100644 index 95bfebf194..0000000000 --- a/lib/stdlib/doc/src/ushell1.ps +++ /dev/null @@ -1,1196 +0,0 @@ -%!PS-Adobe-3.0 -%%Creator: GIMP PostScript file plugin V 1.17 by Peter Kirchgessner -%%Title: ushell1.ps -%%CreationDate: Mon Mar 16 09:53:27 2009 -%%DocumentData: Clean7Bit -%%LanguageLevel: 2 -%%Pages: 1 -%%BoundingBox: 14 14 468 142 -%%EndComments -%%BeginProlog -% Use own dictionary to avoid conflicts -10 dict begin -%%EndProlog -%%Page: 1 1 -% Translate for offset -14.173228346456694 14.173228346456694 translate -% Translate to begin of first scanline -0 127.55905511811024 translate -453.54330708661422 -127.55905511811024 scale -% Image geometry -640 180 8 -% Transformation matrix -[ 640 0 0 180 0 0 ] -% Strings to hold RGB-samples per scanline -/rstr 640 string def -/gstr 640 string def -/bstr 640 string def -{currentfile /ASCII85Decode filter /RunLengthDecode filter rstr readstring pop} -{currentfile /ASCII85Decode filter /RunLengthDecode filter gstr readstring pop} -{currentfile /ASCII85Decode filter /RunLengthDecode filter bstr readstring pop} -true 3 -%%BeginData: 67571 ASCII Bytes -colorimage -JP1PeJP1PeJP1L~> -JP1PeJP1PeJP1L~> -JP1PeJP1PeJP1L~> -!!e'9JNA?CJNABDJ,~> -!!e'9JNA?CJNABDJ,~> -!!e'9JNA?CJNABDJ,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$2%<!Lr-2rr]6=Dt\b^JcC<$JcD\KJ,~> -!$2%<!Lr-2rr]6=Dt\b^JcC<$JcD\KJ,~> -!$2%<!Lr-2rr]6=Dt\b^JcC<$JcD\KJ,~> -#9EjEY>Q6)=6BMbc`J;XJcC<$JcC<$W;hA~> -#9EjEY>Q6)=6BMbc`J;XJcC<$JcC<$W;hA~> -#9EjEY>Q6)=6BMbc`J;XJcC<$JcC<$W;hA~> -#T`rUH-/@H`Dbmu!A3bks+13$s+13Ks*t~> -#T`rUH-/@H`Dbmu!A3bks+13$s+13Ks*t~> -#T`rUH-/@H`Dbmu!A3bks+13$s+13Ks*t~> -#T`rEG5Y4]rp0@Z!W@cP*=4P!s-oDP9H?1krVlmMmt(Lis+13$s/>sJ~> -#T`rEG5Y4]rp0@Z!W@cP*=4P!s-oDP9H?1krVlmMmt(Lis+13$s/>sJ~> -#T`rEG5Y4]rp0@Z!W@cP*=4P!s-oDP9H?1krVlmMmt(Lis+13$s/>sJ~> -#9EjER>jY@r:g3lQA5D=$0F2Hs)Ak%s4op<rrF_?JcC<$JcC<$W;hA~> -#9EjER>jY@r:g3lQA5D=$0F2Hs)Ak%s4op<rrF_?JcC<$JcC<$W;hA~> -#9EjER>jY@r:g3lQA5D=$0F2Hs)Ak%s4op<rrF_?JcC<$JcC<$W;hA~> -!$2(="-F9Q\G-"(Q=^'2$*?/ds)G.?s5e.srrF_?JcC<$JcC<$W;hA~> -!$2(="-F9Q\G-"(Q=^'2$*?/ds)G.?s5e.srrF_?JcC<$JcC<$W;hA~> -!$2(="-F9Q\G-"(Q=^'2$*?/ds)G.?s5e.srrF_?JcC<$JcC<$W;hA~> -!$2%<"&I0WZ2">"Q8&8B"FpIOENK!9!A3bks+13$s+13Ks*t~> -!$2%<"&I0WZ2">"Q8&8B"FpIOENK!9!A3bks+13$s+13Ks*t~> -!$2%<"&I0WZ2">"Q8&8B"FpIOENK!9!A3bks+13$s+13Ks*t~> -#T`rq^Ah!RpEo\8!LAK:rrI,@q>UIImt(Lis+13$s/>sJ~> -#T`rq^Ah!RpEo\8!LAK:rrI,@q>UIImt(Lis+13$s/>sJ~> -#T`rq^Ah!RpEo\8!LAK:rrI,@q>UIImt(Lis+13$s/>sJ~> -#T`s"JSH?.CSgh+!RuStC&iMXrrI,@qu6esC(k*/JcC<$JcC<$WW.J~> -#T`s"JSH?.CSgh+!RuStC&iMXrrI,@qu6esC(k*/JcC<$JcC<$WW.J~> -#T`s"JSH?.CSgh+!RuStC&iMXrrI,@qu6esC(k*/JcC<$JcC<$WW.J~> -#9EjEgp/o#[J'V#fD`&U])M^1eE6Z.!7LkP!5F*bJcC<$JcD_LJ,~> -#9EjEgp/o#[J'V#fD`&U])M^1eE6Z.!7LkP!5F*bJcC<$JcD_LJ,~> -#9EjEgp/o#[J'V#fD`&U])M^1eE6Z.!7LkP!5F*bJcC<$JcD_LJ,~> -!$2%<!T*O$s+13$s+13+s*t~> -!$2%<!T*O$s+13$s+13+s*t~> -!$2%<!T*O$s+13$s+13+s*t~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$.X1!VbZgrrMcToD\g]rn%5Fci4"4rn%5Kp&>-Vf\#!)rr_95f\lE%"5DJ\kK!T&r;P=KmIgJZ -p&<SDo\]ZKoD[ABp@\Fcm/GW;rJ1CTgXt0Ap&>$hrn%5=kPp&~> -!$.X1!VbZgrrMcToD\g]rn%5Fci4"4rn%5Kp&>-Vf\#!)rr_95f\lE%"5DJ\kK!T&r;P=KmIgJZ -p&<SDo\]ZKoD[ABp@\Fcm/GW;rJ1CTgXt0Ap&>$hrn%5=kPp&~> -!$.X1!VbZgrrMcToD\g]rn%5Fci4"4rn%5Kp&>-Vf\#!)rr_95f\lE%"5DJ\kK!T&r;P=KmIgJZ -p&<SDo\]ZKoD[ABp@\Fcm/GW;rJ1CTgXt0Ap&>$hrn%5=kPp&~> -!Zh=)r4;sJp\t?M]=[dnrrC^M])^+RrrBt8rrCOG])^O`rrN,;oD\mfUOUkp!5JHD!:K[_!<2l- -"nM]nb];P3rrf;C6q#_Srrf>i6k8JSrrZO_6rs9d"4Ffn`;BQ8r5SL2!SZc5rrL6To`##66p3I# -rrWs'7#p%%s5ESL#i+SBs6i?D-]@Zt"M5R?7*O]k#gN&Bs2.5m(WQ.J"Fqsi7/+e&!SceorrM<; -mf3=!!7LiG"&[email protected])jpjdIXT/=<lMlA~> -!Zh=)r4;sJp\t?M]=[dnrrC^M])^+RrrBt8rrCOG])^O`rrN,;oD\mfUOUkp!5JHD!:K[_!<2l- -"nM]nb];P3rrf;C6q#_Srrf>i6k8JSrrZO_6rs9d"4Ffn`;BQ8r5SL2!SZc5rrL6To`##66p3I# -rrWs'7#p%%s5ESL#i+SBs6i?D-]@Zt"M5R?7*O]k#gN&Bs2.5m(WQ.J"Fqsi7/+e&!SceorrM<; -mf3=!!7LiG"&[email protected])jpjdIXT/=<lMlA~> -!Zh=)r4;sJp\t?M]=[dnrrC^M])^+RrrBt8rrCOG])^O`rrN,;oD\mfUOUkp!5JHD!:K[_!<2l- -"nM]nb];P3rrf;C6q#_Srrf>i6k8JSrrZO_6rs9d"4Ffn`;BQ8r5SL2!SZc5rrL6To`##66p3I# -rrWs'7#p%%s5ESL#i+SBs6i?D-]@Zt"M5R?7*O]k#gN&Bs2.5m(WQ.J"Fqsi7/+e&!SceorrM<; -mf3=!!7LiG"&[email protected])jpjdIXT/=<lMlA~> -"!.E>/H&uWGk_4?XC2t&hZ!Um6iLKpEPqeU!Nm==rrAJc@0&Z7rr3%sBt![6!U2E,rrMo!m/I&" -r*]TWq>UHor*]UHci3qFSUgA4!NC.^rrFq?p&>%u_#438a`V$#Fo21>SLO=5!Hk=9rrIX^qu6]" -qYL3mju2l3!$0bm!oI+@o`#!G9q_Fa!Ki<5rrK`?iVrtr.,4\"!ER55rrIY@bPqUQnGW@fGGa6s -!Mju/rrf?1@<TfkrrKf@p&>'T2Z3RT*U<ZT~> -"!.E>/H&uWGk_4?XC2t&hZ!Um6iLKpEPqeU!Nm==rrAJc@0&Z7rr3%sBt![6!U2E,rrMo!m/I&" -r*]TWq>UHor*]UHci3qFSUgA4!NC.^rrFq?p&>%u_#438a`V$#Fo21>SLO=5!Hk=9rrIX^qu6]" -qYL3mju2l3!$0bm!oI+@o`#!G9q_Fa!Ki<5rrK`?iVrtr.,4\"!ER55rrIY@bPqUQnGW@fGGa6s -!Mju/rrf?1@<TfkrrKf@p&>'T2Z3RT*U<ZT~> -"!.E>/H&uWGk_4?XC2t&hZ!Um6iLKpEPqeU!Nm==rrAJc@0&Z7rr3%sBt![6!U2E,rrMo!m/I&" -r*]TWq>UHor*]UHci3qFSUgA4!NC.^rrFq?p&>%u_#438a`V$#Fo21>SLO=5!Hk=9rrIX^qu6]" -qYL3mju2l3!$0bm!oI+@o`#!G9q_Fa!Ki<5rrK`?iVrtr.,4\"!ER55rrIY@bPqUQnGW@fGGa6s -!Mju/rrf?1@<TfkrrKf@p&>'T2Z3RT*U<ZT~> -"!.E>Fn#D352P/r!Go">rs$)Ds8SmD(]"(;jFF`>s."Z-J$8PF!JQp-rrML@m/I&+mf*?sJ*6h2 -!mQ8Ap&>&lF34F_1[4T4!F<M>rrU8keG9+GR!:(SrrI8?nc&V>o(r@eju2l3!$0en"6=u%O7iMT -gjhP\r;Qe[MtR)N]4'_!"297-g\h'P<lXh4!J$`arrGO?rVlnBVV_=RUj2D/#+pAEs3lJ>rVlo3 [email protected]#ju3/;!$1A)J,~> -"!.E>Fn#D352P/r!Go">rs$)Ds8SmD(]"(;jFF`>s."Z-J$8PF!JQp-rrML@m/I&+mf*?sJ*6h2 -!mQ8Ap&>&lF34F_1[4T4!F<M>rrU8keG9+GR!:(SrrI8?nc&V>o(r@eju2l3!$0en"6=u%O7iMT -gjhP\r;Qe[MtR)N]4'_!"297-g\h'P<lXh4!J$`arrGO?rVlnBVV_=RUj2D/#+pAEs3lJ>rVlo3 [email protected]#ju3/;!$1A)J,~> -"!.E>Fn#D352P/r!Go">rs$)Ds8SmD(]"(;jFF`>s."Z-J$8PF!JQp-rrML@m/I&+mf*?sJ*6h2 -!mQ8Ap&>&lF34F_1[4T4!F<M>rrU8keG9+GR!:(SrrI8?nc&V>o(r@eju2l3!$0en"6=u%O7iMT -gjhP\r;Qe[MtR)N]4'_!"297-g\h'P<lXh4!J$`arrGO?rVlnBVV_=RUj2D/#+pAEs3lJ>rVlo3 [email protected]#ju3/;!$1A)J,~> -"!.E>Fo21B`NfH(A8hDG!C#B=rrB%tA-dJ\s0]Y*A7U/grro!)A9-Unq#:A2Yl=Y,]4(`5N:.e` -rsS:7]`8"tO7rV9LTgFO!$1t:!-S9O&?Pp3K(J/9A?l17I9dLsA@MR<!+u4@!5/(+!?L;$A,sX@ -rrHo@r;QeISb<!`WH8";!8%0["6KR_df!\YlMob-rr3C]n,M+2s.s7:o`+s/rF?$+s8Th3A,uW, -rrFq?p&>%u_#=96O,`u8"-Sf5.JNiGFf1%+A;L6b!gR;1rVm%pA.`0ZP5P=\ju2l3!$1t:!.4]U% -&a.*Lh(\`A?>h2KC/YK!3uM&!omgerVm#hE4h0krVlrPA?Pn2"QaaBIA$N<!Ki<5rrK`?rVm2rJ -cEJqAD7(`h>Updc%Y]errTT=df'1K_1:6*g\h'P<lXh4!J$a=rrBe3A-;i2s1/,>"DIi8`ioCA# -^67J_6fbDA97bM!36$/!-n8<'sV9g<Fg^]s*(>sA7UJqs/40BEb(..rrA/[A,qbkrr@uVA,qqpr -s4?iA;[1-s8RZLA,r>%rrVe&PlC[`45p2=!K3->rrKf@p&>'T2Z3RT*W?!?Us]5;rF?8Ss8R9Bc -X^S/s8RT~> -"!.E>Fo21B`NfH(A8hDG!C#B=rrB%tA-dJ\s0]Y*A7U/grro!)A9-Unq#:A2Yl=Y,]4(`5N:.e` -rsS:7]`8"tO7rV9LTgFO!$1t:!-S9O&?Pp3K(J/9A?l17I9dLsA@MR<!+u4@!5/(+!?L;$A,sX@ -rrHo@r;QeISb<!`WH8";!8%0["6KR_df!\YlMob-rr3C]n,M+2s.s7:o`+s/rF?$+s8Th3A,uW, -rrFq?p&>%u_#=96O,`u8"-Sf5.JNiGFf1%+A;L6b!gR;1rVm%pA.`0ZP5P=\ju2l3!$1t:!.4]U% -&a.*Lh(\`A?>h2KC/YK!3uM&!omgerVm#hE4h0krVlrPA?Pn2"QaaBIA$N<!Ki<5rrK`?rVm2rJ -cEJqAD7(`h>Updc%Y]errTT=df'1K_1:6*g\h'P<lXh4!J$a=rrBe3A-;i2s1/,>"DIi8`ioCA# -^67J_6fbDA97bM!36$/!-n8<'sV9g<Fg^]s*(>sA7UJqs/40BEb(..rrA/[A,qbkrr@uVA,qqpr -s4?iA;[1-s8RZLA,r>%rrVe&PlC[`45p2=!K3->rrKf@p&>'T2Z3RT*W?!?Us]5;rF?8Ss8R9Bc -X^S/s8RT~> -"!.E>Fo21B`NfH(A8hDG!C#B=rrB%tA-dJ\s0]Y*A7U/grro!)A9-Unq#:A2Yl=Y,]4(`5N:.e` -rsS:7]`8"tO7rV9LTgFO!$1t:!-S9O&?Pp3K(J/9A?l17I9dLsA@MR<!+u4@!5/(+!?L;$A,sX@ -rrHo@r;QeISb<!`WH8";!8%0["6KR_df!\YlMob-rr3C]n,M+2s.s7:o`+s/rF?$+s8Th3A,uW, -rrFq?p&>%u_#=96O,`u8"-Sf5.JNiGFf1%+A;L6b!gR;1rVm%pA.`0ZP5P=\ju2l3!$1t:!.4]U% -&a.*Lh(\`A?>h2KC/YK!3uM&!omgerVm#hE4h0krVlrPA?Pn2"QaaBIA$N<!Ki<5rrK`?rVm2rJ -cEJqAD7(`h>Updc%Y]errTT=df'1K_1:6*g\h'P<lXh4!J$a=rrBe3A-;i2s1/,>"DIi8`ioCA# -^67J_6fbDA97bM!36$/!-n8<'sV9g<Fg^]s*(>sA7UJqs/40BEb(..rrA/[A,qbkrr@uVA,qqpr -s4?iA;[1-s8RZLA,r>%rrVe&PlC[`45p2=!K3->rrKf@p&>'T2Z3RT*W?!?Us]5;rF?8Ss8R9Bc -X^S/s8RT~> -"!.E>+oY"=nGiOLJ4Q':Y\F(9!C#B=rrC[M[h5T^s+Mqh\$r2\s.PF]\#s&_q#:A21B'fKA^g\. -li/"%rrth%K`D'Nb5VDAU3cP5!$2";!HG1>[hOJ#s2@f;\$pmts21i\\$t2ns'E/5[fL`lq#:@. ->Q2)4ER=CY!i_%@r;QeISb<!`WH8%<!Tdkj[f]k-he;tt"'>BB;Z?\)39C2.O@Y5;[oi[TF8`QM ->5uWerO2c3r;QdRl1P&W?,6F="cnXr84[+erreOOo(+=)rsNs&T!u2#L]@D"[ho#C"k:"&Y.)XQ -rrM7?o`"n3r;QeBVuF.6Lo^P:UT.b3KX(P47fKjtJ@GOX!mgp]rVm"#a8_-[rVlr7!.+YE"nc'C -s*^R<rrJ7?p&>')@K$34qP-T)\$ol2lsTh%!c/D1rr3%Q!4Dh+"j;#Cs$>E;rrH0?p&>&CSc&Kf -iVg4f>lVWdrjN$<@K3$Jrr3BIs8SH4NjlL"D?$enrjMp-F85bQgUAo,\&JCIG;#Mr\"8?Sen[gZ -\!r?VOdQ-W"IG;Os2k8L%$R(YKV,`_S1]'dI\ZnO!K<iRrrVKd:]C@p45p2=!K3->rrKf@p&>'T -2Z3R\*WQ/Q8;ZX3=oGf:EmOcj88>WCDUnc;~> -"!.E>+oY"=nGiOLJ4Q':Y\F(9!C#B=rrC[M[h5T^s+Mqh\$r2\s.PF]\#s&_q#:A21B'fKA^g\. -li/"%rrth%K`D'Nb5VDAU3cP5!$2";!HG1>[hOJ#s2@f;\$pmts21i\\$t2ns'E/5[fL`lq#:@. ->Q2)4ER=CY!i_%@r;QeISb<!`WH8%<!Tdkj[f]k-he;tt"'>BB;Z?\)39C2.O@Y5;[oi[TF8`QM ->5uWerO2c3r;QdRl1P&W?,6F="cnXr84[+erreOOo(+=)rsNs&T!u2#L]@D"[ho#C"k:"&Y.)XQ -rrM7?o`"n3r;QeBVuF.6Lo^P:UT.b3KX(P47fKjtJ@GOX!mgp]rVm"#a8_-[rVlr7!.+YE"nc'C -s*^R<rrJ7?p&>')@K$34qP-T)\$ol2lsTh%!c/D1rr3%Q!4Dh+"j;#Cs$>E;rrH0?p&>&CSc&Kf -iVg4f>lVWdrjN$<@K3$Jrr3BIs8SH4NjlL"D?$enrjMp-F85bQgUAo,\&JCIG;#Mr\"8?Sen[gZ -\!r?VOdQ-W"IG;Os2k8L%$R(YKV,`_S1]'dI\ZnO!K<iRrrVKd:]C@p45p2=!K3->rrKf@p&>'T -2Z3R\*WQ/Q8;ZX3=oGf:EmOcj88>WCDUnc;~> -"!.E>+oY"=nGiOLJ4Q':Y\F(9!C#B=rrC[M[h5T^s+Mqh\$r2\s.PF]\#s&_q#:A21B'fKA^g\. -li/"%rrth%K`D'Nb5VDAU3cP5!$2";!HG1>[hOJ#s2@f;\$pmts21i\\$t2ns'E/5[fL`lq#:@. ->Q2)4ER=CY!i_%@r;QeISb<!`WH8%<!Tdkj[f]k-he;tt"'>BB;Z?\)39C2.O@Y5;[oi[TF8`QM ->5uWerO2c3r;QdRl1P&W?,6F="cnXr84[+erreOOo(+=)rsNs&T!u2#L]@D"[ho#C"k:"&Y.)XQ -rrM7?o`"n3r;QeBVuF.6Lo^P:UT.b3KX(P47fKjtJ@GOX!mgp]rVm"#a8_-[rVlr7!.+YE"nc'C -s*^R<rrJ7?p&>')@K$34qP-T)\$ol2lsTh%!c/D1rr3%Q!4Dh+"j;#Cs$>E;rrH0?p&>&CSc&Kf -iVg4f>lVWdrjN$<@K3$Jrr3BIs8SH4NjlL"D?$enrjMp-F85bQgUAo,\&JCIG;#Mr\"8?Sen[gZ -\!r?VOdQ-W"IG;Os2k8L%$R(YKV,`_S1]'dI\ZnO!K<iRrrVKd:]C@p45p2=!K3->rrKf@p&>'T -2Z3R\*WQ/Q8;ZX3=oGf:EmOcj88>WCDUnc;~> -"!.E>@K+aVr;Q`rJHPNDmOnJ<!C#B9rt+!Ns+Ppms8UV?s*^O>s62?6rrm5&e/6]nr;Qa;qYpcM -e=;Er;p,+>!MXo6rr=);rrG.?rr3F`PlLb'g].;(SH&WV0`:qO+T;<>"6]4S55Zo5_Z/6Crr3"* -^@hL,Lm7f:!Ip[5rrK*?rVloQ62gfch#5\orVlsEq8uV7rsUmKs8Sm*mJm2,s1)Y<rr^sSZ#'C= -!$2";!Aj!5rrHE@rVmFi(nB+*^0^i/Nq35A.KBF4r$r%hs)j7ms8U&>rVlj<qu6[Ho(r@eju2l3 -!$2";!CPT?rs\;\s#T/u49(2%s"_RmrrI\?rr3&oeE6c1"\.)Cs,E*<rrViBl2L\d6/2G>IA$N< -!Ki<5rrK`?r;R&L5j&+H4l>?\rr3&Z/Ed$4$gQ75s8UhXjo>?Hg\h'P<lXh4!J$a8rrX;AWH8(= -"TI-TTmQe=$m#BJQu;Bms6)??OGs2="8^pTS,=c:AcD]17+hJ<&U0-*s8U)>s8Prqqu=?:s(8b> -rrKN?qu7)+E;rqZs8VbKGlLIarr3#elMgebr7'^)rrG4?rr3"WP5YC]]jUO5!Tl<<rs0Y*.qmH" -s8O,<rsE/Hs6;)ns8Q3>s*t~> -"!.E>@K+aVr;Q`rJHPNDmOnJ<!C#B9rt+!Ns+Ppms8UV?s*^O>s62?6rrm5&e/6]nr;Qa;qYpcM -e=;Er;p,+>!MXo6rr=);rrG.?rr3F`PlLb'g].;(SH&WV0`:qO+T;<>"6]4S55Zo5_Z/6Crr3"* -^@hL,Lm7f:!Ip[5rrK*?rVloQ62gfch#5\orVlsEq8uV7rsUmKs8Sm*mJm2,s1)Y<rr^sSZ#'C= -!$2";!Aj!5rrHE@rVmFi(nB+*^0^i/Nq35A.KBF4r$r%hs)j7ms8U&>rVlj<qu6[Ho(r@eju2l3 -!$2";!CPT?rs\;\s#T/u49(2%s"_RmrrI\?rr3&oeE6c1"\.)Cs,E*<rrViBl2L\d6/2G>IA$N< -!Ki<5rrK`?r;R&L5j&+H4l>?\rr3&Z/Ed$4$gQ75s8UhXjo>?Hg\h'P<lXh4!J$a8rrX;AWH8(= -"TI-TTmQe=$m#BJQu;Bms6)??OGs2="8^pTS,=c:AcD]17+hJ<&U0-*s8U)>s8Prqqu=?:s(8b> -rrKN?qu7)+E;rqZs8VbKGlLIarr3#elMgebr7'^)rrG4?rr3"WP5YC]]jUO5!Tl<<rs0Y*.qmH" -s8O,<rsE/Hs6;)ns8Q3>s*t~> -"!.E>@K+aVr;Q`rJHPNDmOnJ<!C#B9rt+!Ns+Ppms8UV?s*^O>s62?6rrm5&e/6]nr;Qa;qYpcM -e=;Er;p,+>!MXo6rr=);rrG.?rr3F`PlLb'g].;(SH&WV0`:qO+T;<>"6]4S55Zo5_Z/6Crr3"* -^@hL,Lm7f:!Ip[5rrK*?rVloQ62gfch#5\orVlsEq8uV7rsUmKs8Sm*mJm2,s1)Y<rr^sSZ#'C= -!$2";!Aj!5rrHE@rVmFi(nB+*^0^i/Nq35A.KBF4r$r%hs)j7ms8U&>rVlj<qu6[Ho(r@eju2l3 -!$2";!CPT?rs\;\s#T/u49(2%s"_RmrrI\?rr3&oeE6c1"\.)Cs,E*<rrViBl2L\d6/2G>IA$N< -!Ki<5rrK`?r;R&L5j&+H4l>?\rr3&Z/Ed$4$gQ75s8UhXjo>?Hg\h'P<lXh4!J$a8rrX;AWH8(= -"TI-TTmQe=$m#BJQu;Bms6)??OGs2="8^pTS,=c:AcD]17+hJ<&U0-*s8U)>s8Prqqu=?:s(8b> -rrKN?qu7)+E;rqZs8VbKGlLIarr3#elMgebr7'^)rrG4?rr3"WP5YC]]jUO5!Tl<<rs0Y*.qmH" -s8O,<rsE/Hs6;)ns8Q3>s*t~> -"!.E>Fo)+AJXc]>rq-0h!C#B>rrMn@rZ)+Z5Q?G(rr38S8H4C6nF56up\tD5YlF#Po_e^h*VfX; -WcJ,<(]GEU&pj9O!$2";!BD(t*<[.Hs8O,=rrr2os8VI?r;Qfh+9!8_PktFNr:'daqu6\'^@qR. -Wd+@:rrIV?p&>&lF8c+>rO`"K"3gbn9)\bl,PfJ[rr3-]jo>@VGlI^FoDc@2qu6]%%/h1H.fB;I -1[4T4!F<M>rt(6GqZ$TP56$WZ*??+'>Q=KrnH8IaFf56=!QA.=rr=):rrFV?qYpTY2Y@"L*W5p< -ofW3o%!VLH3o]*[s,*$?1@+r>!J-a8rr=\N*WHZN[eTk&+9!8^%MHbZrrJ7?p&>')@Jp-+cqXN> -"82WS3;rjX2<Xf8!S.q`*<HH`q>L<o<lXh4!J$a>rrMt`r>btZs8%lW*<[SWs.Of=rraABs-SK= -rrhOCs,`3:rrDlmnGr7]rrGO?rVlnBVZ-T(``E->;9](?qtC&%(&f3V)0#WK;Z7[>'QF(PaSu2B -Uj2q47/e2-Dts,-!B]9>rrJ%@[email protected]#ju3/;!ua,Xm/?qa')`gR'Ysb61B.:TpAFr@~> -"!.E>Fo)+AJXc]>rq-0h!C#B>rrMn@rZ)+Z5Q?G(rr38S8H4C6nF56up\tD5YlF#Po_e^h*VfX; -WcJ,<(]GEU&pj9O!$2";!BD(t*<[.Hs8O,=rrr2os8VI?r;Qfh+9!8_PktFNr:'daqu6\'^@qR. -Wd+@:rrIV?p&>&lF8c+>rO`"K"3gbn9)\bl,PfJ[rr3-]jo>@VGlI^FoDc@2qu6]%%/h1H.fB;I -1[4T4!F<M>rt(6GqZ$TP56$WZ*??+'>Q=KrnH8IaFf56=!QA.=rr=):rrFV?qYpTY2Y@"L*W5p< -ofW3o%!VLH3o]*[s,*$?1@+r>!J-a8rr=\N*WHZN[eTk&+9!8^%MHbZrrJ7?p&>')@Jp-+cqXN> -"82WS3;rjX2<Xf8!S.q`*<HH`q>L<o<lXh4!J$a>rrMt`r>btZs8%lW*<[SWs.Of=rraABs-SK= -rrhOCs,`3:rrDlmnGr7]rrGO?rVlnBVZ-T(``E->;9](?qtC&%(&f3V)0#WK;Z7[>'QF(PaSu2B -Uj2q47/e2-Dts,-!B]9>rrJ%@[email protected]#ju3/;!ua,Xm/?qa')`gR'Ysb61B.:TpAFr@~> -"!.E>Fo)+AJXc]>rq-0h!C#B>rrMn@rZ)+Z5Q?G(rr38S8H4C6nF56up\tD5YlF#Po_e^h*VfX; -WcJ,<(]GEU&pj9O!$2";!BD(t*<[.Hs8O,=rrr2os8VI?r;Qfh+9!8_PktFNr:'daqu6\'^@qR. -Wd+@:rrIV?p&>&lF8c+>rO`"K"3gbn9)\bl,PfJ[rr3-]jo>@VGlI^FoDc@2qu6]%%/h1H.fB;I -1[4T4!F<M>rt(6GqZ$TP56$WZ*??+'>Q=KrnH8IaFf56=!QA.=rr=):rrFV?qYpTY2Y@"L*W5p< -ofW3o%!VLH3o]*[s,*$?1@+r>!J-a8rr=\N*WHZN[eTk&+9!8^%MHbZrrJ7?p&>')@Jp-+cqXN> -"82WS3;rjX2<Xf8!S.q`*<HH`q>L<o<lXh4!J$a>rrMt`r>btZs8%lW*<[SWs.Of=rraABs-SK= -rrhOCs,`3:rrDlmnGr7]rrGO?rVlnBVZ-T(``E->;9](?qtC&%(&f3V)0#WK;Z7[>'QF(PaSu2B -Uj2q47/e2-Dts,-!B]9>rrJ%@[email protected]#ju3/;!ua,Xm/?qa')`gR'Ysb61B.:TpAFr@~> -"!.E>Fo)+=JXcK8!C#B>s8S,ZrrrD25Q?G(rr35R8H7pi/1a!Yrs!;Ds8UtYjSf)Y*W?!=j7N?N -"KHMB;p,+>!MXo6rrGC?rVlmYj8/cU*W#d@m4eS?kjSQ)rVln:Xn_nrf_tgN?G?F=!qFb*rVlrl -R#h.E!q(Q@p&>&lF8c+>q;2)M""Woj9)\bl,PfJ[rr3-]jo>@VGl7RB\RYU<"5*XYD"mr11[4T4 -!F<M>rrJ1?rr3#U55bE]o-sG6#'Ggrs8U&>rVlj<qu6dKo)J:BrVlo\2Y@"L*W5p<htd9O%$HMJ -3o]*[s,*$?1;s1l!J-a>rrVrDjneuXNK=&<!qat*qYpSET`"fjOc/o4!P;e<rrLJ@r;QfZ3<&pZ -i@O0krrVK7o(r@e6/2>;!ER55rrIY@rVlo(C]=>:fH("]kPkJiq'?!6HiO-#)uor*K`:uSkV`C% -N;ihXqVLrG#=R5EpYc'qVZ-T!``E->;9\t<!G8h<rt/POs8VeRE;rqZs8V_IGlQ^rrr3"hJc>ZN -r6sX(rrucDs8VSDV>^Dp]jUO5!Tl<<rs0Y+/83N"s8O,9rrMC?qu;0~> -"!.E>Fo)+=JXcK8!C#B>s8S,ZrrrD25Q?G(rr35R8H7pi/1a!Yrs!;Ds8UtYjSf)Y*W?!=j7N?N -"KHMB;p,+>!MXo6rrGC?rVlmYj8/cU*W#d@m4eS?kjSQ)rVln:Xn_nrf_tgN?G?F=!qFb*rVlrl -R#h.E!q(Q@p&>&lF8c+>q;2)M""Woj9)\bl,PfJ[rr3-]jo>@VGl7RB\RYU<"5*XYD"mr11[4T4 -!F<M>rrJ1?rr3#U55bE]o-sG6#'Ggrs8U&>rVlj<qu6dKo)J:BrVlo\2Y@"L*W5p<htd9O%$HMJ -3o]*[s,*$?1;s1l!J-a>rrVrDjneuXNK=&<!qat*qYpSET`"fjOc/o4!P;e<rrLJ@r;QfZ3<&pZ -i@O0krrVK7o(r@e6/2>;!ER55rrIY@rVlo(C]=>:fH("]kPkJiq'?!6HiO-#)uor*K`:uSkV`C% -N;ihXqVLrG#=R5EpYc'qVZ-T!``E->;9\t<!G8h<rt/POs8VeRE;rqZs8V_IGlQ^rrr3"hJc>ZN -r6sX(rrucDs8VSDV>^Dp]jUO5!Tl<<rs0Y+/83N"s8O,9rrMC?qu;0~> -"!.E>Fo)+=JXcK8!C#B>s8S,ZrrrD25Q?G(rr35R8H7pi/1a!Yrs!;Ds8UtYjSf)Y*W?!=j7N?N -"KHMB;p,+>!MXo6rrGC?rVlmYj8/cU*W#d@m4eS?kjSQ)rVln:Xn_nrf_tgN?G?F=!qFb*rVlrl -R#h.E!q(Q@p&>&lF8c+>q;2)M""Woj9)\bl,PfJ[rr3-]jo>@VGl7RB\RYU<"5*XYD"mr11[4T4 -!F<M>rrJ1?rr3#U55bE]o-sG6#'Ggrs8U&>rVlj<qu6dKo)J:BrVlo\2Y@"L*W5p<htd9O%$HMJ -3o]*[s,*$?1;s1l!J-a>rrVrDjneuXNK=&<!qat*qYpSET`"fjOc/o4!P;e<rrLJ@r;QfZ3<&pZ -i@O0krrVK7o(r@e6/2>;!ER55rrIY@rVlo(C]=>:fH("]kPkJiq'?!6HiO-#)uor*K`:uSkV`C% -N;ihXqVLrG#=R5EpYc'qVZ-T!``E->;9\t<!G8h<rt/POs8VeRE;rqZs8V_IGlQ^rrr3"hJc>ZN -r6sX(rrucDs8VSDV>^Dp]jUO5!Tl<<rs0Y+/83N"s8O,9rrMC?qu;0~> -"!.E><;j6._>jOdS,<3sfsWK(^&S,8L:4Ot5<o1%Qi@!feO]_7NW-?d`V9B5Cp<p=%(fsJe$c\" -[^O]cMOa[S"Ho5R;p,+>!MXo6rrM@?rVln=WrBF,f`(mN*W#dAnm/]4I)#\h[JmT8GfBIX!*]?0 -!HHNdrrT>'N;`bW_HQg9!M+c5rrK*?rVlo[Ac9%>>P6lerO)f1qq5fb[KU*@s8Sm>r;QfBF8`NL ->Q;`frO2V'!Aj!5rrHE@rVlo%L&SL]W-/%<!V7c7rsa*)S[PttL]@D![M?6mrVm"5Z*os^rVlo\ -2Y@"L*W5p<Gc(JK%#:qa3o]FGs,*$?1.V>P!J%]ZrrUOIC&7i1NK=&<!l+e^qYpSET`"fjOc/o4 -!P;e<rrLJ@r;Qfa@K*\:C_,_.rrRiR[JKn(6/2>;!ER55rrIY@rVlo>GQ,#R<ZM.VHN(>]ZXWsI -I^Z[i'EA*"K`:uSkV`CEL&SL]WH@k6#C<5TTn35fVZ-T!``E->;9\t<!Ki`J[M6pbs,3AT[\#9n -s+R&Q[[]!qs*pdB[K2>`rr3&c!)NRn"CAOFOdu@L!PMn6rrM7?r;R$Cs8Tc(M<Y%DrO)jhs8V@> -qu;0~> -"!.E><;j6._>jOdS,<3sfsWK(^&S,8L:4Ot5<o1%Qi@!feO]_7NW-?d`V9B5Cp<p=%(fsJe$c\" -[^O]cMOa[S"Ho5R;p,+>!MXo6rrM@?rVln=WrBF,f`(mN*W#dAnm/]4I)#\h[JmT8GfBIX!*]?0 -!HHNdrrT>'N;`bW_HQg9!M+c5rrK*?rVlo[Ac9%>>P6lerO)f1qq5fb[KU*@s8Sm>r;QfBF8`NL ->Q;`frO2V'!Aj!5rrHE@rVlo%L&SL]W-/%<!V7c7rsa*)S[PttL]@D![M?6mrVm"5Z*os^rVlo\ -2Y@"L*W5p<Gc(JK%#:qa3o]FGs,*$?1.V>P!J%]ZrrUOIC&7i1NK=&<!l+e^qYpSET`"fjOc/o4 -!P;e<rrLJ@r;Qfa@K*\:C_,_.rrRiR[JKn(6/2>;!ER55rrIY@rVlo>GQ,#R<ZM.VHN(>]ZXWsI -I^Z[i'EA*"K`:uSkV`CEL&SL]WH@k6#C<5TTn35fVZ-T!``E->;9\t<!Ki`J[M6pbs,3AT[\#9n -s+R&Q[[]!qs*pdB[K2>`rr3&c!)NRn"CAOFOdu@L!PMn6rrM7?r;R$Cs8Tc(M<Y%DrO)jhs8V@> -qu;0~> -"!.E><;j6._>jOdS,<3sfsWK(^&S,8L:4Ot5<o1%Qi@!feO]_7NW-?d`V9B5Cp<p=%(fsJe$c\" -[^O]cMOa[S"Ho5R;p,+>!MXo6rrM@?rVln=WrBF,f`(mN*W#dAnm/]4I)#\h[JmT8GfBIX!*]?0 -!HHNdrrT>'N;`bW_HQg9!M+c5rrK*?rVlo[Ac9%>>P6lerO)f1qq5fb[KU*@s8Sm>r;QfBF8`NL ->Q;`frO2V'!Aj!5rrHE@rVlo%L&SL]W-/%<!V7c7rsa*)S[PttL]@D![M?6mrVm"5Z*os^rVlo\ -2Y@"L*W5p<Gc(JK%#:qa3o]FGs,*$?1.V>P!J%]ZrrUOIC&7i1NK=&<!l+e^qYpSET`"fjOc/o4 -!P;e<rrLJ@r;Qfa@K*\:C_,_.rrRiR[JKn(6/2>;!ER55rrIY@rVlo>GQ,#R<ZM.VHN(>]ZXWsI -I^Z[i'EA*"K`:uSkV`CEL&SL]WH@k6#C<5TTn35fVZ-T!``E->;9\t<!Ki`J[M6pbs,3AT[\#9n -s+R&Q[[]!qs*pdB[K2>`rr3&c!)NRn"CAOFOdu@L!PMn6rrM7?r;R$Cs8Tc(M<Y%DrO)jhs8V@> -qu;0~> -!Zh<`r+6(Zs8TIDqu6Z!ral.Qrr38'AnGcCN;p?%rr3,`OoNUMral1M\bQ1*VpPGC"4j.FUASU* -V>pRRral;$s8SGCrr3#>YP.ttno2/<rr@6AAcSt4rr@9=rr_7mB".d>!,):C!5/(+!;?A'!65!; -!mYDhrVloQSGW<fj+Xf2rs,MlR@3@?s4I9^"6KR_e,<k\lMpn0ral>jGQ7]bY5A5!_u40Lq>^K/ -rFQ<6s8UK7R31\drrZPJRA9c]!36$1!."[email protected];Qh8As<5o"1">P -2Y@"O(n$f.rr2tGral;2s8RrDrr3,2^AftMral/8rVlrZAu5A(!P?#CrrUkcYPS8)Z,ZhDp6h=M -MtR)U]/uFKj8]/>Pl(I\hYq*gc8FearrTT?e,'(MNTpKChjKlh`qB?:J6nY3qu?]2ral<"GQ7]S -ral.Frr383AnL-Fs8TpCrr3,kL&_1Rral.Uo`#,DAqU-`Xi^SB"l5XIs-DU?rrA2\AcS"nrt(-$ -AqnR0s8RjdArFa5s8R]MAcSS(rrVe(Q2L[^AcS:urr]!`EFAJ>#Nd.sRF;-8GQ%ODV:,D=rFQ2O -s8VYCqu;0~> -!Zh<`r+6(Zs8TIDqu6Z!ral.Qrr38'AnGcCN;p?%rr3,`OoNUMral1M\bQ1*VpPGC"4j.FUASU* -V>pRRral;$s8SGCrr3#>YP.ttno2/<rr@6AAcSt4rr@9=rr_7mB".d>!,):C!5/(+!;?A'!65!; -!mYDhrVloQSGW<fj+Xf2rs,MlR@3@?s4I9^"6KR_e,<k\lMpn0ral>jGQ7]bY5A5!_u40Lq>^K/ -rFQ<6s8UK7R31\drrZPJRA9c]!36$1!."[email protected];Qh8As<5o"1">P -2Y@"O(n$f.rr2tGral;2s8RrDrr3,2^AftMral/8rVlrZAu5A(!P?#CrrUkcYPS8)Z,ZhDp6h=M -MtR)U]/uFKj8]/>Pl(I\hYq*gc8FearrTT?e,'(MNTpKChjKlh`qB?:J6nY3qu?]2ral<"GQ7]S -ral.Frr383AnL-Fs8TpCrr3,kL&_1Rral.Uo`#,DAqU-`Xi^SB"l5XIs-DU?rrA2\AcS"nrt(-$ -AqnR0s8RjdArFa5s8R]MAcSS(rrVe(Q2L[^AcS:urr]!`EFAJ>#Nd.sRF;-8GQ%ODV:,D=rFQ2O -s8VYCqu;0~> -!Zh<`r+6(Zs8TIDqu6Z!ral.Qrr38'AnGcCN;p?%rr3,`OoNUMral1M\bQ1*VpPGC"4j.FUASU* -V>pRRral;$s8SGCrr3#>YP.ttno2/<rr@6AAcSt4rr@9=rr_7mB".d>!,):C!5/(+!;?A'!65!; -!mYDhrVloQSGW<fj+Xf2rs,MlR@3@?s4I9^"6KR_e,<k\lMpn0ral>jGQ7]bY5A5!_u40Lq>^K/ -rFQ<6s8UK7R31\drrZPJRA9c]!36$1!."[email protected];Qh8As<5o"1">P -2Y@"O(n$f.rr2tGral;2s8RrDrr3,2^AftMral/8rVlrZAu5A(!P?#CrrUkcYPS8)Z,ZhDp6h=M -MtR)U]/uFKj8]/>Pl(I\hYq*gc8FearrTT?e,'(MNTpKChjKlh`qB?:J6nY3qu?]2ral<"GQ7]S -ral.Frr383AnL-Fs8TpCrr3,kL&_1Rral.Uo`#,DAqU-`Xi^SB"l5XIs-DU?rrA2\AcS"nrt(-$ -AqnR0s8RjdArFa5s8R]MAcSS(rrVe(Q2L[^AcS:urr]!`EFAJ>#Nd.sRF;-8GQ%ODV:,D=rFQ2O -s8VYCqu;0~> -!$0_l!I^U>rrL>?c2Rh,WkJE5h6Z_Q!7h($!9VW-!65"j!;,sa"0dE1O3[b-Tn@ugo`##OK7gQ" -rrFn@g]%9Grdt3jp&>$Krdt4,o)A_JkOAKOfD^C&j7WEP_>]&eqXFLccb08W!$.[2"-%qcZ1\+s -oDX@BaQNSR~> -!$0_l!I^U>rrL>?c2Rh,WkJE5h6Z_Q!7h($!9VW-!65"j!;,sa"0dE1O3[b-Tn@ugo`##OK7gQ" -rrFn@g]%9Grdt3jp&>$Krdt4,o)A_JkOAKOfD^C&j7WEP_>]&eqXFLccb08W!$.[2"-%qcZ1\+s -oDX@BaQNSR~> -!$0_l!I^U>rrL>?c2Rh,WkJE5h6Z_Q!7h($!9VW-!65"j!;,sa"0dE1O3[b-Tn@ugo`##OK7gQ" -rrFn@g]%9Grdt3jp&>$Krdt4,o)A_JkOAKOfD^C&j7WEP_>]&eqXFLccb08W!$.[2"-%qcZ1\+s -oDX@BaQNSR~> -!$0_l!R>og?NFuWJcC<$dJj5&laHfo2<W*]!Si8*?N?dNs6ou<~> -!$0_l!R>og?NFuWJcC<$dJj5&laHfo2<W*]!Si8*?N?dNs6ou<~> -!$0_l!R>og?NFuWJcC<$dJj5&laHfo2<W*]!Si8*?N?dNs6ou<~> -!$0\k!7h(]!6TlmJcF*s!Qk2HrrL:<aSu7trk&7.JcG3=J,~> -!$0\k!7h(]!6TlmJcF*s!Qk2HrrL:<aSu7trk&7.JcG3=J,~> -!$0\k!7h(]!6TlmJcF*s!Qk2HrrL:<aSu7trk&7.JcG3=J,~> -!$..#"#_JQH%H!Hs+13$s7lVE~> -!$..#"#_JQH%H!Hs+13$s7lVE~> -!$..#"#_JQH%H!Hs+13$s7lVE~> -!$1P.",Is-hrXk>C)m`\rrQ[N'D)5,m2m?Wo_8@e73")Lrr`#hZsnUdJcC<$JcGNFJ,~> -!$1P.",Is-hrXk>C)m`\rrQ[N'D)5,m2m?Wo_8@e73")Lrr`#hZsnUdJcC<$JcGNFJ,~> -!$1P.",Is-hrXk>C)m`\rrQ[N'D)5,m2m?Wo_8@e73")Lrr`#hZsnUdJcC<$JcGNFJ,~> -!$1J,!C#B#rr=)9rr=)2rraJBs/L,5rrMX?lMgms@Y+Q1s+13$s7lVE~> -!$1J,!C#B#rr=)9rr=)2rraJBs/L,5rrMX?lMgms@Y+Q1s+13$s7lVE~> -!$1J,!C#B#rr=)9rr=)2rraJBs/L,5rrMX?lMgms@Y+Q1s+13$s7lVE~> -#9Ej*e^C_-[/U(*g&A5V[f$.+52Q#5"kqkTZ*D%BrrC@DYlMW<rr=)9rr=)9rrKOBr;R!Er;X^+ -s8Tq7YlN)JrrMX?r;Qc0rilIPrr2u0rilIRr;Qf0@Y+Q1s+13$s7lVE~> -#9Ej*e^C_-[/U(*g&A5V[f$.+52Q#5"kqkTZ*D%BrrC@DYlMW<rr=)9rr=)9rrKOBr;R!Er;X^+ -s8Tq7YlN)JrrMX?r;Qc0rilIPrr2u0rilIRr;Qf0@Y+Q1s+13$s7lVE~> -#9Ej*e^C_-[/U(*g&A5V[f$.+52Q#5"kqkTZ*D%BrrC@DYlMW<rr=)9rr=)9rrKOBr;R!Er;X^+ -s8Tq7YlN)JrrMX?r;Qc0rilIPrr2u0rilIRr;Qf0@Y+Q1s+13$s7lVE~> -$Q]8F7!rccCo%*_JGs<bDQ*O6!C#B6rs=B]GACu7ZN&$mrbDOU[f-4+*W#d9*W#d:$nqPY!?h=< -rr@rUCB8b%rr3#h/,fJKY]9YX"F\VrXDe)R!IiJqrrK`@JcC<$JcC<$q#>j~> -$Q]8F7!rccCo%*_JGs<bDQ*O6!C#B6rs=B]GACu7ZN&$mrbDOU[f-4+*W#d9*W#d:$nqPY!?h=< -rr@rUCB8b%rr3#h/,fJKY]9YX"F\VrXDe)R!IiJqrrK`@JcC<$JcC<$q#>j~> -$Q]8F7!rccCo%*_JGs<bDQ*O6!C#B6rs=B]GACu7ZN&$mrbDOU[f-4+*W#d9*W#d:$nqPY!?h=< -rr@rUCB8b%rr3#h/,fJKY]9YX"F\VrXDe)R!IiJqrrK`@JcC<$JcC<$q#>j~> -$Q]8F50X',pEop4IfB?JmOnJ<!C#B>rrBt7G70i=Ki$S)s4'[?I@pN=!R+C=rr=)9rr=)9rrJ+N -rr3,"G7Sk^q>UJiHN*pFnLOS<!CGQ?rrgQfs#K-=rrIq?rVlo1@Y+Q1s+13$s7lVE~> -$Q]8F50X',pEop4IfB?JmOnJ<!C#B>rrBt7G70i=Ki$S)s4'[?I@pN=!R+C=rr=)9rr=)9rrJ+N -rr3,"G7Sk^q>UJiHN*pFnLOS<!CGQ?rrgQfs#K-=rrIq?rVlo1@Y+Q1s+13$s7lVE~> -$Q]8F50X',pEop4IfB?JmOnJ<!C#B>rrBt7G70i=Ki$S)s4'[?I@pN=!R+C=rr=)9rr=)9rrJ+N -rr3,"G7Sk^q>UJiHN*pFnLOS<!CGQ?rrgQfs#K-=rrIq?rVlo1@Y+Q1s+13$s7lVE~> -"!.E>FoMCDpEop4/cJoS<%e.L!C#B>rrC[KV$"@0KpVf="P$'CI@pN=!R+C=rr=)9rr=)4rrg<; -'r/;;rr@KH=ogX0rr3#h/,fJK]Oh(G"JPkq3DocZ!AKc:rrK`@JcC<$JcC<$q#>j~> -"!.E>FoMCDpEop4/cJoS<%e.L!C#B>rrC[KV$"@0KpVf="P$'CI@pN=!R+C=rr=)9rr=)4rrg<; -'r/;;rr@KH=ogX0rr3#h/,fJK]Oh(G"JPkq3DocZ!AKc:rrK`@JcC<$JcC<$q#>j~> -"!.E>FoMCDpEop4/cJoS<%e.L!C#B>rrC[KV$"@0KpVf="P$'CI@pN=!R+C=rr=)9rr=)4rrg<; -'r/;;rr@KH=ogX0rr3#h/,fJK]Oh(G"JPkq3DocZ!AKc:rrK`@JcC<$JcC<$q#>j~> -"!.E>FoMCDpEop4?i@e@c2IYC52Q#5!JQm>rrgkCs*^O=rrL>?rVlj<qYpO9oD\h6r;HWrI&?nZ -!IK.lrrMX?r;Qc>rkS_oVuJcYrP8KqrVlo1@Y+Q1s+13$s7lVE~> -"!.E>FoMCDpEop4?i@e@c2IYC52Q#5!JQm>rrgkCs*^O=rrL>?rVlj<qYpO9oD\h6r;HWrI&?nZ -!IK.lrrMX?r;Qc>rkS_oVuJcYrP8KqrVlo1@Y+Q1s+13$s7lVE~> -"!.E>FoMCDpEop4?i@e@c2IYC52Q#5!JQm>rrgkCs*^O=rrL>?rVlj<qYpO9oD\h6r;HWrI&?nZ -!IK.lrrMX?r;Qc>rkS_oVuJcYrP8KqrVlo1@Y+Q1s+13$s7lVE~> -"!.E>FoMCDpEop4Ie`pD52Q#5$&'&,s8UV?s*^O=rrL>?rVlj<qYpO9qYpRH9)S\i+T23<##i\E -s2Pk#rr3#h/,fJK>2T>Z"HeWB3TKo7!P;fls+13$s+14Fs*t~> -"!.E>FoMCDpEop4Ie`pD52Q#5$&'&,s8UV?s*^O=rrL>?rVlj<qYpO9qYpRH9)S\i+T23<##i\E -s2Pk#rr3#h/,fJK>2T>Z"HeWB3TKo7!P;fls+13$s+14Fs*t~> -"!.E>FoMCDpEop4Ie`pD52Q#5$&'&,s8UV?s*^O=rrL>?rVlj<qYpO9qYpRH9)S\i+T23<##i\E -s2Pk#rr3#h/,fJK>2T>Z"HeWB3TKo7!P;fls+13$s+14Fs*t~> -"!.EGKDtlRpH/ESNrC%!/H5YPL`IBR1\^nUKp>sb*CB](rG_`V#YFsos(WPm*Duh9"CiGj*Ei=? -!@1&1rrG%UrVmK-9-#$QWJCNR73*9eGQ7^@4oQH)IJs3D2?"TrLAq2TkiSgQJcC<$JcC<$q#>j~> -"!.EGKDtlRpH/ESNrC%!/H5YPL`IBR1\^nUKp>sb*CB](rG_`V#YFsos(WPm*Duh9"CiGj*Ei=? -!@1&1rrG%UrVmK-9-#$QWJCNR73*9eGQ7^@4oQH)IJs3D2?"TrLAq2TkiSgQJcC<$JcC<$q#>j~> -"!.EGKDtlRpH/ESNrC%!/H5YPL`IBR1\^nUKp>sb*CB](rG_`V#YFsos(WPm*Duh9"CiGj*Ei=? -!@1&1rrG%UrVmK-9-#$QWJCNR73*9eGQ7^@4oQH)IJs3D2?"TrLAq2TkiSgQJcC<$JcC<$q#>j~> -!$1"t!JQlGrrY\J2M6S\JcC<$JcGNFJ,~> -!$1"t!JQlGrrY\J2M6S\JcC<$JcGNFJ,~> -!$1"t!JQlGrrY\J2M6S\JcC<$JcGNFJ,~> -!$1"t!JQkks+13$s+13Hs*t~> -!$1"t!JQkks+13$s+13Hs*t~> -!$1"t!JQkks+13$s+13Hs*t~> -!$1"t!P?=%s+13$s+13Hs*t~> -!$1"t!P?=%s+13$s+13Hs*t~> -!$1"t!P?=%s+13$s+13Hs*t~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$.@)!V"X)rrM*>JcC<$JcFU,J,~> -!$.@)!V"X)rrM*>JcC<$JcFU,J,~> -!$.@)!V"X)rrM*>JcC<$JcFU,J,~> -!Zh<ur1X1qq#:BJe+3M@aJ,F)rVlu=U8%SZrrL7$rr3)_`W*sUU&`:0rrBk4U&`O-rrVhUQM1=Z -Z2;uL!p66)pAY0d\+'Cuce\R"!9*mN!;6?k!jdU@JcC<$JcFX-J,~> -!Zh<ur1X1qq#:BJe+3M@aJ,F)rVlu=U8%SZrrL7$rr3)_`W*sUU&`:0rrBk4U&`O-rrVhUQM1=Z -Z2;uL!p66)pAY0d\+'Cuce\R"!9*mN!;6?k!jdU@JcC<$JcFX-J,~> -!Zh<ur1X1qq#:BJe+3M@aJ,F)rVlu=U8%SZrrL7$rr3)_`W*sUU&`:0rrBk4U&`O-rrVhUQM1=Z -Z2;uL!p66)pAY0d\+'Cuce\R"!9*mN!;6?k!jdU@JcC<$JcFX-J,~> -"!.E>3;n.'NqiVTQA4u1"0d(en,<7gXEkNRpAY/0Yl=Y*]4(_L=T*OGZ2">!V>Y]@9mZ7-!T6*5 -rrFP?lMgnMW9aHbgNpR2rrLi`rVloY3V!+Sc@:E's8V55rH\HtrVln5Z@W%,s+14-s*t~> -"!.E>3;n.'NqiVTQA4u1"0d(en,<7gXEkNRpAY/0Yl=Y*]4(_L=T*OGZ2">!V>Y]@9mZ7-!T6*5 -rrFP?lMgnMW9aHbgNpR2rrLi`rVloY3V!+Sc@:E's8V55rH\HtrVln5Z@W%,s+14-s*t~> -"!.E>3;n.'NqiVTQA4u1"0d(en,<7gXEkNRpAY/0Yl=Y*]4(_L=T*OGZ2">!V>Y]@9mZ7-!T6*5 -rrFP?lMgnMW9aHbgNpR2rrLi`rVloY3V!+Sc@:E's8V55rH\HtrVln5Z@W%,s+14-s*t~> -"!.E>Fn>V6QA4o/!A3d;rrFG?pAY/0Yl=Y*]4(_L\aTP"fm1^.rrW1;YP%nr..lg)!RjX#rrKB? -rVloY3V*1UdsK?QFR&nK;Z6UqU](2o_KOsjs+13$s5<p-~> -"!.E>Fn>V6QA4o/!A3d;rrFG?pAY/0Yl=Y*]4(_L\aTP"fm1^.rrW1;YP%nr..lg)!RjX#rrKB? -rVloY3V*1UdsK?QFR&nK;Z6UqU](2o_KOsjs+13$s5<p-~> -"!.E>Fn>V6QA4o/!A3d;rrFG?pAY/0Yl=Y*]4(_L\aTP"fm1^.rrW1;YP%nr..lg)!RjX#rrKB? -rVloY3V*1UdsK?QFR&nK;Z6UqU](2o_KOsjs+13$s5<p-~> -"!.E>Fo)+<VuB<p=9&;dL5\bu?2jj(S,Q%\A,Q?-/arT:!@@L6rsdIoh#HGJd/RUdBM2!LoD\j+ -?1.^nrZD%;!*T:o"Ju.u.-LS&!3?,!!)<Gc&@)98@/nYJ9=Os$<^%9l9>1-#!WHO+rs")2s5QaF -V>gK%nk1e^92!h8j#P!U9*!]@rs@Uudf9?f>5QH?p\t7gd"24Js+14.s*t~> -"!.E>Fo)+<VuB<p=9&;dL5\bu?2jj(S,Q%\A,Q?-/arT:!@@L6rsdIoh#HGJd/RUdBM2!LoD\j+ -?1.^nrZD%;!*T:o"Ju.u.-LS&!3?,!!)<Gc&@)98@/nYJ9=Os$<^%9l9>1-#!WHO+rs")2s5QaF -V>gK%nk1e^92!h8j#P!U9*!]@rs@Uudf9?f>5QH?p\t7gd"24Js+14.s*t~> -"!.E>Fo)+<VuB<p=9&;dL5\bu?2jj(S,Q%\A,Q?-/arT:!@@L6rsdIoh#HGJd/RUdBM2!LoD\j+ -?1.^nrZD%;!*T:o"Ju.u.-LS&!3?,!!)<Gc&@)98@/nYJ9=Os$<^%9l9>1-#!WHO+rs")2s5QaF -V>gK%nk1e^92!h8j#P!U9*!]@rs@Uudf9?f>5QH?p\t7gd"24Js+14.s*t~> -"!.E>(&ffgmf3<kIf@_'``2u((m`Rs^g$i5M>km']41a=!A3d;rrFG?p&>IscMuQcs8QRQ\'`Tr -L%YHIj&b4-rrN*@qu6ZGrm:k!Y5]n0rm:jp[JreDrm;:"]`6A3=4,E7_uJ2f4OMREq#:Bn+8u3D -:!`k7d=?cHrsJ\OO]TrXo`*qYAcC'X:[RuX!VbILrr^mPb#8!6!DUpls+13$s5<p-~> -"!.E>(&ffgmf3<kIf@_'``2u((m`Rs^g$i5M>km']41a=!A3d;rrFG?p&>IscMuQcs8QRQ\'`Tr -L%YHIj&b4-rrN*@qu6ZGrm:k!Y5]n0rm:jp[JreDrm;:"]`6A3=4,E7_uJ2f4OMREq#:Bn+8u3D -:!`k7d=?cHrsJ\OO]TrXo`*qYAcC'X:[RuX!VbILrr^mPb#8!6!DUpls+13$s5<p-~> -"!.E>(&ffgmf3<kIf@_'``2u((m`Rs^g$i5M>km']41a=!A3d;rrFG?p&>IscMuQcs8QRQ\'`Tr -L%YHIj&b4-rrN*@qu6ZGrm:k!Y5]n0rm:jp[JreDrm;:"]`6A3=4,E7_uJ2f4OMREq#:Bn+8u3D -:!`k7d=?cHrsJ\OO]TrXo`*qYAcC'X:[RuX!VbILrr^mPb#8!6!DUpls+13$s5<p-~> -"!.E>D>ro*rr<"nIfAsJoC;jHIJNpCju<=#M#R#Idm*g2!A3d;rrFG?p&>J%a8XI[s8V`Yr;Zf' -C%_K,e4B!,!WF2<rrD`koE9K1s!Zt-rrdSCruh:>rs`nKs7mi/s8QrHs8UP>p&>9q+9/Bms%Ui= -rrJ[@r;Qf&C]487j#-H-rrFt?nc&g9;ZHc1*>SMP!DUpls+13$s5<p-~> -"!.E>D>ro*rr<"nIfAsJoC;jHIJNpCju<=#M#R#Idm*g2!A3d;rrFG?p&>J%a8XI[s8V`Yr;Zf' -C%_K,e4B!,!WF2<rrD`koE9K1s!Zt-rrdSCruh:>rs`nKs7mi/s8QrHs8UP>p&>9q+9/Bms%Ui= -rrJ[@r;Qf&C]487j#-H-rrFt?nc&g9;ZHc1*>SMP!DUpls+13$s5<p-~> -"!.E>D>ro*rr<"nIfAsJoC;jHIJNpCju<=#M#R#Idm*g2!A3d;rrFG?p&>J%a8XI[s8V`Yr;Zf' -C%_K,e4B!,!WF2<rrD`koE9K1s!Zt-rrdSCruh:>rs`nKs7mi/s8QrHs8UP>p&>9q+9/Bms%Ui= -rrJ[@r;Qf&C]487j#-H-rrFt?nc&g9;ZHc1*>SMP!DUpls+13$s5<p-~> -"!.E>Fo)+<Q2W071uA7uLAq2Uju<=#(B#W]?2ad(/arT:!@@L4rrOq7-MdZBZYB.5!W"&-rrN*@ -r;QfS2?#!,'V,1Oo`"jnGbtE_rVlg"Dls'8,PqE@dn064#Q5bEV0Dr6ci3qFSUgY<!O6G=rrM.? -rVlmTkjeZRb#83<!$2";!DUpls+13$s5<p-~> -"!.E>Fo)+<Q2W071uA7uLAq2Uju<=#(B#W]?2ad(/arT:!@@L4rrOq7-MdZBZYB.5!W"&-rrN*@ -r;QfS2?#!,'V,1Oo`"jnGbtE_rVlg"Dls'8,PqE@dn064#Q5bEV0Dr6ci3qFSUgY<!O6G=rrM.? -rVlmTkjeZRb#83<!$2";!DUpls+13$s5<p-~> -"!.E>Fo)+<Q2W071uA7uLAq2Uju<=#(B#W]?2ad(/arT:!@@L4rrOq7-MdZBZYB.5!W"&-rrN*@ -r;QfS2?#!,'V,1Oo`"jnGbtE_rVlg"Dls'8,PqE@dn064#Q5bEV0Dr6ci3qFSUgY<!O6G=rrM.? -rVlmTkjeZRb#83<!$2";!DUpls+13$s5<p-~> -"!.E>Fo21>jkTk8"R[oBQA5D="QhZCNfNo7!A3d;rrFG?o`"tIi[4[)!S-Q9rrKH?rVlo1ao)/> -^0^1+!rc-@rVm0Ym/R+I?(CpC\,QC1GbtE_rVlg"Dls'8,PqEDdn0T>e*Zu2#Q5b5OaQ._ci3qF -SUgY<#I/(Es2t)r3W8sY2!FK0!Qn==rr=)<rrUech1>TWs+14.s*t~> -"!.E>Fo21>jkTk8"R[oBQA5D="QhZCNfNo7!A3d;rrFG?o`"tIi[4[)!S-Q9rrKH?rVlo1ao)/> -^0^1+!rc-@rVm0Ym/R+I?(CpC\,QC1GbtE_rVlg"Dls'8,PqEDdn0T>e*Zu2#Q5b5OaQ._ci3qF -SUgY<#I/(Es2t)r3W8sY2!FK0!Qn==rr=)<rrUech1>TWs+14.s*t~> -"!.E>Fo21>jkTk8"R[oBQA5D="QhZCNfNo7!A3d;rrFG?o`"tIi[4[)!S-Q9rrKH?rVlo1ao)/> -^0^1+!rc-@rVm0Ym/R+I?(CpC\,QC1GbtE_rVlg"Dls'8,PqEDdn0T>e*Zu2#Q5b5OaQ._ci3qF -SUgY<#I/(Es2t)r3W8sY2!FK0!Qn==rr=)<rrUech1>TWs+14.s*t~> -"!.E>8H#(]XT-4grga1[IfG^grr3,`2ugF@rga%hrr3,?SK*iqrr3,<SJRZup&>)DQ?rQ1!M>AN -SH4YDrVlm#2uN[U*U<Y*i&pu<$%SG7SVAkhs!V@USHOD^s)sq3SHO>bs7mo9rrqJ)SXk#Wq>Us( -Boe[jKQQ2Qn=<r_S`TkN#L@afST*rU3W8sY2!FK0!T%ttSH*F'rrHl?JcC<$JcFX-J,~> -"!.E>8H#(]XT-4grga1[IfG^grr3,`2ugF@rga%hrr3,?SK*iqrr3,<SJRZup&>)DQ?rQ1!M>AN -SH4YDrVlm#2uN[U*U<Y*i&pu<$%SG7SVAkhs!V@USHOD^s)sq3SHO>bs7mo9rrqJ)SXk#Wq>Us( -Boe[jKQQ2Qn=<r_S`TkN#L@afST*rU3W8sY2!FK0!T%ttSH*F'rrHl?JcC<$JcFX-J,~> -"!.E>8H#(]XT-4grga1[IfG^grr3,`2ugF@rga%hrr3,?SK*iqrr3,<SJRZup&>)DQ?rQ1!M>AN -SH4YDrVlm#2uN[U*U<Y*i&pu<$%SG7SVAkhs!V@USHOD^s)sq3SHO>bs7mo9rrqJ)SXk#Wq>Us( -Boe[jKQQ2Qn=<r_S`TkN#L@afST*rU3W8sY2!FK0!T%ttSH*F'rrHl?JcC<$JcFX-J,~> -!Zh<ir-ng3s8Tn6IftQ,s2r4Xrrhn]s8TS-IfPT0rrBD)IfP`4rrB8%IfPl.rrA)WrrAemIfQ>C -rrIY>r;QbWlMgqTJ$&\L#`4%\YeSH_POSR$!5ng9!.b&u"NUQBr/pgT"586Sd.dPGn"'LY[+PEY -lhu;5h#76Waa\g!s7)TWrrJP[nG`L>rI4h<rr3&;J(]DQJcC<$huA3~> -!Zh<ir-ng3s8Tn6IftQ,s2r4Xrrhn]s8TS-IfPT0rrBD)IfP`4rrB8%IfPl.rrA)WrrAemIfQ>C -rrIY>r;QbWlMgqTJ$&\L#`4%\YeSH_POSR$!5ng9!.b&u"NUQBr/pgT"586Sd.dPGn"'LY[+PEY -lhu;5h#76Waa\g!s7)TWrrJP[nG`L>rI4h<rr3&;J(]DQJcC<$huA3~> -!Zh<ir-ng3s8Tn6IftQ,s2r4Xrrhn]s8TS-IfPT0rrBD)IfP`4rrB8%IfPl.rrA)WrrAemIfQ>C -rrIY>r;QbWlMgqTJ$&\L#`4%\YeSH_POSR$!5ng9!.b&u"NUQBr/pgT"586Sd.dPGn"'LY[+PEY -lhu;5h#76Waa\g!s7)TWrrJP[nG`L>rI4h<rr3&;J(]DQJcC<$huA3~> -!$.@)!U.7_rrLKtJcC<$JcFU,J,~> -!$.@)!U.7_rrLKtJcC<$JcFU,J,~> -!$.@)!U.7_rrLKtJcC<$JcFU,J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$2%<!VH<grrMlioD\pdn*g5QrrVf\rndYUo_[nQ"9.cXpAP!ln+6&K!VZ?grrW(rKA$8/oDJXg -qX=1=rrMiio`#!en,'Hk!THTHrrG4Kqu6[UnU^^ks2P(h~> -!$2%<!VH<grrMlioD\pdn*g5QrrVf\rndYUo_[nQ"9.cXpAP!ln+6&K!VZ?grrW(rKA$8/oDJXg -qX=1=rrMiio`#!en,'Hk!THTHrrG4Kqu6[UnU^^ks2P(h~> -!$2%<!VH<grrMlioD\pdn*g5QrrVf\rndYUo_[nQ"9.cXpAP!ln+6&K!VZ?grrW(rKA$8/oDJXg -qX=1=rrMiio`#!en,'Hk!THTHrrG4Kqu6[UnU^^ks2P(h~> -!$2(=!p)_mrVlqdOn//E",\Z;k5>5\CG#,LrrLpOmJd2k_<Lt+`@WZ`lMpn`/RS#I!I1I?rrL/? -f`).EfDklc/Y1u&!pa0mo`"uX/]ZZH!TE_;rrLpOo)A\1qYpO9qu6]o,(]cFs2P(h~> -!$2(=!p)_mrVlqdOn//E",\Z;k5>5\CG#,LrrLpOmJd2k_<Lt+`@WZ`lMpn`/RS#I!I1I?rrL/? -f`).EfDklc/Y1u&!pa0mo`"uX/]ZZH!TE_;rrLpOo)A\1qYpO9qu6]o,(]cFs2P(h~> -!$2(=!p)_mrVlqdOn//E",\Z;k5>5\CG#,LrrLpOmJd2k_<Lt+`@WZ`lMpn`/RS#I!I1I?rrL/? -f`).EfDklc/Y1u&!pa0mo`"uX/]ZZH!TE_;rrLpOo)A\1qYpO9qu6]o,(]cFs2P(h~> -"s*aDh09a\r;Qh\Qh0hK!B0*,rrG1?mJd2k_<Lt&*WQ/%MsLBIGbtH?pRR)lrrGR?i;WoBL*<S@ -rrH*?li-uIiUd'M_-?d9!Ed>=rrW+#a+=8As2Y.i~> -"s*aDh09a\r;Qh\Qh0hK!B0*,rrG1?mJd2k_<Lt&*WQ/%MsLBIGbtH?pRR)lrrGR?i;WoBL*<S@ -rrH*?li-uIiUd'M_-?d9!Ed>=rrW+#a+=8As2Y.i~> -"s*aDh09a\r;Qh\Qh0hK!B0*,rrG1?mJd2k_<Lt&*WQ/%MsLBIGbtH?pRR)lrrGR?i;WoBL*<S@ -rrH*?li-uIiUd'M_-?d9!Ed>=rrW+#a+=8As2Y.i~> -"s*a!J\Fq)qu6_[OS\VL!B0*=rr^-*R/$X[!5JN##d+.,a-\'+R'?Si!4`#q!42V'!M-:jrrH?? -rr3,W`rH(Arg3u*s8TOps8V)prVm0Es8TdDs8Sj]Yl4S&XRlFY!6bBA$(RBM_g&$Xs5Z0;rrE#r -Qiu"?s.o&]Qil%As7ZDY"5NqVo)4pXi;`iGrg3rB7G%S*R$c5!rrVhrh#<ZClMpnGrg3cHWqlJj -"R:fBEiSg4"4rp-jSf)YeGYd4rrT]tpAY'lc2O(5o;_ijao7Y5UAt8AQnilQV>gJp_Z#o6WW3"A -fX$s4XT/=@Qm7?QYl=Y&\GhiqZi0n).e<H9!Tl<;rrLq?JcC<$a8^Y~> -"s*a!J\Fq)qu6_[OS\VL!B0*=rr^-*R/$X[!5JN##d+.,a-\'+R'?Si!4`#q!42V'!M-:jrrH?? -rr3,W`rH(Arg3u*s8TOps8V)prVm0Es8TdDs8Sj]Yl4S&XRlFY!6bBA$(RBM_g&$Xs5Z0;rrE#r -Qiu"?s.o&]Qil%As7ZDY"5NqVo)4pXi;`iGrg3rB7G%S*R$c5!rrVhrh#<ZClMpnGrg3cHWqlJj -"R:fBEiSg4"4rp-jSf)YeGYd4rrT]tpAY'lc2O(5o;_ijao7Y5UAt8AQnilQV>gJp_Z#o6WW3"A -fX$s4XT/=@Qm7?QYl=Y&\GhiqZi0n).e<H9!Tl<;rrLq?JcC<$a8^Y~> -"s*a!J\Fq)qu6_[OS\VL!B0*=rr^-*R/$X[!5JN##d+.,a-\'+R'?Si!4`#q!42V'!M-:jrrH?? -rr3,W`rH(Arg3u*s8TOps8V)prVm0Es8TdDs8Sj]Yl4S&XRlFY!6bBA$(RBM_g&$Xs5Z0;rrE#r -Qiu"?s.o&]Qil%As7ZDY"5NqVo)4pXi;`iGrg3rB7G%S*R$c5!rrVhrh#<ZClMpnGrg3cHWqlJj -"R:fBEiSg4"4rp-jSf)YeGYd4rrT]tpAY'lc2O(5o;_ijao7Y5UAt8AQnilQV>gJp_Z#o6WW3"A -fX$s4XT/=@Qm7?QYl=Y&\GhiqZi0n).e<H9!Tl<;rrLq?JcC<$a8^Y~> -"s*`o]`1dPqYpS`K_59F2Wju<"1Ek"n,E=f]Rg'8%"b>U]S%>KKVAGGZ[r+/!J7HPrrEN]rr3:/ -_>j(QI_5WWK)UE/J\(kRdf9?VB)V`0*W,j<O`W_arrFipre(B(bl<e(KEcrfV>pSV3;idVL])l/ -J(ai;IK"m%Ibk$RrIb9%ir4?(KEH\cqL&9q#C[iJs6,,1M#RDUiu<IHKEHYom!\kd!e,arq>UTZ -XT*=@p&>-?KJL1=rrL`$rIb0.rVm'u!9sO`c@>hH"FQ[<aajAD%"joHab23XKTuN:_1;N<'S<"X -IS>?HKS$$"]n@GLKV8AFZ[r+/!J7HQrrFV?qYpTY2Z*LTh*6JjJcEdjJ,~> -"s*`o]`1dPqYpS`K_59F2Wju<"1Ek"n,E=f]Rg'8%"b>U]S%>KKVAGGZ[r+/!J7HPrrEN]rr3:/ -_>j(QI_5WWK)UE/J\(kRdf9?VB)V`0*W,j<O`W_arrFipre(B(bl<e(KEcrfV>pSV3;idVL])l/ -J(ai;IK"m%Ibk$RrIb9%ir4?(KEH\cqL&9q#C[iJs6,,1M#RDUiu<IHKEHYom!\kd!e,arq>UTZ -XT*=@p&>-?KJL1=rrL`$rIb0.rVm'u!9sO`c@>hH"FQ[<aajAD%"joHab23XKTuN:_1;N<'S<"X -IS>?HKS$$"]n@GLKV8AFZ[r+/!J7HQrrFV?qYpTY2Z*LTh*6JjJcEdjJ,~> -"s*`o]`1dPqYpS`K_59F2Wju<"1Ek"n,E=f]Rg'8%"b>U]S%>KKVAGGZ[r+/!J7HPrrEN]rr3:/ -_>j(QI_5WWK)UE/J\(kRdf9?VB)V`0*W,j<O`W_arrFipre(B(bl<e(KEcrfV>pSV3;idVL])l/ -J(ai;IK"m%Ibk$RrIb9%ir4?(KEH\cqL&9q#C[iJs6,,1M#RDUiu<IHKEHYom!\kd!e,arq>UTZ -XT*=@p&>-?KJL1=rrL`$rIb0.rVm'u!9sO`c@>hH"FQ[<aajAD%"joHab23XKTuN:_1;N<'S<"X -IS>?HKS$$"]n@GLKV8AFZ[r+/!J7HQrrFV?qYpTY2Z*LTh*6JjJcEdjJ,~> -!$2%<!Dgu9rr=)3rrG%?qu6[Kn,E=fI@pN=!URQ"rrG1?rVln7Yl=Y'k/I<!!N!+$rs;oGF(M&\ -s8Q$?rr3M'HN-Ucs8TQ?s8Rm]$XStnrrM%?rVlmQR/[*pJ=QWfo`+sD9;V[gj>d);!$2%<"D>1C -(pX)?"(&_A*W?!?_!:k?rVlsoch&XbrrU.ifDbdNP_f>=!okQBrr3'WkhAE&rrG^@p&>&5Wq65k -@CuO=!O6J>rr=)<rs$32p](8dIK'6L*WQ/+L&V)Qk;N>>!E@/=rrIk?rr3;U8,n$Kk5YJ+:]C@p -45p/<!H#%>rrM9#rr3!Ko(r@eju3,:!Sotks+13js*t~> -!$2%<!Dgu9rr=)3rrG%?qu6[Kn,E=fI@pN=!URQ"rrG1?rVln7Yl=Y'k/I<!!N!+$rs;oGF(M&\ -s8Q$?rr3M'HN-Ucs8TQ?s8Rm]$XStnrrM%?rVlmQR/[*pJ=QWfo`+sD9;V[gj>d);!$2%<"D>1C -(pX)?"(&_A*W?!?_!:k?rVlsoch&XbrrU.ifDbdNP_f>=!okQBrr3'WkhAE&rrG^@p&>&5Wq65k -@CuO=!O6J>rr=)<rs$32p](8dIK'6L*WQ/+L&V)Qk;N>>!E@/=rrIk?rr3;U8,n$Kk5YJ+:]C@p -45p/<!H#%>rrM9#rr3!Ko(r@eju3,:!Sotks+13js*t~> -!$2%<!Dgu9rr=)3rrG%?qu6[Kn,E=fI@pN=!URQ"rrG1?rVln7Yl=Y'k/I<!!N!+$rs;oGF(M&\ -s8Q$?rr3M'HN-Ucs8TQ?s8Rm]$XStnrrM%?rVlmQR/[*pJ=QWfo`+sD9;V[gj>d);!$2%<"D>1C -(pX)?"(&_A*W?!?_!:k?rVlsoch&XbrrU.ifDbdNP_f>=!okQBrr3'WkhAE&rrG^@p&>&5Wq65k -@CuO=!O6J>rr=)<rs$32p](8dIK'6L*WQ/+L&V)Qk;N>>!E@/=rrIk?rr3;U8,n$Kk5YJ+:]C@p -45p/<!H#%>rrM9#rr3!Ko(r@eju3,:!Sotks+13js*t~> -!$2%<!Dgu:rrHN?o`"qMk55/Z/ar]=!T%ep5lbQrrrG1?rVloOB`:9tFSGe;>;EA"rVlms2#]cO -.=2"ddf9?VB)hnV^)"H2r;QfU4T59\1$no>"G!$B..mN="aHmDs5Z0;rr<r85m&%6ruM(<"(&_A -*W,j;r?)"<"&7,6./j/H7G%P=!L/E>rrM.?rVlsVkhAE&rrG^@p&>&5Wq65k@CuO=!O6J>rr=)7 -rrJd@rr3*As8SE0r]pQH2ZE^W<65%<!JZp>rrgnCs*gR=rrLA?rr3!\iVicWg1^IO!-A,=!@m[: -rrM7?qu6]Q5Crics2Y.i~> -!$2%<!Dgu:rrHN?o`"qMk55/Z/ar]=!T%ep5lbQrrrG1?rVloOB`:9tFSGe;>;EA"rVlms2#]cO -.=2"ddf9?VB)hnV^)"H2r;QfU4T59\1$no>"G!$B..mN="aHmDs5Z0;rr<r85m&%6ruM(<"(&_A -*W,j;r?)"<"&7,6./j/H7G%P=!L/E>rrM.?rVlsVkhAE&rrG^@p&>&5Wq65k@CuO=!O6J>rr=)7 -rrJd@rr3*As8SE0r]pQH2ZE^W<65%<!JZp>rrgnCs*gR=rrLA?rr3!\iVicWg1^IO!-A,=!@m[: -rrM7?qu6]Q5Crics2Y.i~> -!$2%<!Dgu:rrHN?o`"qMk55/Z/ar]=!T%ep5lbQrrrG1?rVloOB`:9tFSGe;>;EA"rVlms2#]cO -.=2"ddf9?VB)hnV^)"H2r;QfU4T59\1$no>"G!$B..mN="aHmDs5Z0;rr<r85m&%6ruM(<"(&_A -*W,j;r?)"<"&7,6./j/H7G%P=!L/E>rrM.?rVlsVkhAE&rrG^@p&>&5Wq65k@CuO=!O6J>rr=)7 -rrJd@rr3*As8SE0r]pQH2ZE^W<65%<!JZp>rrgnCs*gR=rrLA?rr3!\iVicWg1^IO!-A,=!@m[: -rrM7?qu6]Q5Crics2Y.i~> -!$2%<!Dgu;rrQWBrUg*j2Wjo:!A3d=rrD9^gB"`srr3![ir&fVk5O*9Wd+=="'_Wb48o0[;m$#Q -#OMI_df9?VB)V`0*W#d:i&pu<!AWs?rrdkBs![O=rrmYDs8V.>r;Qa:r7_;GruM(<"(&_A*W,j; -r?)"<"&7,6./j/H7G%P=!L/E>rrM.?rVlsVkk_C[,QXh=p&>&5Wq65k@CuO=!O6J>rr=)7rrMYA -rZhWqs8SF+rS%>3rr3!uaSl,>L6hi="P-*CI\-Q=!R4F>rrG4?r;[email protected]<H9!Tl<; -rrLq?JcC<$a8^Y~> -!$2%<!Dgu;rrQWBrUg*j2Wjo:!A3d=rrD9^gB"`srr3![ir&fVk5O*9Wd+=="'_Wb48o0[;m$#Q -#OMI_df9?VB)V`0*W#d:i&pu<!AWs?rrdkBs![O=rrmYDs8V.>r;Qa:r7_;GruM(<"(&_A*W,j; -r?)"<"&7,6./j/H7G%P=!L/E>rrM.?rVlsVkk_C[,QXh=p&>&5Wq65k@CuO=!O6J>rr=)7rrMYA -rZhWqs8SF+rS%>3rr3!uaSl,>L6hi="P-*CI\-Q=!R4F>rrG4?r;[email protected]<H9!Tl<; -rrLq?JcC<$a8^Y~> -!$2%<!Dgu;rrQWBrUg*j2Wjo:!A3d=rrD9^gB"`srr3![ir&fVk5O*9Wd+=="'_Wb48o0[;m$#Q -#OMI_df9?VB)V`0*W#d:i&pu<!AWs?rrdkBs![O=rrmYDs8V.>r;Qa:r7_;GruM(<"(&_A*W,j; -r?)"<"&7,6./j/H7G%P=!L/E>rrM.?rVlsVkk_C[,QXh=p&>&5Wq65k@CuO=!O6J>rr=)7rrMYA -rZhWqs8SF+rS%>3rr3!uaSl,>L6hi="P-*CI\-Q=!R4F>rrG4?r;[email protected]<H9!Tl<; -rrLq?JcC<$a8^Y~> -%NYTElKUsRlL"WOoPM5>p&>0dl7pcSrr38pl71QUs8S%KrosO!?iL'19%*"9C]Ag_rosNhDZ0S9 -'/fdh$!Y7@i>l@us'`V?l3Qq[s&m;*`t`4brr=):rs7u:2sU&8s"Wm>rsjRLs#T-(lBDdTs8W)9 ->5eI$0`C8=pAb.5rVlt+^Afhal2tM?r@e-7"'3nD2u`(NC^AYmqs&uclMCP@3W8s[2!Egmro*tA -qu-O&IgEmjs82*HBq4JRrVlq:#grYO#l!UeX6T]a[r;61!&4BO#$)#ps8T-Jro+(Hs8SaFrosLM -rVm+4]^!gBs,`ECl3YL3s*bTflKZ/krr33rdH\>Ys*h!Il2e2-rr3#p,l7NA,Ph9;!rFA@r;Qii -#YfmMJcF'rJ,~> -%NYTElKUsRlL"WOoPM5>p&>0dl7pcSrr38pl71QUs8S%KrosO!?iL'19%*"9C]Ag_rosNhDZ0S9 -'/fdh$!Y7@i>l@us'`V?l3Qq[s&m;*`t`4brr=):rs7u:2sU&8s"Wm>rsjRLs#T-(lBDdTs8W)9 ->5eI$0`C8=pAb.5rVlt+^Afhal2tM?r@e-7"'3nD2u`(NC^AYmqs&uclMCP@3W8s[2!Egmro*tA -qu-O&IgEmjs82*HBq4JRrVlq:#grYO#l!UeX6T]a[r;61!&4BO#$)#ps8T-Jro+(Hs8SaFrosLM -rVm+4]^!gBs,`ECl3YL3s*bTflKZ/krr33rdH\>Ys*h!Il2e2-rr3#p,l7NA,Ph9;!rFA@r;Qii -#YfmMJcF'rJ,~> -%NYTElKUsRlL"WOoPM5>p&>0dl7pcSrr38pl71QUs8S%KrosO!?iL'19%*"9C]Ag_rosNhDZ0S9 -'/fdh$!Y7@i>l@us'`V?l3Qq[s&m;*`t`4brr=):rs7u:2sU&8s"Wm>rsjRLs#T-(lBDdTs8W)9 ->5eI$0`C8=pAb.5rVlt+^Afhal2tM?r@e-7"'3nD2u`(NC^AYmqs&uclMCP@3W8s[2!Egmro*tA -qu-O&IgEmjs82*HBq4JRrVlq:#grYO#l!UeX6T]a[r;61!&4BO#$)#ps8T-Jro+(Hs8SaFrosLM -rVm+4]^!gBs,`ECl3YL3s*bTflKZ/krr33rdH\>Ys*h!Il2e2-rr3#p,l7NA,Ph9;!rFA@r;Qii -#YfmMJcF'rJ,~> -!Zh<Nr%\CPs8SOkoD\fMr\=IJrr2tQr\=IQrr2tJr\=IXr;Qgi11L7_!-.un!,;B2!BaK`rrIJk -rr3,5NrT,br\=J%rr3+%10(eqrVljiqu6Xfr\=V=s8Q6jrr3:mYlF_b1,=WJ\,QC/dUqP=rrDoo -1'=ZYs%<7i"+JGnmJSdB])VfmrA"Jks8V!U1'FgqhuD@-1'>l&l!XJi"'k6gV>W.MZMa_%"dK_8 -s8UXI1&s31rrRrdd/O%FYPg3Yp](8mrA"BPrVm(C1@>,Am",-k"?b98s-3L<!(Qnd!a[WVrr2tR -r\=aXs8RP>BJM>Hr;Qgj11C1^!-8&o!,2<1!CkZ<rrLS?qu6]C:&FqloeL<6s+13rs*t~> -!Zh<Nr%\CPs8SOkoD\fMr\=IJrr2tQr\=IQrr2tJr\=IXr;Qgi11L7_!-.un!,;B2!BaK`rrIJk -rr3,5NrT,br\=J%rr3+%10(eqrVljiqu6Xfr\=V=s8Q6jrr3:mYlF_b1,=WJ\,QC/dUqP=rrDoo -1'=ZYs%<7i"+JGnmJSdB])VfmrA"Jks8V!U1'FgqhuD@-1'>l&l!XJi"'k6gV>W.MZMa_%"dK_8 -s8UXI1&s31rrRrdd/O%FYPg3Yp](8mrA"BPrVm(C1@>,Am",-k"?b98s-3L<!(Qnd!a[WVrr2tR -r\=aXs8RP>BJM>Hr;Qgj11C1^!-8&o!,2<1!CkZ<rrLS?qu6]C:&FqloeL<6s+13rs*t~> -!Zh<Nr%\CPs8SOkoD\fMr\=IJrr2tQr\=IQrr2tJr\=IXr;Qgi11L7_!-.un!,;B2!BaK`rrIJk -rr3,5NrT,br\=J%rr3+%10(eqrVljiqu6Xfr\=V=s8Q6jrr3:mYlF_b1,=WJ\,QC/dUqP=rrDoo -1'=ZYs%<7i"+JGnmJSdB])VfmrA"Jks8V!U1'FgqhuD@-1'>l&l!XJi"'k6gV>W.MZMa_%"dK_8 -s8UXI1&s31rrRrdd/O%FYPg3Yp](8mrA"BPrVm(C1@>,Am",-k"?b98s-3L<!(Qnd!a[WVrr2tR -r\=aXs8RP>BJM>Hr;Qgj11C1^!-8&o!,2<1!CkZ<rrLS?qu6]C:&FqloeL<6s+13rs*t~> -!$/$<"ip05s0lUqrrL'0a8Z1p62gfa/+NT<!F<J3rrGj@kPkRUIK'6Imk*c'!IgX(rrMb1r;Qe0 -r;6Ko@tFZ2s2G"g~> -!$/$<"ip05s0lUqrrL'0a8Z1p62gfa/+NT<!F<J3rrGj@kPkRUIK'6Imk*c'!IgX(rrMb1r;Qe0 -r;6Ko@tFZ2s2G"g~> -!$/$<"ip05s0lUqrrL'0a8Z1p62gfa/+NT<!F<J3rrGj@kPkRUIK'6Imk*c'!IgX(rrMb1r;Qe0 -r;6Ko@tFZ2s2G"g~> -!$/$<"db13PDH-0rrMD.rK@8'p\t0oa*QJ3rrTu\jQHODaFF2Q!Ki-#rrIS?JcC<$SH"*~> -!$/$<"db13PDH-0rrMD.rK@8'p\t0oa*QJ3rrTu\jQHODaFF2Q!Ki-#rrIS?JcC<$SH"*~> -!$/$<"db13PDH-0rrMD.rK@8'p\t0oa*QJ3rrTu\jQHODaFF2Q!Ki-#rrIS?JcC<$SH"*~> -!$/!;".k@+Wh04jiVeT5li$ha_US2W!P7(JrrC%;M#`Y#rrL$cJcC<$SH"*~> -!$/!;".k@+Wh04jiVeT5li$ha_US2W!P7(JrrC%;M#`Y#rrL$cJcC<$SH"*~> -!$/!;".k@+Wh04jiVeT5li$ha_US2W!P7(JrrC%;M#`Y#rrL$cJcC<$SH"*~> -!$2";!d\!iJc>iPBN]t=s+13$s,R,0~> -!$2";!d\!iJc>iPBN]t=s+13$s,R,0~> -!$2";!d\!iJc>iPBN]t=s+13$s,R,0~> -!$2%<"*pi_bhN-ufQmJr!GFXFrrGGskl1[N](l:-aMX^)JcC<$JcCf2J,~> -!$2%<"*pi_bhN-ufQmJr!GFXFrrGGskl1[N](l:-aMX^)JcC<$JcCf2J,~> -!$2%<"*pi_bhN-ufQmJr!GFXFrrGGskl1[N](l:-aMX^)JcC<$JcCf2J,~> -!$2%<!DgtnrrL>?rr3#^i7%],i9'8'!I1I7rrKi?JcC<$JcCf2J,~> -!$2%<!DgtnrrL>?rr3#^i7%],i9'8'!I1I7rrKi?JcC<$JcCf2J,~> -!$2%<!DgtnrrL>?rr3#^i7%],i9'8'!I1I7rrKi?JcC<$JcCf2J,~> -!$2%<!b/kArr2uhroFFJs8V`^mcEQnrr2ufroF.Drr2ueroF.Err3>ojlP[L;#gR`jlQI@"n;9O -jlPk.rrqcNjluO/q#:fjq>^Kimf3=RqW?o$n,E=gkiM+-rrD9^jT+iMrrD6]jT+lNrs.]Jjm[Mk -s8W&Z!;-9j!rK6?JcC<$JcCf2J,~> -!$2%<!b/kArr2uhroFFJs8V`^mcEQnrr2ufroF.Drr2ueroF.Err3>ojlP[L;#gR`jlQI@"n;9O -jlPk.rrqcNjluO/q#:fjq>^Kimf3=RqW?o$n,E=gkiM+-rrD9^jT+iMrrD6]jT+lNrs.]Jjm[Mk -s8W&Z!;-9j!rK6?JcC<$JcCf2J,~> -!$2%<!b/kArr2uhroFFJs8V`^mcEQnrr2ufroF.Drr2ueroF.Err3>ojlP[L;#gR`jlQI@"n;9O -jlPk.rrqcNjluO/q#:fjq>^Kimf3=RqW?o$n,E=gkiM+-rrD9^jT+iMrrD6]jT+lNrs.]Jjm[Mk -s8W&Z!;-9j!rK6?JcC<$JcCf2J,~> -!$2(=#N*U%n,NFQJc7S:6.5e!BkoXd7b%J#GQ'N(9%Et'ErJ!-:Y5X,D)XC@TMY[gHoD</rsik& -;`?XAh>c;<2`FX*MYR2b;p,.?^Kpm>8u4d32f[pYs&:a0rVloJ8,bFMHKbCW6N/nPJ*R'\4Z><@ -?D[\J3W:rBM=(?Cj'9[bJcC<$JcCi3J,~> -!$2(=#N*U%n,NFQJc7S:6.5e!BkoXd7b%J#GQ'N(9%Et'ErJ!-:Y5X,D)XC@TMY[gHoD</rsik& -;`?XAh>c;<2`FX*MYR2b;p,.?^Kpm>8u4d32f[pYs&:a0rVloJ8,bFMHKbCW6N/nPJ*R'\4Z><@ -?D[\J3W:rBM=(?Cj'9[bJcC<$JcCi3J,~> -!$2(=#N*U%n,NFQJc7S:6.5e!BkoXd7b%J#GQ'N(9%Et'ErJ!-:Y5X,D)XC@TMY[gHoD</rsik& -;`?XAh>c;<2`FX*MYR2b;p,.?^Kpm>8u4d32f[pYs&:a0rVloJ8,bFMHKbCW6N/nPJ*R'\4Z><@ -?D[\J3W:rBM=(?Cj'9[bJcC<$JcCi3J,~> -!$2(=!c7q^rr3"kIfB?UmOnO*/AhGeju<=#NrK%]k^NPcQi@!keO]^gTE"r]6W!a]!$2%<%;\)^ -s8T]>s'rV>s3OI6rshuMs8Ti>s8P\[p&G&[KDtlOkqi;<!BK3>rrfR8s"Wm>rsXFJs![O>s0sGQ -ruh:>rrI&?rr3&6!.0:sJcC<$OT0h~> -!$2(=!c7q^rr3"kIfB?UmOnO*/AhGeju<=#NrK%]k^NPcQi@!keO]^gTE"r]6W!a]!$2%<%;\)^ -s8T]>s'rV>s3OI6rshuMs8Ti>s8P\[p&G&[KDtlOkqi;<!BK3>rrfR8s"Wm>rsXFJs![O>s0sGQ -ruh:>rrI&?rr3&6!.0:sJcC<$OT0h~> -!$2(=!c7q^rr3"kIfB?UmOnO*/AhGeju<=#NrK%]k^NPcQi@!keO]^gTE"r]6W!a]!$2%<%;\)^ -s8T]>s'rV>s3OI6rshuMs8Ti>s8P\[p&G&[KDtlOkqi;<!BK3>rrfR8s"Wm>rsXFJs![O>s0sGQ -ruh:>rrI&?rr3&6!.0:sJcC<$OT0h~> -!$2(=#Hi'&pAb/b7fJDNKeECkLAq2Uju<=#Nr/hWKpVf="P$'CI@pN=!R+C=rr=)<rrHr?rr389 -@K0iJs8UG>p\tOub5_LV?2spsdf07IR=kM=!U2E=rrG.?qu6[Om/I"fJ=QWfo`"jnGbtE_NW+qC -5-=kbmtYnkJcC<$JcCi3J,~> -!$2(=#Hi'&pAb/b7fJDNKeECkLAq2Uju<=#Nr/hWKpVf="P$'CI@pN=!R+C=rr=)<rrHr?rr389 -@K0iJs8UG>p\tOub5_LV?2spsdf07IR=kM=!U2E=rrG.?qu6[Om/I"fJ=QWfo`"jnGbtE_NW+qC -5-=kbmtYnkJcC<$JcCi3J,~> -!$2(=#Hi'&pAb/b7fJDNKeECkLAq2Uju<=#Nr/hWKpVf="P$'CI@pN=!R+C=rr=)<rrHr?rr389 -@K0iJs8UG>p\tOub5_LV?2spsdf07IR=kM=!U2E=rrG.?qu6[Om/I"fJ=QWfo`"jnGbtE_NW+qC -5-=kbmtYnkJcC<$JcCi3J,~> -!$2%<#+^SDs8S]\rJguSs-AE=rrhICs,N-:rrIh?rr3,O8H4+1rr3#C;#UCo*W?!=Cp<p=#eOOF -\p8:8G-L`@#uf"Hs1_k>s%:`=rrJO?rr3#`1]@=S3TL#:!AWs?rrdkBs![O=rrdSCrud="NWn2; -s6k`>JcC<$JcCf2J,~> -!$2%<#+^SDs8S]\rJguSs-AE=rrhICs,N-:rrIh?rr3,O8H4+1rr3#C;#UCo*W?!=Cp<p=#eOOF -\p8:8G-L`@#uf"Hs1_k>s%:`=rrJO?rr3#`1]@=S3TL#:!AWs?rrdkBs![O=rrdSCrud="NWn2; -s6k`>JcC<$JcCf2J,~> -!$2%<#+^SDs8S]\rJguSs-AE=rrhICs,N-:rrIh?rr3,O8H4+1rr3#C;#UCo*W?!=Cp<p=#eOOF -\p8:8G-L`@#uf"Hs1_k>s%:`=rrJO?rr3#`1]@=S3TL#:!AWs?rrdkBs![O=rrdSCrud="NWn2; -s6k`>JcC<$JcCf2J,~> -!$2%<!Dgu>rrJa@qu6\_LAq2Uju<=#NrK%]k^NPcQi@!keO]^gTE"r]6W!a]!$2%<!Go">rs-/E -s-JqMX1J3.!EOLFrs;WHs8Ti>s8P^>rr3"eKDtlOkqi;<!BK3>rrfR8s"Wm>rsXFJs![O>s0sGQ -ruh:9rrKi?JcC<$JcCf2J,~> -!$2%<!Dgu>rrJa@qu6\_LAq2Uju<=#NrK%]k^NPcQi@!keO]^gTE"r]6W!a]!$2%<!Go">rs-/E -s-JqMX1J3.!EOLFrs;WHs8Ti>s8P^>rr3"eKDtlOkqi;<!BK3>rrfR8s"Wm>rsXFJs![O>s0sGQ -ruh:9rrKi?JcC<$JcCf2J,~> -!$2%<!Dgu>rrJa@qu6\_LAq2Uju<=#NrK%]k^NPcQi@!keO]^gTE"r]6W!a]!$2%<!Go">rs-/E -s-JqMX1J3.!EOLFrs;WHs8Ti>s8P^>rr3"eKDtlOkqi;<!BK3>rrfR8s"Wm>rsXFJs![O>s0sGQ -ruh:9rrKi?JcC<$JcCf2J,~> -!$2%<!Dgu>rrMD9r\jsKs8SC>rr3,`2ui#ar\jsPec4`Or\kNefDjlJ2`F*s;#gQC2[23Qs8Qo> -rr3,[email protected]\jgur;Qd"2u`g`g/U'j>EbER9%*_=%%2bJs&:`]2i[k;e49Ks"FB;3dR*pl%"IRB -coj<HF'b^CcT1t`!3#kr!PVlks+13$s,[21~> -!$2%<!Dgu>rrMD9r\jsKs8SC>rr3,`2ui#ar\jsPec4`Or\kNefDjlJ2`F*s;#gQC2[23Qs8Qo> -rr3,[email protected]\jgur;Qd"2u`g`g/U'j>EbER9%*_=%%2bJs&:`]2i[k;e49Ks"FB;3dR*pl%"IRB -coj<HF'b^CcT1t`!3#kr!PVlks+13$s,[21~> -!$2%<!Dgu>rrMD9r\jsKs8SC>rr3,`2ui#ar\jsPec4`Or\kNefDjlJ2`F*s;#gQC2[23Qs8Qo> -rr3,[email protected]\jgur;Qd"2u`g`g/U'j>EbER9%*_=%%2bJs&:`]2i[k;e49Ks"FB;3dR*pl%"IRB -coj<HF'b^CcT1t`!3#kr!PVlks+13$s,[21~> -!$2%<#44f!56(ZRroF:Fs8V`^rr3-!lMpnRroF.Drr2ueroF.Err38mjlP\%mJm4SroF:Ks8VT_ -rr3,rn,IL3roF0ZH2[aDc8Y_%#jUO5lh0fJm/$_]"SD9bs60ID!:^!f!9jFD!:g'g!9a@C!:p-h -#Nk.0nF?5Ps8D$`o`+qH*Dc*Ss+13$s,[21~> -!$2%<#44f!56(ZRroF:Fs8V`^rr3-!lMpnRroF.Drr2ueroF.Err38mjlP\%mJm4SroF:Ks8VT_ -rr3,rn,IL3roF0ZH2[aDc8Y_%#jUO5lh0fJm/$_]"SD9bs60ID!:^!f!9jFD!:g'g!9a@C!:p-h -#Nk.0nF?5Ps8D$`o`+qH*Dc*Ss+13$s,[21~> -!$2%<#44f!56(ZRroF:Fs8V`^rr3-!lMpnRroF.Drr2ueroF.Err38mjlP\%mJm4SroF:Ks8VT_ -rr3,rn,IL3roF0ZH2[aDc8Y_%#jUO5lh0fJm/$_]"SD9bs60ID!:^!f!9jFD!:g'g!9a@C!:p-h -#Nk.0nF?5Ps8D$`o`+qH*Dc*Ss+13$s,[21~> -!$/lT!I(UDhuT^&rVloB:kAXts+13$s3Udr~> -!$/lT!I(UDhuT^&rVloB:kAXts+13$s3Udr~> -!$/lT!I(UDhuT^&rVloB:kAXts+13$s3Udr~> -!$/iS!-8'$!-\;?!)nIKJcC<$JcF'rJ,~> -!$/iS!-8'$!-\;?!)nIKJcC<$JcF'rJ,~> -!$/iS!-8'$!-\;?!)nIKJcC<$JcF'rJ,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -"!.FAao7+sNW/qY`R48V"0^kcRDo1.QN,gOJcC<$JcC<$iW"E~> -"!.FAao7+sNW/qY`R48V"0^kcRDo1.QN,gOJcC<$JcC<$iW"E~> -"!.FAao7+sNW/qY`R48V"0^kcRDo1.QN,gOJcC<$JcC<$iW"E~> -"!.EcIf>N8P(N`6b'V_2rrZ3AqBEoY"!.ENAqBu5s+13$s5F!.~> -"!.EcIf>N8P(N`6b'V_2rrZ3AqBEoY"!.ENAqBu5s+13$s5F!.~> -"!.EcIf>N8P(N`6b'V_2rrZ3AqBEoY"!.ENAqBu5s+13$s5F!.~> -"!.E>FoMCApEon>!mbW@o`#"land4#rrX;A[q:3kJcC<$JcF^/J,~> -"!.E>FoMCApEon>!mbW@o`#"land4#rrX;A[q:3kJcC<$JcF^/J,~> -"!.E>FoMCApEon>!mbW@o`#"land4#rrX;A[q:3kJcC<$JcF^/J,~> -"!.F0jSo/[8C[V<!o$B@p&>9Qo`!>>s.-4irrh.os-Btirrgnos,aejrs6qrs,"Pks57Vjrr?j6 -1&u7krrYqM13WZs!+#RZ!.FhG"4R;?iIV#[s+13$s5F!.~> -"!.F0jSo/[8C[V<!o$B@p&>9Qo`!>>s.-4irrh.os-Btirrgnos,aejrs6qrs,"Pks57Vjrr?j6 -1&u7krrYqM13WZs!+#RZ!.FhG"4R;?iIV#[s+13$s5F!.~> -"!.F0jSo/[8C[V<!o$B@p&>9Qo`!>>s.-4irrh.os-Btirrgnos,aejrs6qrs,"Pks57Vjrr?j6 -1&u7krrYqM13WZs!+#RZ!.FhG"4R;?iIV#[s+13$s5F!.~> -!$2%<!r>IAqu6]a0CSoAKpVf="P$'CI@pN='[0DRFK#6>hA08oCp<s>fgPl?E3K9+!NL>Brs'\0 -[oNJ.@D2[*!M"jps+13$s+14(s*t~> -!$2%<!r>IAqu6]a0CSoAKpVf="P$'CI@pN='[0DRFK#6>hA08oCp<s>fgPl?E3K9+!NL>Brs'\0 -[oNJ.@D2[*!M"jps+13$s+14(s*t~> -!$2%<!r>IAqu6]a0CSoAKpVf="P$'CI@pN='[0DRFK#6>hA08oCp<s>fgPl?E3K9+!NL>Brs'\0 -[oNJ.@D2[*!M"jps+13$s+14(s*t~> -!$2(=!pX(@qYpTT4R`:NKpVf="P$'CI>RsX'X(@5FK#5r5/^V(Cp<s>fgPl?A%DU=!OHM>rs'\E -aAr9?;m$&R!K28Ts+13$s+14(s*t~> -!$2(=!pX(@qYpTT4R`:NKpVf="P$'CI>RsX'X(@5FK#5r5/^V(Cp<s>fgPl?A%DU=!OHM>rs'\E -aAr9?;m$&R!K28Ts+13$s+14(s*t~> -!$2(=!pX(@qYpTT4R`:NKpVf="P$'CI>RsX'X(@5FK#5r5/^V(Cp<s>fgPl?A%DU=!OHM>rs'\E -aAr9?;m$&R!K28Ts+13$s+14(s*t~> -"WdXCiHE09rrUPJp@&"^KpVf="P$'CI4>.]'JrYdFK!D[s28(?Cp<s>fgPl?A%DU=%'s[Js34F> -aAr9?;`+G=!1\W?JcC<$JcFI(J,~> -"WdXCiHE09rrUPJp@&"^KpVf="P$'CI4>.]'JrYdFK!D[s28(?Cp<s>fgPl?A%DU=%'s[Js34F> -aAr9?;`+G=!1\W?JcC<$JcFI(J,~> -"WdXCiHE09rrUPJp@&"^KpVf="P$'CI4>.]'JrYdFK!D[s28(?Cp<s>fgPl?A%DU=%'s[Js34F> -aAr9?;`+G=!1\W?JcC<$JcFI(J,~> -"<IO"K"LmZ!lf6Amf*TIQiI*^L.M>mTDnj)bY\=]C2NA<`**+VYlFaV70!9s\c2U;ZYBI>52ZC] -<WE(tb45K5pS#?Qs+13$s+146s*t~> -"<IO"K"LmZ!lf6Amf*TIQiI*^L.M>mTDnj)bY\=]C2NA<`**+VYlFaV70!9s\c2U;ZYBI>52ZC] -<WE(tb45K5pS#?Qs+13$s+146s*t~> -"<IO"K"LmZ!lf6Amf*TIQiI*^L.M>mTDnj)bY\=]C2NA<`**+VYlFaV70!9s\c2U;ZYBI>52ZC] -<WE(tb45K5pS#?Qs+13$s+146s*t~> -"!.E>4oL$;QN.!%J(j;i$,D(EKS*o.s*^O=rtN[Rs)_,ss8U#?s(knmKQ&0Bs0%"QKFEF;s0*Ve -KP)jJs/(DIKE/:MrrVEb;1\aus+13$s60K5~> -"!.E>4oL$;QN.!%J(j;i$,D(EKS*o.s*^O=rtN[Rs)_,ss8U#?s(knmKQ&0Bs0%"QKFEF;s0*Ve -KP)jJs/(DIKE/:MrrVEb;1\aus+13$s60K5~> -"!.E>4oL$;QN.!%J(j;i$,D(EKS*o.s*^O=rtN[Rs)_,ss8U#?s(knmKQ&0Bs0%"QKFEF;s0*Ve -KP)jJs/(DIKE/:MrrVEb;1\aus+13$s60K5~> -!Zh<rr0RVbs8U<pli.7-R$aGp8H6ibrr3,d\GsV)rr3)`]`5qKQiqA:s8TJ*Qisnus''RDQiq#A -s8T8$QiOknrrVn]\Ujd3s+13$s60K5~> -!Zh<rr0RVbs8U<pli.7-R$aGp8H6ibrr3,d\GsV)rr3)`]`5qKQiqA:s8TJ*Qisnus''RDQiq#A -s8T8$QiOknrrVn]\Ujd3s+13$s60K5~> -!Zh<rr0RVbs8U<pli.7-R$aGp8H6ibrr3,d\GsV)rr3)`]`5qKQiqA:s8TJ*Qisnus''RDQiq#A -s8T8$QiOknrrVn]\Ujd3s+13$s60K5~> -!$1"t!NEL+rrLY@li.!t@JKj'cgC`3!Tqsas+13$s+13us*t~> -!$1"t!NEL+rrLY@li.!t@JKj'cgC`3!Tqsas+13$s+13us*t~> -!$1"t!NEL+rrLY@li.!t@JKj'cgC`3!Tqsas+13$s+13us*t~> -!$1"t!U4S&/HJH"li."Wj+75]s+13$s2+ed~> -!$1"t!U4S&/HJH"li."Wj+75]s+13$s2+ed~> -!$1"t!U4S&/HJH"li."Wj+75]s+13$s2+ed~> -!$2(="8MHXoB-&Qp&=C[nU^_$rr`#do(.G4JcDABJ,~> -!$2(="8MHXoB-&Qp&=C[nU^_$rr`#do(.G4JcDABJ,~> -!$2(="8MHXoB-&Qp&=C[nU^_$rr`#do(.G4JcDABJ,~> -!$2(=")T&-1&V%PiV<?N"6A%arr2otkl0-5g\h'Ph>HmE!8@>M!oDM`r;QiRf%p?*!9F(/!9j.V -!WCjOrrUg'j8ArWec=e0rr`5Nf&cQ(!VYROrr`)Jf'<87!<)lI!:]mc!U0(HrrMTRr;Qlif%0?h -rr_fBf(/e>!:^!=!;Q6e!U0(Ors%i@f(]4Eo_d8?g&D!Ol2K9+rrM*Rr;Qu_f%0j#s6]a8!oX+E -r72)2s8RBo.2IL7JcDABJ,~> -!$2(=")T&-1&V%PiV<?N"6A%arr2otkl0-5g\h'Ph>HmE!8@>M!oDM`r;QiRf%p?*!9F(/!9j.V -!WCjOrrUg'j8ArWec=e0rr`5Nf&cQ(!VYROrr`)Jf'<87!<)lI!:]mc!U0(HrrMTRr;Qlif%0?h -rr_fBf(/e>!:^!=!;Q6e!U0(Ors%i@f(]4Eo_d8?g&D!Ol2K9+rrM*Rr;Qu_f%0j#s6]a8!oX+E -r72)2s8RBo.2IL7JcDABJ,~> -!$2(=")T&-1&V%PiV<?N"6A%arr2otkl0-5g\h'Ph>HmE!8@>M!oDM`r;QiRf%p?*!9F(/!9j.V -!WCjOrrUg'j8ArWec=e0rr`5Nf&cQ(!VYROrr`)Jf'<87!<)lI!:]mc!U0(HrrMTRr;Qlif%0?h -rr_fBf(/e>!:^!=!;Q6e!U0(Ors%i@f(]4Eo_d8?g&D!Ol2K9+rrM*Rr;Qu_f%0j#s6]a8!oX+E -r72)2s8RBo.2IL7JcDABJ,~> -!$2(=!H#(<rrU\+ec#LRg2BGM^An5kH2^86<m(FA!mMnWo`"s6(&\(7c<Nh@bPqMIbZRD>cN!oK -2>ouERJ-X]ZRbtR"N*i$CY/Rc"MdJrDV>$h"M.&lEnpBg!r.jWrVm0%WCB@+j8].frC-gJmJ[%d -i)?WKrrVP,JGoK[ns2a7ORE/Kmug.0Pk"eRm>h08!9!SN!oR"WrVm,bK18>*qZ"e;7KaM,s5A>( -7KEG]rrU_-eG]CPg2KMN^&S,8rC-lP56%S[7KGS@r;Qa;JcC<$TDsE~> -!$2(=!H#(<rrU\+ec#LRg2BGM^An5kH2^86<m(FA!mMnWo`"s6(&\(7c<Nh@bPqMIbZRD>cN!oK -2>ouERJ-X]ZRbtR"N*i$CY/Rc"MdJrDV>$h"M.&lEnpBg!r.jWrVm0%WCB@+j8].frC-gJmJ[%d -i)?WKrrVP,JGoK[ns2a7ORE/Kmug.0Pk"eRm>h08!9!SN!oR"WrVm,bK18>*qZ"e;7KaM,s5A>( -7KEG]rrU_-eG]CPg2KMN^&S,8rC-lP56%S[7KGS@r;Qa;JcC<$TDsE~> -!$2(=!H#(<rrU\+ec#LRg2BGM^An5kH2^86<m(FA!mMnWo`"s6(&\(7c<Nh@bPqMIbZRD>cN!oK -2>ouERJ-X]ZRbtR"N*i$CY/Rc"MdJrDV>$h"M.&lEnpBg!r.jWrVm0%WCB@+j8].frC-gJmJ[%d -i)?WKrrVP,JGoK[ns2a7ORE/Kmug.0Pk"eRm>h08!9!SN!oR"WrVm,bK18>*qZ"e;7KaM,s5A>( -7KEG]rrU_-eG]CPg2KMN^&S,8rC-lP56%S[7KGS@r;Qa;JcC<$TDsE~> -!$2(=!H#(=rr^Pl*7b&g$.aRGs8Ql>s,N-=rs.@Es8U#Z'(>Mn!krU^rVmJLFlEAaEl.jnF6<M^ -FN+8a_=IU-X^NaYrt`"Xqu=rhdf6Ufs8U/hec2gjs8Tudg%YLJQVCKYrs5BFs8TH[ir:%trrHH? -rr3)XK.X(srr__K4b3P3')7TQs8SOJoCS*2s8S@Gp@4-5oD\pMK.O&%rs%YBXT/<NSGW<iX)\0u -KX^RQ"4)95eGfIPf7(a>s(nt<rrW1aM>[ATqNHs;rr='js+13Bs*t~> -!$2(=!H#(=rr^Pl*7b&g$.aRGs8Ql>s,N-=rs.@Es8U#Z'(>Mn!krU^rVmJLFlEAaEl.jnF6<M^ -FN+8a_=IU-X^NaYrt`"Xqu=rhdf6Ufs8U/hec2gjs8Tudg%YLJQVCKYrs5BFs8TH[ir:%trrHH? -rr3)XK.X(srr__K4b3P3')7TQs8SOJoCS*2s8S@Gp@4-5oD\pMK.O&%rs%YBXT/<NSGW<iX)\0u -KX^RQ"4)95eGfIPf7(a>s(nt<rrW1aM>[ATqNHs;rr='js+13Bs*t~> -!$2(=!H#(=rr^Pl*7b&g$.aRGs8Ql>s,N-=rs.@Es8U#Z'(>Mn!krU^rVmJLFlEAaEl.jnF6<M^ -FN+8a_=IU-X^NaYrt`"Xqu=rhdf6Ufs8U/hec2gjs8Tudg%YLJQVCKYrs5BFs8TH[ir:%trrHH? -rr3)XK.X(srr__K4b3P3')7TQs8SOJoCS*2s8S@Gp@4-5oD\pMK.O&%rs%YBXT/<NSGW<iX)\0u -KX^RQ"4)95eGfIPf7(a>s(nt<rrW1aM>[ATqNHs;rr='js+13Bs*t~> -!$2(=!H#(>rrgYtbt$=`rrJF?rr3,`2uenurr35Z5QATNg+Dderr]f^iZ8!t!Go">rt)eNs0@.> -s6"m>s'*=#It+Hlrr]'Il7;i5!CGQ?rsY$Js.+]?s3cD>s"Wm>rrIY?q#:GWNQ5&trr=)<rrZoB -ruM(<#[Y7Fs5ea>F/es7"T!VCSUg_>!UMN=rsLsInURY>s/:J?gHkH2"QY'BEiT-=!Q&(>rr=): -rra_Bs06D3rrgYtbt-@`rrJI?rr3#^2Z3RUq]Yk:!V7c;rr='js+13Bs*t~> -!$2(=!H#(>rrgYtbt$=`rrJF?rr3,`2uenurr35Z5QATNg+Dderr]f^iZ8!t!Go">rt)eNs0@.> -s6"m>s'*=#It+Hlrr]'Il7;i5!CGQ?rsY$Js.+]?s3cD>s"Wm>rrIY?q#:GWNQ5&trr=)<rrZoB -ruM(<#[Y7Fs5ea>F/es7"T!VCSUg_>!UMN=rsLsInURY>s/:J?gHkH2"QY'BEiT-=!Q&(>rr=): -rra_Bs06D3rrgYtbt-@`rrJI?rr3#^2Z3RUq]Yk:!V7c;rr='js+13Bs*t~> -!$2(=!H#(>rrgYtbt$=`rrJF?rr3,`2uenurr35Z5QATNg+Dderr]f^iZ8!t!Go">rt)eNs0@.> -s6"m>s'*=#It+Hlrr]'Il7;i5!CGQ?rsY$Js.+]?s3cD>s"Wm>rrIY?q#:GWNQ5&trr=)<rrZoB -ruM(<#[Y7Fs5ea>F/es7"T!VCSUg_>!UMN=rsLsInURY>s/:J?gHkH2"QY'BEiT-=!Q&(>rr=): -rra_Bs06D3rrgYtbt-@`rrJI?rr3#^2Z3RUq]Yk:!V7c;rr='js+13Bs*t~> -!$2(=!H#(>rrh'#s$tW=rrJF?rr3Mk2uenus8W!s5QB*^s"3^5rr^F#ruM(<!Go">rt2kOs8TTE -@Vr^Vs'&*]SXkV9q#:H,mdVh+rrGI@rr3ChMuWgT@UaQ)s8Oh?rr3"LSG<*dWW'q<rVlj<rVlt4 -[f7BIrs>hR^Am_@]`3&Pq#:KoXT+iArr3#c0`D"[4Q-8(M.0qOli5^*`Vs<HjS8`Xlb3==WrE#! -_H[!=!$2";#)nfDs06C@raGm,q#:KNc2U>arr3"cL&V)Qk;N8<!HY:<rrI&?qu6X:JcC<$TDsE~> -!$2(=!H#(>rrh'#s$tW=rrJF?rr3Mk2uenus8W!s5QB*^s"3^5rr^F#ruM(<!Go">rt2kOs8TTE -@Vr^Vs'&*]SXkV9q#:H,mdVh+rrGI@rr3ChMuWgT@UaQ)s8Oh?rr3"LSG<*dWW'q<rVlj<rVlt4 -[f7BIrs>hR^Am_@]`3&Pq#:KoXT+iArr3#c0`D"[4Q-8(M.0qOli5^*`Vs<HjS8`Xlb3==WrE#! -_H[!=!$2";#)nfDs06C@raGm,q#:KNc2U>arr3"cL&V)Qk;N8<!HY:<rrI&?qu6X:JcC<$TDsE~> -!$2(=!H#(>rrh'#s$tW=rrJF?rr3Mk2uenus8W!s5QB*^s"3^5rr^F#ruM(<!Go">rt2kOs8TTE -@Vr^Vs'&*]SXkV9q#:H,mdVh+rrGI@rr3ChMuWgT@UaQ)s8Oh?rr3"LSG<*dWW'q<rVlj<rVlt4 -[f7BIrs>hR^Am_@]`3&Pq#:KoXT+iArr3#c0`D"[4Q-8(M.0qOli5^*`Vs<HjS8`Xlb3==WrE#! -_H[!=!$2";#)nfDs06C@raGm,q#:KNc2U>arr3"cL&V)Qk;N8<!HY:<rrI&?qu6X:JcC<$TDsE~> -!$2(=!H#(;rrGX?rr3"bLAq2Zju<=uGst.lTg/MU!A3d3rr=)<rrHr?rr3SB@K6@PVn/[Ps8UQZ -pAb/mEq]M6nLOS<!CGQ?rsY$Js8Q96\riK^s"Wm>rrIY?pAY06<W2pt*W?!?B=@iLr^@-E7DAe$ -aT)9]WV-8lSUg_>!UMN=rsV$Js42%u\n]pugCeK)\cGt,pAY/6WrE#!_H[!=!$2";#"7,Ds03St -rji)5p&>%`eGfIKQ\GG=!TuB=rr=)9rr=)9rr='js+13Bs*t~> -!$2(=!H#(;rrGX?rr3"bLAq2Zju<=uGst.lTg/MU!A3d3rr=)<rrHr?rr3SB@K6@PVn/[Ps8UQZ -pAb/mEq]M6nLOS<!CGQ?rsY$Js8Q96\riK^s"Wm>rrIY?pAY06<W2pt*W?!?B=@iLr^@-E7DAe$ -aT)9]WV-8lSUg_>!UMN=rsV$Js42%u\n]pugCeK)\cGt,pAY/6WrE#!_H[!=!$2";#"7,Ds03St -rji)5p&>%`eGfIKQ\GG=!TuB=rr=)9rr=)9rr='js+13Bs*t~> -!$2(=!H#(;rrGX?rr3"bLAq2Zju<=uGst.lTg/MU!A3d3rr=)<rrHr?rr3SB@K6@PVn/[Ps8UQZ -pAb/mEq]M6nLOS<!CGQ?rsY$Js8Q96\riK^s"Wm>rrIY?pAY06<W2pt*W?!?B=@iLr^@-E7DAe$ -aT)9]WV-8lSUg_>!UMN=rsV$Js42%u\n]pugCeK)\cGt,pAY/6WrE#!_H[!=!$2";#"7,Ds03St -rji)5p&>%`eGfIKQ\GG=!TuB=rr=)9rr=)9rr='js+13Bs*t~> -!$2(=!H#(;rrGX?rr3"bLAq2Zju<>?l.=P>h*:l<!A3d3rr=)<rrHr?rr3;:@K1_Qq>^5bJc#HJ -WcIh5!V%]=rrGI@rr3ChMuQnts8VVDV>i::rr3"LSG)s`a&W*<!$2%<")#(BrVkCOls@F+$N;FT -&f]0jrrJ[@rr3#c0`D"[4Q,F!jT#8@9^LLmk5PA]/+N?5!HP4>rrKu@rVlj<rVm+KW;6JnZ!6So -rr=)4rrG[?rr3"cL&V)Qk;N;=!E[;<rrGp?qYpO9JcC<$TDsE~> -!$2(=!H#(;rrGX?rr3"bLAq2Zju<>?l.=P>h*:l<!A3d3rr=)<rrHr?rr3;:@K1_Qq>^5bJc#HJ -WcIh5!V%]=rrGI@rr3ChMuQnts8VVDV>i::rr3"LSG)s`a&W*<!$2%<")#(BrVkCOls@F+$N;FT -&f]0jrrJ[@rr3#c0`D"[4Q,F!jT#8@9^LLmk5PA]/+N?5!HP4>rrKu@rVlj<rVm+KW;6JnZ!6So -rr=)4rrG[?rr3"cL&V)Qk;N;=!E[;<rrGp?qYpO9JcC<$TDsE~> -!$2(=!H#(;rrGX?rr3"bLAq2Zju<>?l.=P>h*:l<!A3d3rr=)<rrHr?rr3;:@K1_Qq>^5bJc#HJ -WcIh5!V%]=rrGI@rr3ChMuQnts8VVDV>i::rr3"LSG)s`a&W*<!$2%<")#(BrVkCOls@F+$N;FT -&f]0jrrJ[@rr3#c0`D"[4Q,F!jT#8@9^LLmk5PA]/+N?5!HP4>rrKu@rVlj<rVm+KW;6JnZ!6So -rr=)4rrG[?rr3"cL&V)Qk;N;=!E[;<rrGp?qYpO9JcC<$TDsE~> -!$2(=!H#(;rrGX?rr31nL&(cKfL5W:!ROO=rrF_?qu6]dn,*+b*W?!MG,kK?r3L5>DR'->r2b#? -o_\Xf!NL2=rrMNXr;Qff/,fJ`:X9"?q2^a>8(IY>q1kI>5McA>pjf.=rrW,cp&+gja&W*<"t]9D -s7^9?r;QlmA&J?FoE&p)r;Q]tq<dtTrrJ[@rr3St47iLPoL@j%6hC?Xo02Ho62gfa/+NW=!qu$Y -r;Qe<WrE#&`aJN?s7%Z>rrVn+_#F?7Z#'C=!$2%<!qYgXr;QdgeGfIPSqQq>s4Kd=rrVdXkPY>] -o/c@:rr='js+13Bs*t~> -!$2(=!H#(;rrGX?rr31nL&(cKfL5W:!ROO=rrF_?qu6]dn,*+b*W?!MG,kK?r3L5>DR'->r2b#? -o_\Xf!NL2=rrMNXr;Qff/,fJ`:X9"?q2^a>8(IY>q1kI>5McA>pjf.=rrW,cp&+gja&W*<"t]9D -s7^9?r;QlmA&J?FoE&p)r;Q]tq<dtTrrJ[@rr3St47iLPoL@j%6hC?Xo02Ho62gfa/+NW=!qu$Y -r;Qe<WrE#&`aJN?s7%Z>rrVn+_#F?7Z#'C=!$2%<!qYgXr;QdgeGfIPSqQq>s4Kd=rrVdXkPY>] -o/c@:rr='js+13Bs*t~> -!$2(=!H#(;rrGX?rr31nL&(cKfL5W:!ROO=rrF_?qu6]dn,*+b*W?!MG,kK?r3L5>DR'->r2b#? -o_\Xf!NL2=rrMNXr;Qff/,fJ`:X9"?q2^a>8(IY>q1kI>5McA>pjf.=rrW,cp&+gja&W*<"t]9D -s7^9?r;QlmA&J?FoE&p)r;Q]tq<dtTrrJ[@rr3St47iLPoL@j%6hC?Xo02Ho62gfa/+NW=!qu$Y -r;Qe<WrE#&`aJN?s7%Z>rrVn+_#F?7Z#'C=!$2%<!qYgXr;QdgeGfIPSqQq>s4Kd=rrVdXkPY>] -o/c@:rr='js+13Bs*t~> -!$2(=!H#(>rseACd5nGXg&M)7QI4)Lrr3GhdF$63s8V?2d3Z]XhYmHT,o6L[%Hc'c(t$ais8R#B -dC<ffrs3;IdBd]is*:UCd/f_qrr3&r!'pP`%GoLO,LOp&s8Pd:d@H*mrrbg=d?p!orrbd<d?9go -rrULHCB"5Ef$U6^dF%I`qF/fZJueqOr;P(EIBrbM!HY7=ruS!:PlLd^dF!JXdF%ahf3m"RB?pPW -d:L_Q@b(M<=T-VJ9(W&^'70_os8Vc>d9V[XdJs6SHdU58nG`FgJ"HW=!P`^Sd/ZZ7rt8-.l2UeO -dEt%YdFnR"P*XMC7fE>f<64t:!DLl9rr='js+13Bs*t~> -!$2(=!H#(>rseACd5nGXg&M)7QI4)Lrr3GhdF$63s8V?2d3Z]XhYmHT,o6L[%Hc'c(t$ais8R#B -dC<ffrs3;IdBd]is*:UCd/f_qrr3&r!'pP`%GoLO,LOp&s8Pd:d@H*mrrbg=d?p!orrbd<d?9go -rrULHCB"5Ef$U6^dF%I`qF/fZJueqOr;P(EIBrbM!HY7=ruS!:PlLd^dF!JXdF%ahf3m"RB?pPW -d:L_Q@b(M<=T-VJ9(W&^'70_os8Vc>d9V[XdJs6SHdU58nG`FgJ"HW=!P`^Sd/ZZ7rt8-.l2UeO -dEt%YdFnR"P*XMC7fE>f<64t:!DLl9rr='js+13Bs*t~> -!$2(=!H#(>rseACd5nGXg&M)7QI4)Lrr3GhdF$63s8V?2d3Z]XhYmHT,o6L[%Hc'c(t$ais8R#B -dC<ffrs3;IdBd]is*:UCd/f_qrr3&r!'pP`%GoLO,LOp&s8Pd:d@H*mrrbg=d?p!orrbd<d?9go -rrULHCB"5Ef$U6^dF%I`qF/fZJueqOr;P(EIBrbM!HY7=ruS!:PlLd^dF!JXdF%ahf3m"RB?pPW -d:L_Q@b(M<=T-VJ9(W&^'70_os8Vc>d9V[XdJs6SHdU58nG`FgJ"HW=!P`^Sd/ZZ7rt8-.l2UeO -dEt%YdFnR"P*XMC7fE>f<64t:!DLl9rr='js+13Bs*t~> -!$2(=#&Sap[f?B>r(@$+rr3(Z92#3^rr\``92GQe!3,lh!.OnH!G*,IrrAVf9)skKrrR(2B)MZ1 -BM31?rr@9B9)sbGrrW'`5lUc`JG`%?W;ceu:eQK?rr`6n98`]J"8GM^OSo+[h)/sHrr?O+9*WB8 -s5lsIUAk/mp&7SaW;?MpSAY=*"i)ONs8Vlk9*XGVs2[i+_#=98_FmPArr2uGrC[.KrVm%q*5DOR -gAFSD:&b.pX%Q+BrVlnp]);R.Zi*Msrr)j#KFd>Is1J8&!,MT6"-#rHr;HWrLY2M(!J0,*rr^WT -Zl=SrJcDABJ,~> -!$2(=#&Sap[f?B>r(@$+rr3(Z92#3^rr\``92GQe!3,lh!.OnH!G*,IrrAVf9)skKrrR(2B)MZ1 -BM31?rr@9B9)sbGrrW'`5lUc`JG`%?W;ceu:eQK?rr`6n98`]J"8GM^OSo+[h)/sHrr?O+9*WB8 -s5lsIUAk/mp&7SaW;?MpSAY=*"i)ONs8Vlk9*XGVs2[i+_#=98_FmPArr2uGrC[.KrVm%q*5DOR -gAFSD:&b.pX%Q+BrVlnp]);R.Zi*Msrr)j#KFd>Is1J8&!,MT6"-#rHr;HWrLY2M(!J0,*rr^WT -Zl=SrJcDABJ,~> -!$2(=#&Sap[f?B>r(@$+rr3(Z92#3^rr\``92GQe!3,lh!.OnH!G*,IrrAVf9)skKrrR(2B)MZ1 -BM31?rr@9B9)sbGrrW'`5lUc`JG`%?W;ceu:eQK?rr`6n98`]J"8GM^OSo+[h)/sHrr?O+9*WB8 -s5lsIUAk/mp&7SaW;?MpSAY=*"i)ONs8Vlk9*XGVs2[i+_#=98_FmPArr2uGrC[.KrVm%q*5DOR -gAFSD:&b.pX%Q+BrVlnp]);R.Zi*Msrr)j#KFd>Is1J8&!,MT6"-#rHr;HWrLY2M(!J0,*rr^WT -Zl=SrJcDABJ,~> -!$2(="/omHD;5'j*S^Spgd'Wo!O?IprrIq?g]%:[^YAbh1[3cr"/0CAFFjICs.B=A~> -!$2(="/omHD;5'j*S^Spgd'Wo!O?IprrIq?g]%:[^YAbh1[3cr"/0CAFFjICs.B=A~> -!$2(="/omHD;5'j*S^Spgd'Wo!O?IprrIq?g]%:[^YAbh1[3cr"/0CAFFjICs.B=A~> -!$0Vi!KE/qrrW+FUtktOk'abprrUPsbhW4"\q/eprrT!Kon!-os+14Fs*t~> -!$0Vi!KE/qrrW+FUtktOk'abprrUPsbhW4"\q/eprrT!Kon!-os+14Fs*t~> -!$0Vi!KE/qrrW+FUtktOk'abprrUPsbhW4"\q/eprrT!Kon!-os+14Fs*t~> -!$0Vi!O2LWrrN)%g]%<B^>&YghTjmU!RKTVrrKn$JcC<$JcGKEJ,~> -!$0Vi!O2LWrrN)%g]%<B^>&YghTjmU!RKTVrrKn$JcC<$JcGKEJ,~> -!$0Vi!O2LWrrN)%g]%<B^>&YghTjmU!RKTVrrKn$JcC<$JcGKEJ,~> -!$.X1!SDh6rrC7A5QK%#rrJ>#p&>&uU\t,l_>Z\"ec#LKV6GONJcC<$hZ&*~> -!$.X1!SDh6rrC7A5QK%#rrJ>#p&>&uU\t,l_>Z\"ec#LKV6GONJcC<$hZ&*~> -!$.X1!SDh6rrC7A5QK%#rrJ>#p&>&uU\t,l_>Z\"ec#LKV6GONJcC<$hZ&*~> -!Zh<Ir$)>4s8S:^o)Ac,,O4Up!^%].ec,^;8E]sO"TCUbZAJS\"Z/[Js,sDOrs&<E>Q=_N_"[j3 -dV8`9[J'V'\lIP@p@\FeS;d+>rrC+<,66HsrrV_<Y5SA'o\A4na8Q#>mVojjs+13$s53j,~> -!Zh<Ir$)>4s8S:^o)Ac,,O4Up!^%].ec,^;8E]sO"TCUbZAJS\"Z/[Js,sDOrs&<E>Q=_N_"[j3 -dV8`9[J'V'\lIP@p@\FeS;d+>rrC+<,66HsrrV_<Y5SA'o\A4na8Q#>mVojjs+13$s53j,~> -!Zh<Ir$)>4s8S:^o)Ac,,O4Up!^%].ec,^;8E]sO"TCUbZAJS\"Z/[Js,sDOrs&<E>Q=_N_"[j3 -dV8`9[J'V'\lIP@p@\FeS;d+>rrC+<,66HsrrV_<Y5SA'o\A4na8Q#>mVojjs+13$s53j,~> -!Zh=>rV6]S3WK-WF0PQ6rrW)nrp9Xf5Ml4O?/GS],PqE@f1,]<&cNmPVK`(?2WiUik^]N0s6V]9 -rrs_D_.`%J]_DF/b>J:\WV6>m\mk:2!r%S@rr3#S7/co^0_,/B!f!3?qu6[sa8Gr=GH(Ijs+13$ -s5<p-~> -!Zh=>rV6]S3WK-WF0PQ6rrW)nrp9Xf5Ml4O?/GS],PqE@f1,]<&cNmPVK`(?2WiUik^]N0s6V]9 -rrs_D_.`%J]_DF/b>J:\WV6>m\mk:2!r%S@rr3#S7/co^0_,/B!f!3?qu6[sa8Gr=GH(Ijs+13$ -s5<p-~> -!Zh=>rV6]S3WK-WF0PQ6rrW)nrp9Xf5Ml4O?/GS],PqE@f1,]<&cNmPVK`(?2WiUik^]N0s6V]9 -rrs_D_.`%J]_DF/b>J:\WV6>m\mk:2!r%S@rr3#S7/co^0_,/B!f!3?qu6[sa8Gr=GH(Ijs+13$ -s5<p-~> -!$2";!S0d=rrR#CrV$6nilfOArVllVrlY;kr;QfHd/<nK3o^1dHN4$"rlYl+s8V$Is3AgBs8Us! -bPB?;rr2uLrlYT(s8Ufr(X("[rVlj<qu7B09`MVcs8Q9ls2[$bs8UT^-HjTqrr2uBrlY`7s8Pal -s0OVbs8UL?rlY<.rr3,OFoRN7pAY0)@f?<-qpkZF"k`YNs/C)<rrM+mrr3&R:%\Da!F<J;rrH-? -qu6[kd"24Js+14.s*t~> -!$2";!S0d=rrR#CrV$6nilfOArVllVrlY;kr;QfHd/<nK3o^1dHN4$"rlYl+s8V$Is3AgBs8Us! -bPB?;rr2uLrlYT(s8Ufr(X("[rVlj<qu7B09`MVcs8Q9ls2[$bs8UT^-HjTqrr2uBrlY`7s8Pal -s0OVbs8UL?rlY<.rr3,OFoRN7pAY0)@f?<-qpkZF"k`YNs/C)<rrM+mrr3&R:%\Da!F<J;rrH-? -qu6[kd"24Js+14.s*t~> -!$2";!S0d=rrR#CrV$6nilfOArVllVrlY;kr;QfHd/<nK3o^1dHN4$"rlYl+s8V$Is3AgBs8Us! -bPB?;rr2uLrlYT(s8Ufr(X("[rVlj<qu7B09`MVcs8Q9ls2[$bs8UT^-HjTqrr2uBrlY`7s8Pal -s0OVbs8UL?rlY<.rr3,OFoRN7pAY0)@f?<-qpkZF"k`YNs/C)<rrM+mrr3&R:%\Da!F<J;rrH-? -qu6[kd"24Js+14.s*t~> -!$2%<!luR\qu6\6YP.tuSPTp\rr3#PGQ(D>@)`*E!@8NZrrG1?rVloGD>m?GC!H[BQ*41aD:&;e -4]%qNE7=jeFoG2CF4L;C:^KrpQMpg`*UNe4CeG:8:p'ct\P`?&!J]_2rrF"/r_NWSlgOiS\mkX< -!UMN=rrkOCs8T'>q>UNBCZ53k!F<J;rrH-?qu6[kd"24Js+14.s*t~> -!$2%<!luR\qu6\6YP.tuSPTp\rr3#PGQ(D>@)`*E!@8NZrrG1?rVloGD>m?GC!H[BQ*41aD:&;e -4]%qNE7=jeFoG2CF4L;C:^KrpQMpg`*UNe4CeG:8:p'ct\P`?&!J]_2rrF"/r_NWSlgOiS\mkX< -!UMN=rrkOCs8T'>q>UNBCZ53k!F<J;rrH-?qu6[kd"24Js+14.s*t~> -!$2%<!luR\qu6\6YP.tuSPTp\rr3#PGQ(D>@)`*E!@8NZrrG1?rVloGD>m?GC!H[BQ*41aD:&;e -4]%qNE7=jeFoG2CF4L;C:^KrpQMpg`*UNe4CeG:8:p'ct\P`?&!J]_2rrF"/r_NWSlgOiS\mkX< -!UMN=rrkOCs8T'>q>UNBCZ53k!F<J;rrH-?qu6[kd"24Js+14.s*t~> -!$2%<".O.FUAOrj*VB@42Wk#=!JQm>rrLY@rVlnMHN*pI`0)9pTDnikCp<p=&\DKOs$g8gs0HG> ->J^=[s/L,:rrJj?rr2s=qYpO9li."Q0`:qO+T;<>!Nj`DrrEjhrr3"*^@2(&\mkX<$hSPIs8TTP -mJm3cF8Gn<b'_b:rrHE?qu6[sa8>l;:!eIkJcC<$i;\<~> -!$2%<".O.FUAOrj*VB@42Wk#=!JQm>rrLY@rVlnMHN*pI`0)9pTDnikCp<p=&\DKOs$g8gs0HG> ->J^=[s/L,:rrJj?rr2s=qYpO9li."Q0`:qO+T;<>!Nj`DrrEjhrr3"*^@2(&\mkX<$hSPIs8TTP -mJm3cF8Gn<b'_b:rrHE?qu6[sa8>l;:!eIkJcC<$i;\<~> -!$2%<".O.FUAOrj*VB@42Wk#=!JQm>rrLY@rVlnMHN*pI`0)9pTDnikCp<p=&\DKOs$g8gs0HG> ->J^=[s/L,:rrJj?rr2s=qYpO9li."Q0`:qO+T;<>!Nj`DrrEjhrr3"*^@2(&\mkX<$hSPIs8TTP -mJm3cF8Gn<b'_b:rrHE?qu6[sa8>l;:!eIkJcC<$i;\<~> -!$1t:!V[r=rrJ%?o`"qMk5PA]KpVf=!S'a9rrg)o++'CSrrHr?rr3\E@K6?sf)Pd*])Q!NruM-> -ErZ0&rcJ66HN*pE*W#d9*UE_+m4eJ<!1Ee.!5nR2!$2%<!FEM/rrK]?r;QrH:B1>t_uBZ:WH7t: -!mH/@qYpS%^\e$3<QG":!DUpls+13$s5<p-~> -!$1t:!V[r=rrJ%?o`"qMk5PA]KpVf=!S'a9rrg)o++'CSrrHr?rr3\E@K6?sf)Pd*])Q!NruM-> -ErZ0&rcJ66HN*pE*W#d9*UE_+m4eJ<!1Ee.!5nR2!$2%<!FEM/rrK]?r;QrH:B1>t_uBZ:WH7t: -!mH/@qYpS%^\e$3<QG":!DUpls+13$s5<p-~> -!$1t:!V[r=rrJ%?o`"qMk5PA]KpVf=!S'a9rrg)o++'CSrrHr?rr3\E@K6?sf)Pd*])Q!NruM-> -ErZ0&rcJ66HN*pE*W#d9*UE_+m4eJ<!1Ee.!5nR2!$2%<!FEM/rrK]?r;QrH:B1>t_uBZ:WH7t: -!mH/@qYpS%^\e$3<QG":!DUpls+13$s5<p-~> -!$1t:!V[r>rrRqDqt0mh2Wk#=!JQm>[email protected]/lXCp<p="hS4Cs$kT;rs;oGruM->ErV/d -rhfd3HN*pE*W#d9*UE_+m4eG;!3#mp!IDl_rr=)<rrHH?n,EF"@f660mZ3s:MsC<A!NC/<rrU\m -deWnD?,-::!EI2;rrGj@JcC<$JcF[.J,~> -!$1t:!V[r>rrRqDqt0mh2Wk#=!JQm>[email protected]/lXCp<p="hS4Cs$kT;rs;oGruM->ErV/d -rhfd3HN*pE*W#d9*UE_+m4eG;!3#mp!IDl_rr=)<rrHH?n,EF"@f660mZ3s:MsC<A!NC/<rrU\m -deWnD?,-::!EI2;rrGj@JcC<$JcF[.J,~> -!$1t:!V[r>rrRqDqt0mh2Wk#=!JQm>[email protected]/lXCp<p="hS4Cs$kT;rs;oGruM->ErV/d -rhfd3HN*pE*W#d9*UE_+m4eG;!3#mp!IDl_rr=)<rrHH?n,EF"@f660mZ3s:MsC<A!NC/<rrU\m -deWnD?,-::!EI2;rrGj@JcC<$JcF[.J,~> -"!.ESQ2^dapEon>!f3H@oD\hLk5PA]KpVf=!S'a>rrH1pr;QdYir/lXCp<p="hS4Cs$kT;rsi8L -ruM->ErT(\s8U@jHN*pI*WQ/ET`4rl\mt+,#jj>Gs-L=s;uQ^q!H5+8rr=)<rrHH?pAY3*40AJ] -!P2b;rr]NAGGY9<!pbT@rr3&[?J>5T!h#5@qu6[sa8Gr=N19J;rrRp:iIV#[s+146s*t~> -"!.ESQ2^dapEon>!f3H@oD\hLk5PA]KpVf=!S'a>rrH1pr;QdYir/lXCp<p="hS4Cs$kT;rsi8L -ruM->ErT(\s8U@jHN*pI*WQ/ET`4rl\mt+,#jj>Gs-L=s;uQ^q!H5+8rr=)<rrHH?pAY3*40AJ] -!P2b;rr]NAGGY9<!pbT@rr3&[?J>5T!h#5@qu6[sa8Gr=N19J;rrRp:iIV#[s+146s*t~> -"!.ESQ2^dapEon>!f3H@oD\hLk5PA]KpVf=!S'a>rrH1pr;QdYir/lXCp<p="hS4Cs$kT;rsi8L -ruM->ErT(\s8U@jHN*pI*WQ/ET`4rl\mt+,#jj>Gs-L=s;uQ^q!H5+8rr=)<rrHH?pAY3*40AJ] -!P2b;rr]NAGGY9<!pbT@rr3&[?J>5T!h#5@qu6[sa8Gr=N19J;rrRp:iIV#[s+146s*t~> -"!.F7L]/>!-0>1,OHfM3rs4/s"rf])s7("+*W\IhrVlmE(B"153o^,<!Ua.j*X,I.s8PR>r;R72 -_>iTrWcJ.*7ii^IO*^g*"P=b:D".H(!U2E,rs8S9*Zg(.s5bC&*W_5mq#:=7rVln)^@qR.V#ZbY -rrK]?qu6clO,!<)rrJ(@rr3#R#lG_DoD\ajH_UB:!EI2<rrHl?qu6_+!8)l&JcC<$kl6/~> -"!.F7L]/>!-0>1,OHfM3rs4/s"rf])s7("+*W\IhrVlmE(B"153o^,<!Ua.j*X,I.s8PR>r;R72 -_>iTrWcJ.*7ii^IO*^g*"P=b:D".H(!U2E,rs8S9*Zg(.s5bC&*W_5mq#:=7rVln)^@qR.V#ZbY -rrK]?qu6clO,!<)rrJ(@rr3#R#lG_DoD\ajH_UB:!EI2<rrHl?qu6_+!8)l&JcC<$kl6/~> -"!.F7L]/>!-0>1,OHfM3rs4/s"rf])s7("+*W\IhrVlmE(B"153o^,<!Ua.j*X,I.s8PR>r;R72 -_>iTrWcJ.*7ii^IO*^g*"P=b:D".H(!U2E,rs8S9*Zg(.s5bC&*W_5mq#:=7rVln)^@qR.V#ZbY -rrK]?qu6clO,!<)rrJ(@rr3#R#lG_DoD\ajH_UB:!EI2<rrHl?qu6_+!8)l&JcC<$kl6/~> -!Zh=@r;Q]q!<2Qhr;Q]qr;QWos8Mrr!<2lqr;Q]q!<2lq!<2or!ri6"rVcitrr)lrrqucsct2PC -rr2osr;QHj!<2rs!<2Wj!Jcp<rrg.X*?E#qs8W)qrrW1K\,QF)rr2ouUltZU"L]<k*6nH^!L9>S -s8W(Ls+13$s6'E4~> -!Zh=@r;Q]q!<2Qhr;Q]qr;QWos8Mrr!<2lqr;Q]q!<2lq!<2or!ri6"rVcitrr)lrrqucsct2PC -rr2osr;QHj!<2rs!<2Wj!Jcp<rrg.X*?E#qs8W)qrrW1K\,QF)rr2ouUltZU"L]<k*6nH^!L9>S -s8W(Ls+13$s6'E4~> -!Zh=@r;Q]q!<2Qhr;Q]qr;QWos8Mrr!<2lqr;Q]q!<2lq!<2or!ri6"rVcitrr)lrrqucsct2PC -rr2osr;QHj!<2rs!<2Wj!Jcp<rrg.X*?E#qs8W)qrrW1K\,QF)rr2ouUltZU"L]<k*6nH^!L9>S -s8W(Ls+13$s6'E4~> -!$-XjhZ!ZCKWKb)JcC<$W;hA~> -!$-XjhZ!ZCKWKb)JcC<$W;hA~> -!$-XjhZ!ZCKWKb)JcC<$W;hA~> -!$-XjhZ!VoV1JYts+13Js*t~> -!$-XjhZ!VoV1JYts+13Js*t~> -!$-XjhZ!VoV1JYts+13Js*t~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -"!.EGKDtlRpH/DRMZ3VYn3d5LP5bIij\#Q@RfEEb3<0$WGlA]a6N$i_#?;`a8,rVfC&T+R;>l(G -JcC<$JcE=]J,~> -"!.EGKDtlRpH/DRMZ3VYn3d5LP5bIij\#Q@RfEEb3<0$WGlA]a6N$i_#?;`a8,rVfC&T+R;>l(G -JcC<$JcE=]J,~> -"!.EGKDtlRpH/DRMZ3VYn3d5LP5bIij\#Q@RfEEb3<0$WGlA]a6N$i_#?;`a8,rVfC&T+R;>l(G -JcC<$JcE=]J,~> -"!.E>FoMCDpEop4IfB?]mOnO*LB%;6+?0=\NrT.U,6.[sQi@!ceO]]>#=@//1]RJsW;cet`*%Zk -JcC<$JcE=]J,~> -"!.E>FoMCDpEop4IfB?]mOnO*LB%;6+?0=\NrT.U,6.[sQi@!ceO]]>#=@//1]RJsW;cet`*%Zk -JcC<$JcE=]J,~> -"!.E>FoMCDpEop4IfB?]mOnO*LB%;6+?0=\NrT.U,6.[sQi@!ceO]]>#=@//1]RJsW;cet`*%Zk -JcC<$JcE=]J,~> -"!.E>FoMCDpEop4?iIkU[4_KFLB#Xsj#@!uNrT.U,6.[sQi@!ceO]]>#=@//1]RJsJ,[7bPZ`S; -JcC<$JcE=]J,~> -"!.E>FoMCDpEop4?iIkU[4_KFLB#Xsj#@!uNrT.U,6.[sQi@!ceO]]>#=@//1]RJsJ,[7bPZ`S; -JcC<$JcE=]J,~> -"!.E>FoMCDpEop4?iIkU[4_KFLB#Xsj#@!uNrT.U,6.[sQi@!ceO]]>#=@//1]RJsJ,[7bPZ`S; -JcC<$JcE=]J,~> -"!.E>FoMCDpEop4/cJof<%e3:L;e#pju<=#NrT.U,6.[sQi@!neO]`?`bsu(1]RJs4T/F`P_&jc -s+13$s185\~> -"!.E>FoMCDpEop4/cJof<%e3:L;e#pju<=#NrT.U,6.[sQi@!neO]`?`bsu(1]RJs4T/F`P_&jc -s+13$s185\~> -"!.E>FoMCDpEop4/cJof<%e3:L;e#pju<=#NrT.U,6.[sQi@!neO]`?`bsu(1]RJs4T/F`P_&jc -s+13$s185\~> -"!.E>FoMCDN@+[tIfB?]mOnO*7t'7oju<=#NrT.U,6.[sQi@!neO]`?@(lU)1]RJsW.Fu"s+13$ -s0_lW~> -"!.E>FoMCDN@+[tIfB?]mOnO*7t'7oju<=#NrT.U,6.[sQi@!neO]`?@(lU)1]RJsW.Fu"s+13$ -s0_lW~> -"!.E>FoMCDN@+[tIfB?]mOnO*7t'7oju<=#NrT.U,6.[sQi@!neO]`?@(lU)1]RJsW.Fu"s+13$ -s0_lW~> -"!.EqJc9EfN$eRsIfB?]mOnO*:>,[Iju<=#4DS_H%ZgY6I/[miEjG_O-r4Vf'q,*@H2_RZNIh+\ -s+13$s1//[~> -"!.EqJc9EfN$eRsIfB?]mOnO*:>,[Iju<=#4DS_H%ZgY6I/[miEjG_O-r4Vf'q,*@H2_RZNIh+\ -s+13$s1//[~> -"!.EqJc9EfN$eRsIfB?]mOnO*:>,[Iju<=#4DS_H%ZgY6I/[miEjG_O-r4Vf'q,*@H2_RZNIh+\ -s+13$s1//[~> -"!.FAg]"G\n0\1pfDbdQqS3'fgA_*SpVQscrNQKo5QCc2rilU?s8RLcrilTd;#gR>rilI@JcC<$ -JcC<$\c70~> -"!.FAg]"G\n0\1pfDbdQqS3'fgA_*SpVQscrNQKo5QCc2rilU?s8RLcrilTd;#gR>rilI@JcC<$ -JcC<$\c70~> -"!.FAg]"G\n0\1pfDbdQqS3'fgA_*SpVQscrNQKo5QCc2rilU?s8RLcrilTd;#gR>rilI@JcC<$ -JcC<$\c70~> -"!.EOOT,7\pEo5+!Sp!8rrM9Lrr3#lh1>TWs+13$s0DZT~> -"!.EOOT,7\pEo5+!Sp!8rrM9Lrr3#lh1>TWs+13$s0DZT~> -"!.EOOT,7\pEo5+!Sp!8rrM9Lrr3#lh1>TWs+13$s0DZT~> -"!.F:N;agu,jt!u!W;G<s+13$s+13Es*t~> -"!.F:N;agu,jt!u!W;G<s+13$s+13Es*t~> -"!.F:N;agu,jt!u!W;G<s+13$s+13Es*t~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$1k7!Tr3hs+13$s+130s*t~> -!$1k7!Tr3hs+13$s+130s*t~> -!$1k7!Tr3hs+13$s+130s*t~> -!$1k7!M=gls+13$s+130s*t~> -!$1k7!M=gls+13$s+130s*t~> -!$1k7!M=gls+13$s+130s*t~> -!$1k7!M=gls+13$s+130s*t~> -!$1k7!M=gls+13$s+130s*t~> -!$1k7!M=gls+13$s+130s*t~> -"!.FA\c-1DC]FF,IfB?Jon%bpJcC<$JcCo5J,~> -"!.FA\c-1DC]FF,IfB?Jon%bpJcC<$JcCo5J,~> -"!.FA\c-1DC]FF,IfB?Jon%bpJcC<$JcCo5J,~> -"!.EWHiCG[ZXj*.IfKF\:r@kaJcC<$JcCo5J,~> -"!.EWHiCG[ZXj*.IfKF\:r@kaJcC<$JcCo5J,~> -"!.EWHiCG[ZXj*.IfKF\:r@kaJcC<$JcCo5J,~> -"!.E>FoMCHpEop4IX`oYeq*jPs+13$s,m>3~> -"!.E>FoMCHpEop4IX`oYeq*jPs+13$s,m>3~> -"!.E>FoMCHpEop4IX`oYeq*jPs+13$s,m>3~> -"!.E>FoMCGpEop4%[H&MJcC<$JcC<$OT0h~> -"!.E>FoMCGpEop4%[H&MJcC<$JcC<$OT0h~> -"!.E>FoMCGpEop4%[H&MJcC<$JcC<$OT0h~> -"!.E>FoMCHpEop4IXWfXf7EsQs+13$s,m>3~> -"!.E>FoMCHpEop4IXWfXf7EsQs+13$s,m>3~> -"!.E>FoMCHpEop4IXWfXf7EsQs+13$s,m>3~> -"!.EWHiCJ\Zt'-.IfKF[:W.haJcC<$JcCo5J,~> -"!.EWHiCJ\Zt'-.IfKF[:W.haJcC<$JcCo5J,~> -"!.EWHiCJ\Zt'-.IfKF[:W.haJcC<$JcCo5J,~> -"!.FA\Gg"ACB+=QZ2Xb(omhVnJcC<$JcCo5J,~> -"!.FA\Gg"ACB+=QZ2Xb(omhVnJcC<$JcCo5J,~> -"!.FA\Gg"ACB+=QZ2Xb(omhVnJcC<$JcCo5J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$1>(!5\M"!/Q4+JcC<$JcDMFJ,~> -!$1>(!5\M"!/Q4+JcC<$JcDMFJ,~> -!$1>(!5\M"!/Q4+JcC<$JcDMFJ,~> -!$1>(!NTW+fE%aPJcC<$JcC<$U]5i~> -!$1>(!NTW+fE%aPJcC<$JcC<$U]5i~> -!$1>(!NTW+fE%aPJcC<$JcC<$U]5i~> -!$2";!UqE7rrS:OqY'piX)\(;!Phrks+13$s+13Fs*t~> -!$2";!UqE7rrS:OqY'piX)\(;!Phrks+13$s+13Fs*t~> -!$2";!UqE7rrS:OqY'piX)\(;!Phrks+13$s+13Fs*t~> -!$2%<!o?]]rVlqMU\FN_!NU5<rrKo?JcC<$JcC<$U]5i~> -!$2%<!o?]]rVlqMU\FN_!NU5<rrKo?JcC<$JcC<$U]5i~> -!$2%<!o?]]rVlqMU\FN_!NU5<rrKo?JcC<$JcC<$U]5i~> -!$2(="4M)A*W5p=I\Q`7rrK0?r;Qf5>_2p+s+13$s.fUE~> -!$2(="4M)A*W5p=I\Q`7rrK0?r;Qf5>_2p+s+13$s.fUE~> -!$2(="4M)A*W5p=I\Q`7rrK0?r;Qf5>_2p+s+13$s.fUE~> -#9EjEc[=+>*W,j;JsuK7!NU5<rrKo?JcC<$JcC<$U]5i~> -#9EjEc[=+>*W,j;JsuK7!NU5<rrKo?JcC<$JcC<$U]5i~> -#9EjEc[=+>*W,j;JsuK7!NU5<rrKo?JcC<$JcC<$U]5i~> -#9EilJ'@rm*W#d9*VfX8X)\(;!Phrks+13$s+13Fs*t~> -#9EilJ'@rm*W#d9*VfX8X)\(;!Phrks+13$s+13Fs*t~> -#9EilJ'@rm*W#d9*VfX8X)\(;!Phrks+13$s+13Fs*t~> -"!.E>.fNZR$Zu=H!Ht@8rrK0?r;Qf5>_2p+s+13$s.fUE~> -"!.E>.fNZR$Zu=H!Ht@8rrK0?r;Qf5>_2p+s+13$s.fUE~> -"!.E>.fNZR$Zu=H!Ht@8rrK0?r;Qf5>_2p+s+13$s.fUE~> -!Zh=*rP/FLao25@FK>?7rrK0?r;Qf5>_2p+s+13$s.fUE~> -!Zh=*rP/FLao25@FK>?7rrK0?r;Qf5>_2p+s+13$s.fUE~> -!Zh=*rP/FLao25@FK>?7rrK0?r;Qf5>_2p+s+13$s.fUE~> -!$1t:!$2%<!dUd@p\t8pEW#h;^g)HjJcC<$JcDMFJ,~> -!$1t:!$2%<!dUd@p\t8pEW#h;^g)HjJcC<$JcDMFJ,~> -!$1t:!$2%<!dUd@p\t8pEW#h;^g)HjJcC<$JcDMFJ,~> -!$1t:!&FQR!g'2VpAY/oEW#h;^g)HjJcC<$JcDMFJ,~> -!$1t:!&FQR!g'2VpAY/oEW#h;^g)HjJcC<$JcDMFJ,~> -!$1t:!&FQR!g'2VpAY/oEW#h;^g)HjJcC<$JcDMFJ,~> -!$1>(!NU5<rrKo?JcC<$JcC<$U]5i~> -!$1>(!NU5<rrKo?JcC<$JcC<$U]5i~> -!$1>(!NU5<rrKo?JcC<$JcC<$U]5i~> -!$1>(!NSrmXT=#YJcC<$JcC<$U]5i~> -!$1>(!NSrmXT=#YJcC<$JcC<$U]5i~> -!$1>(!NSrmXT=#YJcC<$JcC<$U]5i~> -!$1>(!71L[!2G,FJcC<$JcDMFJ,~> -!$1>(!71L[!2G,FJcC<$JcDMFJ,~> -!$1>(!71L[!2G,FJcC<$JcDMFJ,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -!$-XjJcC<$JcC?%J,~> -%%EndData -showpage -%%Trailer -end -%%EOF diff --git a/lib/stdlib/doc/src/ushell2.gif b/lib/stdlib/doc/src/ushell2.gif Binary files differdeleted file mode 100644 index 273cf2078a..0000000000 --- a/lib/stdlib/doc/src/ushell2.gif +++ /dev/null diff --git a/lib/stdlib/doc/src/ushell2.ps b/lib/stdlib/doc/src/ushell2.ps deleted file mode 100644 index e6db3c2be2..0000000000 --- a/lib/stdlib/doc/src/ushell2.ps +++ /dev/null @@ -1,404 +0,0 @@ -%!PS-Adobe-3.0 -%%Creator: GIMP PostScript file plugin V 1.17 by Peter Kirchgessner -%%Title: ushell2.ps -%%CreationDate: Mon Mar 16 09:52:14 2009 -%%DocumentData: Clean7Bit -%%LanguageLevel: 2 -%%Pages: 1 -%%BoundingBox: 14 14 468 74 -%%EndComments -%%BeginProlog -% Use own dictionary to avoid conflicts -10 dict begin -%%EndProlog -%%Page: 1 1 -% Translate for offset -14.173228346456694 14.173228346456694 translate -% Translate to begin of first scanline -0 59.527559055118118 translate -453.54330708661422 -59.527559055118118 scale -% Image geometry -640 84 8 -% Transformation matrix -[ 640 0 0 84 0 0 ] -% Strings to hold RGB-samples per scanline -/rstr 640 string def -/gstr 640 string def -/bstr 640 string def -{currentfile /ASCII85Decode filter /RunLengthDecode filter rstr readstring pop} -{currentfile /ASCII85Decode filter /RunLengthDecode filter gstr readstring pop} -{currentfile /ASCII85Decode filter /RunLengthDecode filter bstr readstring pop} -true 3 -%%BeginData: 18704 ASCII Bytes -colorimage -J`MCCJ`MCCJ`M=~> -J`MCCJ`MCCJ`M=~> -J`MCCJ`MCCJ`M=~> -!)nG7JO+iQJO+lRJ,~> -!)nG7JO+iQJO+lRJ,~> -!)nG7JO+iQJO+lRJ,~> -!D=/Y=b0_,=b0_.=b$~> -!D=/Y=b0_,=b0_.=b$~> -!D=/Y=b0_,=b0_.=b$~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M>rrKOJQ2^i3JcC<$JcFU,J,~> -!D>M>rrKOJQ2^i3JcC<$JcFU,J,~> -!D>M>rrKOJQ2^i3JcC<$JcFU,J,~> -"%t`UXo(ooJ,'$ES](+!!dROKr;Qh<:2'>#!I!f.rr]@2s).a0!581-!+,Ru!6ss3!kNp@p\t9X -H-uWnOA=s3rrK.0nG`Jtr;QbGr_<HIrVlo/@tFZ2s+14-s*t~> -"%t`UXo(ooJ,'$ES](+!!dROKr;Qh<:2'>#!I!f.rr]@2s).a0!581-!+,Ru!6ss3!kNp@p\t9X -H-uWnOA=s3rrK.0nG`Jtr;QbGr_<HIrVlo/@tFZ2s+14-s*t~> -"%t`UXo(ooJ,'$ES](+!!dROKr;Qh<:2'>#!I!f.rr]@2s).a0!581-!+,Ru!6ss3!kNp@p\t9X -H-uWnOA=s3rrK.0nG`Jtr;QbGr_<HIrVlo/@tFZ2s+14-s*t~> -"A:iVM0T!F!8RAL!HP42rrUp#*W5p=fZ5E4rrG1@rr3+VQ2`JQrQbK2q#:?CrQbLB\*j7sS:L>5 -!T-**rrJOjli.%FchRG<!HlihrrIe?nc&Yn]MJP,!J%!FcN0Vnrr3#u++aHCs+14-s*t~> -"A:iVM0T!F!8RAL!HP42rrUp#*W5p=fZ5E4rrG1@rr3+VQ2`JQrQbK2q#:?CrQbLB\*j7sS:L>5 -!T-**rrJOjli.%FchRG<!HlihrrIe?nc&Yn]MJP,!J%!FcN0Vnrr3#u++aHCs+14-s*t~> -"A:iVM0T!F!8RAL!HP42rrUp#*W5p=fZ5E4rrG1@rr3+VQ2`JQrQbK2q#:?CrQbLB\*j7sS:L>5 -!T-**rrJOjli.%FchRG<!HlihrrIe?nc&Yn]MJP,!J%!FcN0Vnrr3#u++aHCs+14-s*t~> -"A:iVM3Rl:!;QQc#kn;uEiSm+o_8@b!;HKb!;HEk!$;%;!V[r6rs;$Hs8VtORf>#.rq$3eoD\lN -QMU(M!l&CArVllrrU^6ks5-$;rq$<ks8W#qoFLs$rV6Ego^r._r:GB$o^qnX!W2]krs&2ss8)B[ -rVlg+qXdY#o_/=bKUDN+o_/%Z$2J#IrQSC<s'rY>rrMrfrVloH97d+os+14.s*t~> -"A:iVM3Rl:!;QQc#kn;uEiSm+o_8@b!;HKb!;HEk!$;%;!V[r6rs;$Hs8VtORf>#.rq$3eoD\lN -QMU(M!l&CArVllrrU^6ks5-$;rq$<ks8W#qoFLs$rV6Ego^r._r:GB$o^qnX!W2]krs&2ss8)B[ -rVlg+qXdY#o_/=bKUDN+o_/%Z$2J#IrQSC<s'rY>rrMrfrVloH97d+os+14.s*t~> -"A:iVM3Rl:!;QQc#kn;uEiSm+o_8@b!;HKb!;HEk!$;%;!V[r6rs;$Hs8VtORf>#.rq$3eoD\lN -QMU(M!l&CArVllrrU^6ks5-$;rq$<ks8W#qoFLs$rV6Ego^r._r:GB$o^qnX!W2]krs&2ss8)B[ -rVlg+qXdY#o_/=bKUDN+o_/%Z$2J#IrQSC<s'rY>rrMrfrVloH97d+os+14.s*t~> -#>7/YM1+"-g&D!PlY?DI$r#a^Efa$E-rf=.k?@j5!F+acrr=,;rrMj@o`#1<s8Sp?s8O^er[7f! -nFce_n:I\.rrK]@r;QcHr?qfQqqj5tr[7lYrV35f-kO2nd:1^L-n*1:a!_W=-n+farrK!?rr35q -.KBF2-jn]-rsk_N%3R&fs8Rb&=stdhj7rWTKtI?a"NGcfA%MI8!T6+ls+13$s5<p-~> -#>7/YM1+"-g&D!PlY?DI$r#a^Efa$E-rf=.k?@j5!F+acrr=,;rrMj@o`#1<s8Sp?s8O^er[7f! -nFce_n:I\.rrK]@r;QcHr?qfQqqj5tr[7lYrV35f-kO2nd:1^L-n*1:a!_W=-n+farrK!?rr35q -.KBF2-jn]-rsk_N%3R&fs8Rb&=stdhj7rWTKtI?a"NGcfA%MI8!T6+ls+13$s5<p-~> -#>7/YM1+"-g&D!PlY?DI$r#a^Efa$E-rf=.k?@j5!F+acrr=,;rrMj@o`#1<s8Sp?s8O^er[7f! -nFce_n:I\.rrK]@r;QcHr?qfQqqj5tr[7lYrV35f-kO2nd:1^L-n*1:a!_W=-n+farrK!?rr35q -.KBF2-jn]-rsk_N%3R&fs8Rb&=stdhj7rWTKtI?a"NGcfA%MI8!T6+ls+13$s5<p-~> -#>7/YM'th_:B(7oI@pN=%,$43E\H&Es1hq?B=@g>!OQP=rr=,;rrMj@o`#1<s8Sp?s8Qb.rr3"H -U%SEdTmZ8-!P2e8rrb=Bh[t\DrrXkBdn0N<#U90GWZSDDruV1>!J6g6rs#?Ds3C3-.K08I6JDA; -!EI5>rs"-+b5_Li9CVrcA%M^:\[g>jrrM%@JcC<$JcF[.J,~> -#>7/YM'th_:B(7oI@pN=%,$43E\H&Es1hq?B=@g>!OQP=rr=,;rrMj@o`#1<s8Sp?s8Qb.rr3"H -U%SEdTmZ8-!P2e8rrb=Bh[t\DrrXkBdn0N<#U90GWZSDDruV1>!J6g6rs#?Ds3C3-.K08I6JDA; -!EI5>rs"-+b5_Li9CVrcA%M^:\[g>jrrM%@JcC<$JcF[.J,~> -#>7/YM'th_:B(7oI@pN=%,$43E\H&Es1hq?B=@g>!OQP=rr=,;rrMj@o`#1<s8Sp?s8Qb.rr3"H -U%SEdTmZ8-!P2e8rrb=Bh[t\DrrXkBdn0N<#U90GWZSDDruV1>!J6g6rs#?Ds3C3-.K08I6JDA; -!EI5>rs"-+b5_Li9CVrcA%M^:\[g>jrrM%@JcC<$JcF[.J,~> -"A:iVM3Ro;!O.@YS-6(rs)P.=rrg,Cs(?9LS,mM@rVlj=qu6]k-M7<@Ff=JZXSVqtI%g96!p59A -mJd3uA,Q?,li!=U,37WFrVlsOnC'u-rraPCs/L,=rr@3@rrI_@p&>5nG5k:_oI9b=!CGN<rrH-@ -rVlnOR/[*ddS'*0#%P7Eo4(4*rVloV4b<Was+14.s*t~> -"A:iVM3Ro;!O.@YS-6(rs)P.=rrg,Cs(?9LS,mM@rVlj=qu6]k-M7<@Ff=JZXSVqtI%g96!p59A -mJd3uA,Q?,li!=U,37WFrVlsOnC'u-rraPCs/L,=rr@3@rrI_@p&>5nG5k:_oI9b=!CGN<rrH-@ -rVlnOR/[*ddS'*0#%P7Eo4(4*rVloV4b<Was+14.s*t~> -"A:iVM3Ro;!O.@YS-6(rs)P.=rrg,Cs(?9LS,mM@rVlj=qu6]k-M7<@Ff=JZXSVqtI%g96!p59A -mJd3uA,Q?,li!=U,37WFrVlsOnC'u-rraPCs/L,=rr@3@rrI_@p&>5nG5k:_oI9b=!CGN<rrH-@ -rVlnOR/[*ddS'*0#%P7Eo4(4*rVloV4b<Was+14.s*t~> -"A:iVM3Rl:!3lHR"G!EMEiT-="Me=CB3bCd!4;_)!$;%;!V[r5rr`6B_HQd8!IUU7rrKi?m/I*t -A,ZE.oRHgj"!$CZ55kK`/b%4\rVm!Equ<[:qYpSJS+ZdcVKVu6ch&[brrGI?r;QdtaSl,>KUDc= -!RaX1rrHW@rr3#'D#OA7i&uYkJcC<$i;\<~> -"A:iVM3Rl:!3lHR"G!EMEiT-="Me=CB3bCd!4;_)!$;%;!V[r5rr`6B_HQd8!IUU7rrKi?m/I*t -A,ZE.oRHgj"!$CZ55kK`/b%4\rVm!Equ<[:qYpSJS+ZdcVKVu6ch&[brrGI?r;QdtaSl,>KUDc= -!RaX1rrHW@rr3#'D#OA7i&uYkJcC<$i;\<~> -"A:iVM3Rl:!3lHR"G!EMEiT-="Me=CB3bCd!4;_)!$;%;!V[r5rr`6B_HQd8!IUU7rrKi?m/I*t -A,ZE.oRHgj"!$CZ55kK`/b%4\rVm!Equ<[:qYpSJS+ZdcVKVu6ch&[brrGI?r;QdtaSl,>KUDc= -!RaX1rrHW@rr3#'D#OA7i&uYkJcC<$i;\<~> -"A:iVM3Ro;!O'H@rrgMCs)P.=rrg,Cs(Ae8rr=,;rrMj@o`#$m+2V4\rrITArr3"HU&P&nhI3>\ -rrVG(_X.:(jbC#[email protected]($+6gRf<<h/b%4\rVm!Equ<[:qYp\MS,`M0q#:PqG,("4kUHK1 -!CGN<rs)QFs6>F?KUDc=!RaX1rrHW@rr3#'D#XG9rb/]js+13$s5<p-~> -"A:iVM3Ro;!O'H@rrgMCs)P.=rrg,Cs(Ae8rr=,;rrMj@o`#$m+2V4\rrITArr3"HU&P&nhI3>\ -rrVG(_X.:(jbC#[email protected]($+6gRf<<h/b%4\rVm!Equ<[:qYp\MS,`M0q#:PqG,("4kUHK1 -!CGN<rs)QFs6>F?KUDc=!RaX1rrHW@rr3#'D#XG9rb/]js+13$s5<p-~> -"A:iVM3Ro;!O'H@rrgMCs)P.=rrg,Cs(Ae8rr=,;rrMj@o`#$m+2V4\rrITArr3"HU&P&nhI3>\ -rrVG(_X.:(jbC#[email protected]($+6gRf<<h/b%4\rVm!Equ<[:qYp\MS,`M0q#:PqG,("4kUHK1 -!CGN<rs)QFs6>F?KUDc=!RaX1rrHW@rr3#'D#XG9rb/]js+13$s5<p-~> -"A:iVM'r6h"*ae$E;i3->g`RlWrE#$^g6ulB)Y-tHN*pIEC1"!K)YcQBL)tmMtR)OX^KZXrrL26 -r^-^;k5PA^aT->ZrrLn@lMgmTJH#QLq5"!W"d:Fsh_(&j6Na7`oU5YF"K_V&WcIt9"Q"/d6bE-n% -HrJ%lcE#Es8S]Y&NNhhrs-tX6W;80KUDc=!RaX1rrL\]r^-^"f`(mOrZ?^kJcC<$huA3~> -"A:iVM'r6h"*ae$E;i3->g`RlWrE#$^g6ulB)Y-tHN*pIEC1"!K)YcQBL)tmMtR)OX^KZXrrL26 -r^-^;k5PA^aT->ZrrLn@lMgmTJH#QLq5"!W"d:Fsh_(&j6Na7`oU5YF"K_V&WcIt9"Q"/d6bE-n% -HrJ%lcE#Es8S]Y&NNhhrs-tX6W;80KUDc=!RaX1rrL\]r^-^"f`(mOrZ?^kJcC<$huA3~> -"A:iVM'r6h"*ae$E;i3->g`RlWrE#$^g6ulB)Y-tHN*pIEC1"!K)YcQBL)tmMtR)OX^KZXrrL26 -r^-^;k5PA^aT->ZrrLn@lMgmTJH#QLq5"!W"d:Fsh_(&j6Na7`oU5YF"K_V&WcIt9"Q"/d6bE-n% -HrJ%lcE#Es8S]Y&NNhhrs-tX6W;80KUDc=!RaX1rrL\]r^-^"f`(mOrZ?^kJcC<$huA3~> -"%t`Umek`?jT#8Drn.G5s8VBUrr3,pkPtSCrn.;4rr2uYrn.;6rr2uWrn.;8p&>'hir&fVh#>t, -mJ[%dpY"j1rrMuVlMgqTJ#rYL!;QQH"nL[MqW%/Gf`V$Ls7H9C"T&/uoBQ/O"6eFkrV-<pmdL2U -hZ!NTm/GZ<h>I9Win<2gs6]=TrrMoUn,ECKrn.;5rVlm_hLY]Xs+14-s*t~> -"%t`Umek`?jT#8Drn.G5s8VBUrr3,pkPtSCrn.;4rr2uYrn.;6rr2uWrn.;8p&>'hir&fVh#>t, -mJ[%dpY"j1rrMuVlMgqTJ#rYL!;QQH"nL[MqW%/Gf`V$Ls7H9C"T&/uoBQ/O"6eFkrV-<pmdL2U -hZ!NTm/GZ<h>I9Win<2gs6]=TrrMoUn,ECKrn.;5rVlm_hLY]Xs+14-s*t~> -"%t`Umek`?jT#8Drn.G5s8VBUrr3,pkPtSCrn.;4rr2uYrn.;6rr2uWrn.;8p&>'hir&fVh#>t, -mJ[%dpY"j1rrMuVlMgqTJ#rYL!;QQH"nL[MqW%/Gf`V$Ls7H9C"T&/uoBQ/O"6eFkrV-<pmdL2U -hZ!NTm/GZ<h>I9Win<2gs6]=TrrMoUn,ECKrn.;5rVlm_hLY]Xs+14-s*t~> -!D>M>rrN#pQ2^jZJcC<$JcFU,J,~> -!D>M>rrN#pQ2^jZJcC<$JcFU,J,~> -!D>M>rrN#pQ2^jZJcC<$JcFU,J,~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>NQrrK"krVlo0ipm$KT)JZh\GVHh_h%i=s+13$s/Z0M~> -!D>NQrrK"krVlo0ipm$KT)JZh\GVHh_h%i=s+13$s/Z0M~> -!D>NQrrK"krVlo0ipm$KT)JZh\GVHh_h%i=s+13$s/Z0M~> -!D>NRrrSc-mJ[%d[sIB4rsbK>%<J'>s0[duMN!M0JcC<$JcC<$X8d\~> -!D>NRrrSc-mJ[%d[sIB4rsbK>%<J'>s0[duMN!M0JcC<$JcC<$X8d\~> -!D>NRrrSc-mJ[%d[sIB4rsbK>%<J'>s0[duMN!M0JcC<$JcC<$X8d\~> -#>7/Ys.,5omJQtc^LI'5rsP9XW>)=oS,`M<TDjEAJcC<$JcD\KJ,~> -#>7/Ys.,5omJQtc^LI'5rsP9XW>)=oS,`M<TDjEAJcC<$JcD\KJ,~> -#>7/Ys.,5omJQtc^LI'5rsP9XW>)=oS,`M<TDjEAJcC<$JcD\KJ,~> -#>7/YUPnOomJHnacVF36$>6gI*rkZbs8P:?JcC<$JcC<$VuM8~> -#>7/YUPnOomJHnacVF36$>6gI*rkZbs8P:?JcC<$JcC<$VuM8~> -#>7/YUPnOomJHnacVF36$>6gI*rkZbs8P:?JcC<$JcC<$VuM8~> -#>7/YoCW&:mJ?h`dn0<6"RfFC*W>s:"6h]b:P&Oss+13$s/H$K~> -#>7/YoCW&:mJ?h`dn0<6"RfFC*W>s:"6h]b:P&Oss+13$s/H$K~> -#>7/YoCW&:mJ?h`dn0<6"RfFC*W>s:"6h]b:P&Oss+13$s/H$K~> -!D>NQrrFh@qYpTB:A+Vm7Kc'As8V@[j+75]s+13$s/5mI~> -!D>NQrrFh@qYpTB:A+Vm7Kc'As8V@[j+75]s+13$s/5mI~> -!D>NQrrFh@qYpTB:A+Vm7Kc'As8V@[j+75]s+13$s/5mI~> -!D>NQrrFh@qu6]>;Y0nm*o1A]s'3Bks+13$s+13Is*t~> -!D>NQrrFh@qu6]>;Y0nm*o1A]s'3Bks+13$s+13Is*t~> -!D>NQrrFh@qu6]>;Y0nm*o1A]s'3Bks+13$s+13Is*t~> -!D>NQrrFh@r;Qi1B);6$$-D</*riT\s'3D9rrW+o]Rg*6s+13$s0;TS~> -!D>NQrrFh@r;Qi1B);6$$-D</*riT\s'3D9rrW+o]Rg*6s+13$s0;TS~> -!D>NQrrFh@r;Qi1B);6$$-D</*riT\s'3D9rrW+o]Rg*6s+13$s0;TS~> -%SJn`a+f<dI>4^#s0R1?p&>?%JqahkK!>9SKDpT*YPnJ&pAdU4s+13$s+13Ts*t~> -%SJn`a+f<dI>4^#s0R1?p&>?%JqahkK!>9SKDpT*YPnJ&pAdU4s+13$s+13Ts*t~> -%SJn`a+f<dI>4^#s0R1?p&>?%JqahkK!>9SKDpT*YPnJ&pAdU4s+13$s+13Ts*t~> -"%t`UcMWt2ZiC'>jR`BS[$D;i[Jp1+Yl9phLTURU!rS@iJcC<$JcC<$Z2]=~> -"%t`UcMWt2ZiC'>jR`BS[$D;i[Jp1+Yl9phLTURU!rS@iJcC<$JcC<$Z2]=~> -"%t`UcMWt2ZiC'>jR`BS[$D;i[Jp1+Yl9phLTURU!rS@iJcC<$JcC<$Z2]=~> -!D>N9rrBb-rrK*@JcC<$JcC<$X8d\~> -!D>N9rrBb-rrK*@JcC<$JcC<$X8d\~> -!D>N9rrBb-rrK*@JcC<$JcC<$X8d\~> -!D>N1rr_L<A`eRDJcC<$JcDeNJ,~> -!D>N1rr_L<A`eRDJcC<$JcDeNJ,~> -!D>N1rr_L<A`eRDJcC<$JcDeNJ,~> -!D>N1rrW/foR[$ns+13$s/Q*L~> -!D>N1rrW/foR[$ns+13$s/Q*L~> -!D>N1rrW/foR[$ns+13$s/Q*L~> -"A:iVs5s=1"5EkUl29$2ir8rZh:1/0s+13$s+13<s*t~> -"A:iVs5s=1"5EkUl29$2ir8rZh:1/0s+13$s+13<s*t~> -"A:iVs5s=1"5EkUl29$2ir8rZh:1/0s+13$s+13<s*t~> -"A:iVh08ih"Bs"KI4bCg#D<'VcX9:FaasJCs+13$s-it<~> -"A:iVh08ih"Bs"KI4bCg#D<'VcX9:FaasJCs+13$s-it<~> -"A:iVh08ih"Bs"KI4bCg#D<'VcX9:FaasJCs+13$s-it<~> -"A:iVM3S#>"P-'BI@pE:#01rEs6Fa?JcC<$JcC<$S,\!~> -"A:iVM3S#>"P-'BI@pE:#01rEs6Fa?JcC<$JcC<$S,\!~> -"A:iVM3S#>"P-'BI@pE:#01rEs6Fa?JcC<$JcC<$S,\!~> -"A:iVM3S#>&_9GOI@m*XH^=^3]7/f?mX;?ks+13$s+13>s*t~> -"A:iVM3S#>&_9GOI@m*XH^=^3]7/f?mX;?ks+13$s+13>s*t~> -"A:iVM3S#>&_9GOI@m*XH^=^3]7/f?mX;?ks+13$s+13>s*t~> -(eZsjM3S&?peOBZI63OaTp(Z>s1iGWB=NNmJcC<$JcD2=J,~> -(eZsjM3S&?peOBZI63OaTp(Z>s1iGWB=NNmJcC<$JcD2=J,~> -(eZsjM3S&?peOBZI63OaTp(Z>s1iGWB=NNmJcC<$JcD2=J,~> -(eZsjiH7CN7\E6NhS8LYs3"=?s+$lZY&JslJcC<$JcD2=J,~> -(eZsjiH7CN7\E6NhS8LYs3"=?s+$lZY&JslJcC<$JcD2=J,~> -(eZsjiH7CN7\E6NhS8LYs3"=?s+$lZY&JslJcC<$JcD2=J,~> -#tmA[s6JIqhUY0nrs?kHs+R*>s8&m>JcC<$JcC<$S,\!~> -#tmA[s6JIqhUY0nrs?kHs+R*>s8&m>JcC<$JcC<$S,\!~> -#tmA[s6JIqhUY0nrs?kHs+R*>s8&m>JcC<$JcC<$S,\!~> -!D>NPrrrDA=TA!drr3;J;ZD6@qZ$K"Ck;V;s+13$s-s%=~> -!D>NPrrrDA=TA!drr3;J;ZD6@qZ$K"Ck;V;s+13$s-s%=~> -!D>NPrrrDA=TA!drr3;J;ZD6@qZ$K"Ck;V;s+13$s-s%=~> -$qi\^s5;#@_cHg;R?IQk#ci.Ks*1Qc[V16lJcC<$JcD2=J,~> -$qi\^s5;#@_cHg;R?IQk#ci.Ks*1Qc[V16lJcC<$JcD2=J,~> -$qi\^s5;#@_cHg;R?IQk#ci.Ks*1Qc[V16lJcC<$JcD2=J,~> -#>7/Ys-lqc>Q+R%Pl=S]G5_FBGZ/@.s+13$s+13<s*t~> -#>7/Ys-lqc>Q+R%Pl=S]G5_FBGZ/@.s+13$s+13<s*t~> -#>7/Ys-lqc>Q+R%Pl=S]G5_FBGZ/@.s+13$s+13<s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -"A:iVrHeGa#!r.ds)PpSp&>3n>ok%TqqqDSq^h^f!K[21s+13$s+13ns*t~> -"A:iVrHeGa#!r.ds)PpSp&>3n>ok%TqqqDSq^h^f!K[21s+13$s+13ns*t~> -"A:iVrHeGa#!r.ds)PpSp&>3n>ok%TqqqDSq^h^f!K[21s+13$s+13ns*t~> -"A:iVM3S#>!S0a>rrQHBrq??qB=@j?dS&Kt!DCl?rrJ._JcC<$JcC<$bQ!(~> -"A:iVM3S#>!S0a>rrQHBrq??qB=@j?dS&Kt!DCl?rrJ._JcC<$JcC<$bQ!(~> -"A:iVM3S#>!S0a>rrQHBrq??qB=@j?dS&Kt!DCl?rrJ._JcC<$JcC<$bQ!(~> -#tmA[Vm$.#r2Oo<rrHB@pAYG3[f?BU9E5%Cn,E=fl.l:<!64s:$./AAbQ$Y^s8U+<^B!*hrr<u: -^B!3krrBk6^B!;Fs+13$s+13us*t~> -#tmA[Vm$.#r2Oo<rrHB@pAYG3[f?BU9E5%Cn,E=fl.l:<!64s:$./AAbQ$Y^s8U+<^B!*hrr<u: -^B!3krrBk6^B!;Fs+13$s+13us*t~> -#tmA[Vm$.#r2Oo<rrHB@pAYG3[f?BU9E5%Cn,E=fl.l:<!64s:$./AAbQ$Y^s8U+<^B!*hrr<u: -^B!3krrBk6^B!;Fs+13$s+13us*t~> -!D>NPrrJs@qu6\1[.jS,B60c?KhMIG>f$F>!NC2?rr=,<rs;-HlS8F'MZ:+rra#_Uf`/6dra#_X -h#FNara#VXi.:oZs+13$s3q!u~> -!D>NPrrJs@qu6\1[.jS,B60c?KhMIG>f$F>!NC2?rr=,<rs;-HlS8F'MZ:+rra#_Uf`/6dra#_X -h#FNara#VXi.:oZs+13$s3q!u~> -!D>NPrrJs@qu6\1[.jS,B60c?KhMIG>f$F>!NC2?rr=,<rs;-HlS8F'MZ:+rra#_Uf`/6dra#_X -h#FNara#VXi.:oZs+13$s3q!u~> -!D>NQrrJU@qYpT[2#%"[B39M-AkW1(X+Kg?jFOf>rr=,<rs;-HlS8F'MZ5`trr3+VQ2`K)rr3+K -U&QA5rr3#-e:IXNs+13$s3q!u~> -!D>NQrrJU@qYpT[2#%"[B39M-AkW1(X+Kg?jFOf>rr=,<rs;-HlS8F'MZ5`trr3+VQ2`K)rr3+K -U&QA5rr3#-e:IXNs+13$s3q!u~> -!D>NQrrJU@qYpT[2#%"[B39M-AkW1(X+Kg?jFOf>rr=,<rs;-HlS8F'MZ5`trr3+VQ2`K)rr3+K -U&QA5rr3#-e:IXNs+13$s3q!u~> -!D>NRrrSFGq"k!kDm&j7"_Y:Ds3aR>rrbOCs2S1=rr=,<rs;-HlS8F'MZ5`trr3+VQ2`K)rr34N -U&V$'AnJ&os+13$s+13ts*t~> -!D>NRrrSFGq"k!kDm&j7"_Y:Ds3aR>rrbOCs2S1=rr=,<rs;-HlS8F'MZ5`trr3+VQ2`K)rr34N -U&V$'AnJ&os+13$s+13ts*t~> -!D>NRrrSFGq"k!kDm&j7"_Y:Ds3aR>rrbOCs2S1=rr=,<rs;-HlS8F'MZ5`trr3+VQ2`K)rr34N -U&V$'AnJ&os+13$s+13ts*t~> -#"q&Xs-8l>qYpS+](Z.-B=@j?dS'T>"NXX/@F+oP!$;(<#t<M41&mGps#T3>rre+Bs"<a=rs*qF -s'i@E\(?32JcC<$JcF-tJ,~> -#"q&Xs-8l>qYpS+](Z.-B=@j?dS'T>"NXX/@F+oP!$;(<#t<M41&mGps#T3>rre+Bs"<a=rs*qF -s'i@E\(?32JcC<$JcF-tJ,~> -#"q&Xs-8l>qYpS+](Z.-B=@j?dS'T>"NXX/@F+oP!$;(<#t<M41&mGps#T3>rre+Bs"<a=rs*qF -s'i@E\(?32JcC<$JcF-tJ,~> -"\UrWSW34:rrQZBrq??qB=@j?dS'Q=![-G]r;R9Ks2TNes$bT+1&mGps#T3>rre+Bs"<a=rrd_C -s!.@=rrItfrr3&A/!>J`JcC<$JcFF'J,~> -"\UrWSW34:rrQZBrq??qB=@j?dS'Q=![-G]r;R9Ks2TNes$bT+1&mGps#T3>rre+Bs"<a=rrd_C -s!.@=rrItfrr3&A/!>J`JcC<$JcFF'J,~> -"\UrWSW34:rrQZBrq??qB=@j?dS'Q=![-G]r;R9Ks2TNes$bT+1&mGps#T3>rre+Bs"<a=rrd_C -s!.@=rrItfrr3&A/!>J`JcC<$JcFF'J,~> -"A:iVO;Rp;"^M*-EkD;Crrr.#*Zd]BrVlrj2YI"J&*t?<p](9d4rX\IGkqC42?"X"Jbf?</H-[n -MYdAE,5rVaPl(I[!l>M%JcC<$JcC<$g&HR~> -"A:iVO;Rp;"^M*-EkD;Crrr.#*Zd]BrVlrj2YI"J&*t?<p](9d4rX\IGkqC42?"X"Jbf?</H-[n -MYdAE,5rVaPl(I[!l>M%JcC<$JcC<$g&HR~> -"A:iVO;Rp;"^M*-EkD;Crrr.#*Zd]BrVlrj2YI"J&*t?<p](9d4rX\IGkqC42?"X"Jbf?</H-[n -MYdAE,5rVaPl(I[!l>M%JcC<$JcC<$g&HR~> -!D>N"rrMF?JcC<$JcC<$\Gq'~> -!D>N"rrMF?JcC<$JcC<$\Gq'~> -!D>N"rrMF?JcC<$JcC<$\Gq'~> -!D>N"rrMF?JcC<$JcC<$\Gq'~> -!D>N"rrMF?JcC<$JcC<$\Gq'~> -!D>N"rrMF?JcC<$JcC<$\Gq'~> -!D>N"rrM_GJcC<$JcC<$\Gq'~> -!D>N"rrM_GJcC<$JcC<$\Gq'~> -!D>N"rrM_GJcC<$JcC<$\Gq'~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>NQrrIH;oD\f.li.!"FSu.?F^'4jrrR%AK'!"7oNPOlmf*=XI"D<Ks+13$s7QDB~> -!D>NQrrIH;oD\f.li.!"FSu.?F^'4jrrR%AK'!"7oNPOlmf*=XI"D<Ks+13$s7QDB~> -!D>NQrrIH;oD\f.li.!"FSu.?F^'4jrrR%AK'!"7oNPOlmf*=XI"D<Ks+13$s7QDB~> -#tmA[V69hPmGN'ZrrHa>li."*b5D8@bH`l#rrU9A4lu\Gqn\)'mf*=Q1YMd/Qb7nLJcC<$TDsE~> -#tmA[V69hPmGN'ZrrHa>li."*b5D8@bH`l#rrU9A4lu\Gqn\)'mf*=Q1YMd/Qb7nLJcC<$TDsE~> -#tmA[V69hPmGN'ZrrHa>li."*b5D8@bH`l#rrU9A4lu\Gqn\)'mf*=Q1YMd/Qb7nLJcC<$TDsE~> -#tmA[n8Punj$2c%rrQrD)WUhulS8;:!T6-$rrKH@mf*=Q1YMd/G,BijJcC<$TDsE~> -#tmA[n8Punj$2c%rrQrD)WUhulS8;:!T6-$rrKH@mf*=Q1YMd/G,BijJcC<$TDsE~> -#tmA[n8Punj$2c%rrQrD)WUhulS8;:!T6-$rrKH@mf*=Q1YMd/G,BijJcC<$TDsE~> -#YR8Zs6a"o,j+k$"+miX*r>m;&MNhW!_OI]qu6]_1&LtOi'%&=!U2-6*XdYos6(XJ*d)V6s8)[8 -!L<EYrrKH@pAY0m`Vgh\YP[T:VZ%V=\GZBJr>l?_rRb34I3'HPs8TJ)*WsmkpmV.&$f1io*Y&i) -*rl96M#JG%-KtK-K&fcM0C",gJcC<$ZN#F~> -#YR8Zs6a"o,j+k$"+miX*r>m;&MNhW!_OI]qu6]_1&LtOi'%&=!U2-6*XdYos6(XJ*d)V6s8)[8 -!L<EYrrKH@pAY0m`Vgh\YP[T:VZ%V=\GZBJr>l?_rRb34I3'HPs8TJ)*WsmkpmV.&$f1io*Y&i) -*rl96M#JG%-KtK-K&fcM0C",gJcC<$ZN#F~> -#YR8Zs6a"o,j+k$"+miX*r>m;&MNhW!_OI]qu6]_1&LtOi'%&=!U2-6*XdYos6(XJ*d)V6s8)[8 -!L<EYrrKH@pAY0m`Vgh\YP[T:VZ%V=\GZBJr>l?_rRb34I3'HPs8TJ)*WsmkpmV.&$f1io*Y&i) -*rl96M#JG%-KtK-K&fcM0C",gJcC<$ZN#F~> -#tmA[j`JBojA4A"rr=,:rrGJko`"sd-N!fDlS8;:!T6->rrFb?rr37OU&QA5s8S+>qYpS-\,QC. -ZYK46!VIi=rrd2tl3hh8rrG+?qu6sSnGfp5T`>$9qtpBq*rio]rVljnrr3"BVZ$MqVKVt=$1o\H -s*Z-8s6):js+13$s0DZT~> -#tmA[j`JBojA4A"rr=,:rrGJko`"sd-N!fDlS8;:!T6->rrFb?rr37OU&QA5s8S+>qYpS-\,QC. -ZYK46!VIi=rrd2tl3hh8rrG+?qu6sSnGfp5T`>$9qtpBq*rio]rVljnrr3"BVZ$MqVKVt=$1o\H -s*Z-8s6):js+13$s0DZT~> -#tmA[j`JBojA4A"rr=,:rrGJko`"sd-N!fDlS8;:!T6->rrFb?rr37OU&QA5s8S+>qYpS-\,QC. -ZYK46!VIi=rrd2tl3hh8rrG+?qu6sSnGfp5T`>$9qtpBq*rio]rVljnrr3"BVZ$MqVKVt=$1o\H -s*Z-8s6):js+13$s0DZT~> -#tmA[Z+0ffmGruorr=,+rrMj@qu6]_1&LtOi'%&=!A9uCW=)Uts!.@>s,N->s8Dnq!CO?qrrKH@ -pAY0d-i<oEl8/D="$#BAlM[[b*q93<BE%o5D#F=nrM]l7s1Mh9rrI;?rVlnq:B%4!U+--BI@pQ> -n9BNaJcC<$ZN#F~> -#tmA[Z+0ffmGruorr=,+rrMj@qu6]_1&LtOi'%&=!A9uCW=)Uts!.@>s,N->s8Dnq!CO?qrrKH@ -pAY0d-i<oEl8/D="$#BAlM[[b*q93<BE%o5D#F=nrM]l7s1Mh9rrI;?rVlnq:B%4!U+--BI@pQ> -n9BNaJcC<$ZN#F~> -#tmA[Z+0ffmGruorr=,+rrMj@qu6]_1&LtOi'%&=!A9uCW=)Uts!.@>s,N->s8Dnq!CO?qrrKH@ -pAY0d-i<oEl8/D="$#BAlM[[b*q93<BE%o5D#F=nrM]l7s1Mh9rrI;?rVlnq:B%4!U+--BI@pQ> -n9BNaJcC<$ZN#F~> -!D>NQrrJ/QoD\e3li."[-N!fDlS8;:!T6->rrF`grGr=hs+cMkN.JhFs-EV)!A:k\rrKH@pAY0d --i<oEl8/D="$#B2LAc/r'_).2BDhc1k(N\S!tkRH@/9g'G,G6<!N%dREs.C#s*^Mjs+13$s/uBP~> -!D>NQrrJ/QoD\e3li."[-N!fDlS8;:!T6->rrF`grGr=hs+cMkN.JhFs-EV)!A:k\rrKH@pAY0d --i<oEl8/D="$#B2LAc/r'_).2BDhc1k(N\S!tkRH@/9g'G,G6<!N%dREs.C#s*^Mjs+13$s/uBP~> -!D>NQrrJ/QoD\e3li."[-N!fDlS8;:!T6->rrF`grGr=hs+cMkN.JhFs-EV)!A:k\rrKH@pAY0d --i<oEl8/D="$#B2LAc/r'_).2BDhc1k(N\S!tkRH@/9g'G,G6<!N%dREs.C#s*^Mjs+13$s/uBP~> -!D>NArr=,:rrI;!o`"sd-N!fDlS8;:!T6->rrFb?qu6eNOHG[Brr2s>rr3&1/@YWY!OHP7rrMd? -rVltal0:)'rrY7Ah`h&>"dftms0cS<rrL2@rr3+[&c]OPrVlkIrr34HVZ6[Gs/'u9rrIP?JcC<$ -JcDnQJ,~> -!D>NArr=,:rrI;!o`"sd-N!fDlS8;:!T6->rrFb?qu6eNOHG[Brr2s>rr3&1/@YWY!OHP7rrMd? -rVltal0:)'rrY7Ah`h&>"dftms0cS<rrL2@rr3+[&c]OPrVlkIrr34HVZ6[Gs/'u9rrIP?JcC<$ -JcDnQJ,~> -!D>NArr=,:rrI;!o`"sd-N!fDlS8;:!T6->rrFb?qu6eNOHG[Brr2s>rr3&1/@YWY!OHP7rrMd? -rVltal0:)'rrY7Ah`h&>"dftms0cS<rrL2@rr3+[&c]OPrVlkIrr34HVZ6[Gs/'u9rrIP?JcC<$ -JcDnQJ,~> -!D>NDrs#)m;$g)sOT#1[%P@AR"ER<H;3V"Z"Dg[A;4IRb#\6F=;54*j]iY21"KMM%\l8T*"1%t, -[/No/IRUaGo2.Lk;8N&-!W8Vh;$3-Ul8/D="$#B7R/TqdP99;o[V,O<!Uc$J;$<@(s6>O@;#mj" -rs.%n;,Ok'j+I>.!*K7#!I^Sks+13$s/uBP~> -!D>NDrs#)m;$g)sOT#1[%P@AR"ER<H;3V"Z"Dg[A;4IRb#\6F=;54*j]iY21"KMM%\l8T*"1%t, -[/No/IRUaGo2.Lk;8N&-!W8Vh;$3-Ul8/D="$#B7R/TqdP99;o[V,O<!Uc$J;$<@(s6>O@;#mj" -rs.%n;,Ok'j+I>.!*K7#!I^Sks+13$s/uBP~> -!D>NDrs#)m;$g)sOT#1[%P@AR"ER<H;3V"Z"Dg[A;4IRb#\6F=;54*j]iY21"KMM%\l8T*"1%t, -[/No/IRUaGo2.Lk;8N&-!W8Vh;$3-Ul8/D="$#B7R/TqdP99;o[V,O<!Uc$J;$<@(s6>O@;#mj" -rs.%n;,Ok'j+I>.!*K7#!I^Sks+13$s/uBP~> -!D>NDrrD*WbQ-Q!rrLHrp&>$CrlbB"rr2uJrlbB%rr2uHrlbB'rr2uErlbN-s8Pm:rlbIg^&S*2 -bQR%cnc/OcbQ.&)rrDflbQID8qpt`G"5!DLoDZr;nC@I:nDX9E!:Kj1"SL4Cs6T^.!<)lr!oD/F -rr2u]rlbAfrr3#\m",1fs+13Qs*t~> -!D>NDrrD*WbQ-Q!rrLHrp&>$CrlbB"rr2uJrlbB%rr2uHrlbB'rr2uErlbN-s8Pm:rlbIg^&S*2 -bQR%cnc/OcbQ.&)rrDflbQID8qpt`G"5!DLoDZr;nC@I:nDX9E!:Kj1"SL4Cs6T^.!<)lr!oD/F -rr2u]rlbAfrr3#\m",1fs+13Qs*t~> -!D>NDrrD*WbQ-Q!rrLHrp&>$CrlbB"rr2uJrlbB%rr2uHrlbB'rr2uErlbN-s8Pm:rlbIg^&S*2 -bQR%cnc/OcbQ.&)rrDflbQID8qpt`G"5!DLoDZr;nC@I:nDX9E!:Kj1"SL4Cs6T^.!<)lr!oD/F -rr2u]rlbAfrr3#\m",1fs+13Qs*t~> -!D>MarrFV?rq?G6YCZ_)s+13$s3q!u~> -!D>MarrFV?rq?G6YCZ_)s+13$s3q!u~> -!D>MarrFV?rq?G6YCZ_)s+13$s3q!u~> -!D>M`rr=PJ-30Zhs+13$s+13us*t~> -!D>M`rr=PJ-30Zhs+13$s+13us*t~> -!D>M`rr=PJ-30Zhs+13$s+13us*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -"A:iVs2Y,1!3Z>$!Q4&Ns+13$s+132s*t~> -"A:iVs2Y,1!3Z>$!Q4&Ns+13$s+132s*t~> -"A:iVs2Y,1!3Z>$!Q4&Ns+13$s+132s*t~> -"A:iV`..c8#(NHKs0$t?JcC<$JcC<$OT0h~> -"A:iV`..c8#(NHKs0$t?JcC<$JcC<$OT0h~> -"A:iV`..c8#(NHKs0$t?JcC<$JcC<$OT0h~> -"A:iVM3S#>!S0a>rrTTDqgncus+13$s,m>3~> -"A:iVM3S#>!S0a>rrTTDqgncus+13$s,m>3~> -"A:iVM3S#>!S0a>rrTTDqgncus+13$s,m>3~> -#tmA[hr=\9pK5Z<rrL#?JcC<$JcC<$OoKq~> -#tmA[hr=\9pK5Z<rrL#?JcC<$JcC<$OoKq~> -#tmA[hr=\9pK5Z<rrL#?JcC<$JcC<$OoKq~> -!D>NPrrG@?qu6]<<.Y(#s+13$s-!D4~> -!D>NPrrG@?qu6]<<.Y(#s+13$s-!D4~> -!D>NPrrG@?qu6]<<.Y(#s+13$s-!D4~> -!D>NRrrVaZjneuXg-^GkJcC<$JcCo5J,~> -!D>NRrrVaZjneuXg-^GkJcC<$JcCo5J,~> -!D>NRrrVaZjneuXg-^GkJcC<$JcCo5J,~> -#"q&Xs5RM>qYpTI7tL\ks+13$s,m>3~> -#"q&Xs5RM>qYpTI7tL\ks+13$s,m>3~> -#"q&Xs5RM>qYpTI7tL\ks+13$s,m>3~> -"\UrWgN^j:rrU/EqLSZts+13$s,m>3~> -"\UrWgN^j:rrU/EqLSZts+13$s,m>3~> -"\UrWgN^j:rrU/EqLSZts+13$s,m>3~> -"A:iVM,sS>"gS+-]QWRks+13$s+133s*t~> -"A:iVM,sS>"gS+-]QWRks+13$s+133s*t~> -"A:iVM,sS>"gS+-]QWRks+13$s+133s*t~> -"%t`UaS^ktWrN+,i.:oZs+13$s,[21~> -"%t`UaS^ktWrN+,i.:oZs+13$s,[21~> -"%t`UaS^ktWrN+,i.:oZs+13$s,[21~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -!D>M*s+13$s+13&s*t~> -%%EndData -showpage -%%Trailer -end -%%EOF diff --git a/lib/stdlib/doc/src/ushell3.gif b/lib/stdlib/doc/src/ushell3.gif Binary files differdeleted file mode 100644 index 2141268b91..0000000000 --- a/lib/stdlib/doc/src/ushell3.gif +++ /dev/null diff --git a/lib/stdlib/doc/src/ushell3.ps b/lib/stdlib/doc/src/ushell3.ps deleted file mode 100644 index dd64eeab5c..0000000000 --- a/lib/stdlib/doc/src/ushell3.ps +++ /dev/null @@ -1,662 +0,0 @@ -%!PS-Adobe-3.0 -%%Creator: GIMP PostScript file plugin V 1.17 by Peter Kirchgessner -%%Title: ushell3.ps -%%CreationDate: Mon Mar 16 12:15:17 2009 -%%DocumentData: Clean7Bit -%%LanguageLevel: 2 -%%Pages: 1 -%%BoundingBox: 14 14 468 78 -%%EndComments -%%BeginProlog -% Use own dictionary to avoid conflicts -10 dict begin -%%EndProlog -%%Page: 1 1 -% Translate for offset -14.173228346456694 14.173228346456694 translate -% Translate to begin of first scanline -0 63.070866141732289 translate -453.54330708661422 -63.070866141732289 scale -% Image geometry -640 89 8 -% Transformation matrix -[ 640 0 0 89 0 0 ] -% Strings to hold RGB-samples per scanline -/rstr 640 string def -/gstr 640 string def -/bstr 640 string def -{currentfile /ASCII85Decode filter /RunLengthDecode filter rstr readstring pop} -{currentfile /ASCII85Decode filter /RunLengthDecode filter gstr readstring pop} -{currentfile /ASCII85Decode filter /RunLengthDecode filter bstr readstring pop} -true 3 -%%BeginData: 37475 ASCII Bytes -colorimage -!1\W%J`VIEJ`VLFJ,~> -!1\W%J`VIEJ`VLFJ,~> -!1\W%J`VIEJ`VLFJ,~> -!T[+/6@hIS6@hIU6@]~> -!T[+/6@hIS6@hIU6@]~> -!T[+/6@hIS6@hIU6@]~> -!ouWfJQ.2"JQ.2"KN*I~> -!ouWfJQ.2"JQ.2"KN*I~> -!ouWfJQ.2"JQ.2"KN*I~> -!ouXLJcC<$JcC<$K`?Q~> -!ouXLJcC<$JcC<$K`?Q~> -!ouXLJcC<$JcC<$K`?Q~> -!ouXLQ2^m!`0L?'S`PG&JcC<$i;\<~> -!ouXLQ2^m!`0L?'S`PG&JcC<$i;\<~> -!ouXLQ2^m!`0L?'S`PG&JcC<$i;\<~> -"QVjNs.9-i!1*E[!L)+*rrR=EF8Z%>Dc_2VrrI?7rr3)%^&N-N<rg/5rr?^0<rgP6rrU,eg\:^K -o6/O]!ioDNpAY05U[\9aJa!(4!2BHo!,VW6!lAR@JcC<$JcF^/J,~> -"QVjNs.9-i!1*E[!L)+*rrR=EF8Z%>Dc_2VrrI?7rr3)%^&N-N<rg/5rr?^0<rgP6rrU,eg\:^K -o6/O]!ioDNpAY05U[\9aJa!(4!2BHo!,VW6!lAR@JcC<$JcF^/J,~> -"QVjNs.9-i!1*E[!L)+*rrR=EF8Z%>Dc_2VrrI?7rr3)%^&N-N<rg/5rr?^0<rgP6rrU,eg\:^K -o6/O]!ioDNpAY05U[\9aJa!(4!2BHo!,VW6!lAR@JcC<$JcF^/J,~> -"lqsOs)!Pi_uSNdrrHB@o)Ad;\gmXX!mS).p&>%Ili-neJXl`h]D]YJlM1AZb5K6Z:po`k!N()7 -rrML@l2LdgT^2UZil(<`rrJFsrVlntF7K88K;)-<s8T'RrkniDCAn/50C=>jJcC<$iW"E~> -"lqsOs)!Pi_uSNdrrHB@o)Ad;\gmXX!mS).p&>%Ili-neJXl`h]D]YJlM1AZb5K6Z:po`k!N()7 -rrML@l2LdgT^2UZil(<`rrJFsrVlntF7K88K;)-<s8T'RrkniDCAn/50C=>jJcC<$iW"E~> -"lqsOs)!Pi_uSNdrrHB@o)Ad;\gmXX!mS).p&>%Ili-neJXl`h]D]YJlM1AZb5K6Z:po`k!N()7 -rrML@l2LdgT^2UZil(<`rrJFsrVlntF7K88K;)-<s8T'RrkniDCAn/50C=>jJcC<$iW"E~> -"lqsOs)#"3rrHB@nc&X\.f95Hkqi#4!AWp>rrdnCs!dR.rrSCIp@&"_b$si5rrML@l2LdPK]E(5 -GGY9<!NC/2rs4ONpZ4C7s,`6>rrN#\rVlo\2M(mZs+14/s*t~> -"lqsOs)#"3rrHB@nc&X\.f95Hkqi#4!AWp>rrdnCs!dR.rrSCIp@&"_b$si5rrML@l2LdPK]E(5 -GGY9<!NC/2rs4ONpZ4C7s,`6>rrN#\rVlo\2M(mZs+14/s*t~> -"lqsOs)#"3rrHB@nc&X\.f95Hkqi#4!AWp>rrdnCs!dR.rrSCIp@&"_b$si5rrML@l2LdPK]E(5 -GGY9<!NC/2rs4ONpZ4C7s,`6>rrN#\rVlo\2M(mZs+14/s*t~> -#in9Rs)"@hjludE!Us(f*"5s=s'2Za*#r;Ts6`DV*!'XArr3#i.f95Hkqi#4$2ktGs.=c>s!c4l -*!(ffp&>*eKVIo7!Q/(<rrCjQ*!EJBm4518*!<VHr42bG&A7u":Z[6L*5hd.*"`Z#*8gPk!Q/1@ -rrsVGs/-hF]Dhg?\f;.l*$"qWWG$>m*$OSArs>H^rr<#^9E1*5p\t46JcC<$JcF^/J,~> -#in9Rs)"@hjludE!Us(f*"5s=s'2Za*#r;Ts6`DV*!'XArr3#i.f95Hkqi#4$2ktGs.=c>s!c4l -*!(ffp&>*eKVIo7!Q/(<rrCjQ*!EJBm4518*!<VHr42bG&A7u":Z[6L*5hd.*"`Z#*8gPk!Q/1@ -rrsVGs/-hF]Dhg?\f;.l*$"qWWG$>m*$OSArs>H^rr<#^9E1*5p\t46JcC<$JcF^/J,~> -#in9Rs)"@hjludE!Us(f*"5s=s'2Za*#r;Ts6`DV*!'XArr3#i.f95Hkqi#4$2ktGs.=c>s!c4l -*!(ffp&>*eKVIo7!Q/(<rrCjQ*!EJBm4518*!<VHr42bG&A7u":Z[6L*5hd.*"`Z#*8gPk!Q/1@ -rrsVGs/-hF]Dhg?\f;.l*$"qWWG$>m*$OSArs>H^rr<#^9E1*5p\t46JcC<$JcF^/J,~> -#in9Rs(s&?2H0VT!G&_>rsZf$s'-u6s8T->s&7&=rrJm@rr3#i.f95Hkqhu3#T`sFSpp_><^Zld -!I1F5rrJj?mJd4)=nhq!7G$o6ErQ(@4l><[rVm<akl8@2QiI(:nGiNVK_>?L_HQutn,FF,rrHT? -r;QeAV>^DuW>MT6s7dl/rs"REs8UEef)5OJ*J+6As+14/s*t~> -#in9Rs(s&?2H0VT!G&_>rsZf$s'-u6s8T->s&7&=rrJm@rr3#i.f95Hkqhu3#T`sFSpp_><^Zld -!I1F5rrJj?mJd4)=nhq!7G$o6ErQ(@4l><[rVm<akl8@2QiI(:nGiNVK_>?L_HQutn,FF,rrHT? -r;QeAV>^DuW>MT6s7dl/rs"REs8UEef)5OJ*J+6As+14/s*t~> -#in9Rs(s&?2H0VT!G&_>rsZf$s'-u6s8T->s&7&=rrJm@rr3#i.f95Hkqhu3#T`sFSpp_><^Zld -!I1F5rrJj?mJd4)=nhq!7G$o6ErQ(@4l><[rVm<akl8@2QiI(:nGiNVK_>?L_HQutn,FF,rrHT? -r;QeAV>^DuW>MT6s7dl/rs"REs8UEef)5OJ*J+6As+14/s*t~> -"lqsOs)#";rrJFMriH=Cs8Q??rr3,%EW8soriH3>HN*pFngaP:!U2E4rrciCl&)D8rrIA?pAY3[ -N2>qA!Q/(<rrDWgXTL6.m4eM="$PQ&3;rj[2<b(S?N0s.E:s82R=t85#/XRDCU3s\rVln-]);R/ -GGY9<!NC/>rrMm?nG`]SNW9#h7m6eM!$-XjJcC<$iW"E~> -"lqsOs)#";rrJFMriH=Cs8Q??rr3,%EW8soriH3>HN*pFngaP:!U2E4rrciCl&)D8rrIA?pAY3[ -N2>qA!Q/(<rrDWgXTL6.m4eM="$PQ&3;rj[2<b(S?N0s.E:s82R=t85#/XRDCU3s\rVln-]);R/ -GGY9<!NC/>rrMm?nG`]SNW9#h7m6eM!$-XjJcC<$iW"E~> -"lqsOs)#";rrJFMriH=Cs8Q??rr3,%EW8soriH3>HN*pFngaP:!U2E4rrciCl&)D8rrIA?pAY3[ -N2>qA!Q/(<rrDWgXTL6.m4eM="$PQ&3;rj[2<b(S?N0s.E:s82R=t85#/XRDCU3s\rVln-]);R/ -GGY9<!NC/>rrMm?nG`]SNW9#h7m6eM!$-XjJcC<$iW"E~> -"lqsOs)#":rrAAaD?P3us'3D>rrfBBs&2tsD?.$BrrM[?qu6]]1\C\Lp*RC[q>UJ?V"Xfh^Kp4+ -!Q/(=rrN"UrGD]YfBk9jrrYFAj>d,<"?#EC^0^[9!L\W6rs$>Ds(eq?*W?!=@_2L;!I(C=rrK*? -rr3#o,k1g7OH'8>!T-'<rr='js+13$s5F!.~> -"lqsOs)#":rrAAaD?P3us'3D>rrfBBs&2tsD?.$BrrM[?qu6]]1\C\Lp*RC[q>UJ?V"Xfh^Kp4+ -!Q/(=rrN"UrGD]YfBk9jrrYFAj>d,<"?#EC^0^[9!L\W6rs$>Ds(eq?*W?!=@_2L;!I(C=rrK*? -rr3#o,k1g7OH'8>!T-'<rr='js+13$s5F!.~> -"lqsOs)#":rrAAaD?P3us'3D>rrfBBs&2tsD?.$BrrM[?qu6]]1\C\Lp*RC[q>UJ?V"Xfh^Kp4+ -!Q/(=rrN"UrGD]YfBk9jrrYFAj>d,<"?#EC^0^[9!L\W6rs$>Ds(eq?*W?!=@_2L;!I(C=rrK*? -rr3#o,k1g7OH'8>!T-'<rr='js+13$s5F!.~> -"lqsOs)#";rrK1Rrr3,.C&_GSrr3,%EW8tZq>UKd.f95Hkqhu3"84(R@K-9-Mu!AP!I1F>rrV/% -Zi0n*m!?)+rrV55[Jp1,odBb="fictm1u>nrrYFAj>d,<"?#EC^0^[9"dt&Ds,$[Lrs$>D^O^an -*W?!=@_2L;#C!$Es+UKPF8l1?pa#A/!K`<?rrM"?rVlnZNIh+\s+14/s*t~> -"lqsOs)#";rrK1Rrr3,.C&_GSrr3,%EW8tZq>UKd.f95Hkqhu3"84(R@K-9-Mu!AP!I1F>rrV/% -Zi0n*m!?)+rrV55[Jp1,odBb="fictm1u>nrrYFAj>d,<"?#EC^0^[9"dt&Ds,$[Lrs$>D^O^an -*W?!=@_2L;#C!$Es+UKPF8l1?pa#A/!K`<?rrM"?rVlnZNIh+\s+14/s*t~> -"lqsOs)#";rrK1Rrr3,.C&_GSrr3,%EW8tZq>UKd.f95Hkqhu3"84(R@K-9-Mu!AP!I1F>rrV/% -Zi0n*m!?)+rrV55[Jp1,odBb="fictm1u>nrrYFAj>d,<"?#EC^0^[9"dt&Ds,$[Lrs$>D^O^an -*W?!=@_2L;#C!$Es+UKPF8l1?pa#A/!K`<?rrM"?rVlnZNIh+\s+14/s*t~> -"lqsOs(spt=Tb&ka`%/]"E;N`>f$F>"KQPB_eK*Q!1!Q`"F!iV=e#Ej"E@<P=ePKg"8nU*aSu2? -ZsEZ6!J95.rrU7AErH"=gd(0)!N()?rrN%dr`KD\/_BA3O8`8aPP"R6rE08hp]%s6qYp`RIT]gC -rV?I'lAL)E_.]YEs1_\\6!=!^rs-kl=]qs.WH8(=!Vdr0rrLc"r`K83]D_a10C=>jJcC<$iW"E~> -"lqsOs(spt=Tb&ka`%/]"E;N`>f$F>"KQPB_eK*Q!1!Q`"F!iV=e#Ej"E@<P=ePKg"8nU*aSu2? -ZsEZ6!J95.rrU7AErH"=gd(0)!N()?rrN%dr`KD\/_BA3O8`8aPP"R6rE08hp]%s6qYp`RIT]gC -rV?I'lAL)E_.]YEs1_\\6!=!^rs-kl=]qs.WH8(=!Vdr0rrLc"r`K83]D_a10C=>jJcC<$iW"E~> -"lqsOs(spt=Tb&ka`%/]"E;N`>f$F>"KQPB_eK*Q!1!Q`"F!iV=e#Ej"E@<P=ePKg"8nU*aSu2? -ZsEZ6!J95.rrU7AErH"=gd(0)!N()?rrN%dr`KD\/_BA3O8`8aPP"R6rE08hp]%s6qYp`RIT]gC -rV?I'lAL)E_.]YEs1_\\6!=!^rs-kl=]qs.WH8(=!Vdr0rrLc"r`K83]D_a10C=>jJcC<$iW"E~> -"QVjNs4mOh"53_Se,I2eeGoR$nG`FjleVU@ci1c]f`(mNc2PQ[gA_*PbPo?Yh"C[Jp<rm=!6+rS -!93tW!qO4arVlomdH^`5l@c>>rrDcl_?K,Np&!#$rk\d,s8VZg_?BH0s7"\:rr_/q_Y3a(#jL4G -s3:H@s6'?t!<)lr#1p`/s8VB?rr3#tb4#?1h>Y7kb5M>AGH(Ijs+13$s5F!.~> -"QVjNs4mOh"53_Se,I2eeGoR$nG`FjleVU@ci1c]f`(mNc2PQ[gA_*PbPo?Yh"C[Jp<rm=!6+rS -!93tW!qO4arVlomdH^`5l@c>>rrDcl_?K,Np&!#$rk\d,s8VZg_?BH0s7"\:rr_/q_Y3a(#jL4G -s3:H@s6'?t!<)lr#1p`/s8VB?rr3#tb4#?1h>Y7kb5M>AGH(Ijs+13$s5F!.~> -"QVjNs4mOh"53_Se,I2eeGoR$nG`FjleVU@ci1c]f`(mNc2PQ[gA_*PbPo?Yh"C[Jp<rm=!6+rS -!93tW!qO4arVlomdH^`5l@c>>rrDcl_?K,Np&!#$rk\d,s8VZg_?BH0s7"\:rr_/q_Y3a(#jL4G -s3:H@s6'?t!<)lr#1p`/s8VB?rr3#tb4#?1h>Y7kb5M>AGH(Ijs+13$s5F!.~> -!ouXLQ2^mSnWj+TkkTf0JcC<$i;\<~> -!ouXLQ2^mSnWj+TkkTf0JcC<$i;\<~> -!ouXLQ2^mSnWj+TkkTf0JcC<$i;\<~> -!ouXLJcC<$JcC<$K`?Q~> -!ouXLJcC<$JcC<$K`?Q~> -!ouXLJcC<$JcC<$K`?Q~> -!ouXLJcG]K!TNR]rr_0&bgEm!"4YQ=h#.0PdXhFLs/5mI~> -!ouXLJcG]K!TNR]rr_0&bgEm!"4YQ=h#.0PdXhFLs/5mI~> -!ouXLJcG]K!TNR]rr_0&bgEm!"4YQ=h#.0PdXhFLs/5mI~> -!ouXLrVll.r;Qf;o()e]\@V&,!kGPPr;QirYf6S@!PSC#rr_SkYi""2!o&"@rVm>df)Ne\s8V;e -g&M)arUBgji3;2?s1.k'"4H?3\b5t)>rthhrVluBmf'fsrr^ZQYd<0c!6"l@!6Y!7"1eHrci3qH -Bhpuir;Qe$_L_`<s/>sJ~> -!ouXLrVll.r;Qf;o()e]\@V&,!kGPPr;QirYf6S@!PSC#rr_SkYi""2!o&"@rVm>df)Ne\s8V;e -g&M)arUBgji3;2?s1.k'"4H?3\b5t)>rthhrVluBmf'fsrr^ZQYd<0c!6"l@!6Y!7"1eHrci3qH -Bhpuir;Qe$_L_`<s/>sJ~> -!ouXLrVll.r;Qf;o()e]\@V&,!kGPPr;QirYf6S@!PSC#rr_SkYi""2!o&"@rVm>df)Ne\s8V;e -g&M)arUBgji3;2?s1.k'"4H?3\b5t)>rthhrVluBmf'fsrr^ZQYd<0c!6"l@!6Y!7"1eHrci3qH -Bhpuir;Qe$_L_`<s/>sJ~> -!ouXLrr3"u(&\(5P`>P2rrICpm/I-7?T\2c!r?\qrVlmIoCDnbn;;!sKAbYe!T1]hrrJ(?r;RA& -HiH[ds6YVXJCjfHq>^KAch7;Bie2*#ir:%mrr[/AruLe4!F<J;rsMiIr#l%>c$X;BD5H.i!P<LR -CB8[tpAY48s0uV;rrLA?r;QfW3e@<^s/>sJ~> -!ouXLrr3"u(&\(5P`>P2rrICpm/I-7?T\2c!r?\qrVlmIoCDnbn;;!sKAbYe!T1]hrrJ(?r;RA& -HiH[ds6YVXJCjfHq>^KAch7;Bie2*#ir:%mrr[/AruLe4!F<J;rsMiIr#l%>c$X;BD5H.i!P<LR -CB8[tpAY48s0uV;rrLA?r;QfW3e@<^s/>sJ~> -!ouXLrr3"u(&\(5P`>P2rrICpm/I-7?T\2c!r?\qrVlmIoCDnbn;;!sKAbYe!T1]hrrJ(?r;RA& -HiH[ds6YVXJCjfHq>^KAch7;Bie2*#ir:%mrr[/AruLe4!F<J;rsMiIr#l%>c$X;BD5H.i!P<LR -CB8[tpAY48s0uV;rrLA?r;QfW3e@<^s/>sJ~> -#NS0Qs8SgH'Dqe1RtBDp!T?-5rrFS?o)AmK5l^js[GUubZ>0::!n)SAr;RA&HiH[ds3jR>_1$W_ -r;ZeYN:m2T\mk]II!C_Grr[/AruLe4!F<J;rsMiIr#l%>LR%o>oI8\t#.&^Es8U`apAY48s0uV; -rrLA?r;QigKVj>#JcD_LJ,~> -#NS0Qs8SgH'Dqe1RtBDp!T?-5rrFS?o)AmK5l^js[GUubZ>0::!n)SAr;RA&HiH[ds3jR>_1$W_ -r;ZeYN:m2T\mk]II!C_Grr[/AruLe4!F<J;rsMiIr#l%>LR%o>oI8\t#.&^Es8U`apAY48s0uV; -rrLA?r;QigKVj>#JcD_LJ,~> -#NS0Qs8SgH'Dqe1RtBDp!T?-5rrFS?o)AmK5l^js[GUubZ>0::!n)SAr;RA&HiH[ds3jR>_1$W_ -r;ZeYN:m2T\mk]II!C_Grr[/AruLe4!F<J;rsMiIr#l%>LR%o>oI8\t#.&^Es8U`apAY48s0uV; -rrLA?r;QigKVj>#JcD_LJ,~> -#NS0Qs.PG>*W#d:UNuP4!e+Bor;QbBr`fGnp&>'O48f*[Bj?GlrrFS4r`fH/rVlrS>c%E!#M_TE -s2a:$pAJ50_Z0XSs3"YPaT)6E>a;X#c2[h+rEKWWs79J\-<sg.rr3#!F8PtRh9c29n,Mjis8U'5 -o)IQO1br<@mf3=+rEKd&s5qB%s1G-(s3748r`fI&rr3)Dir?1SrrHE?qu72Dli+*fs+cm>s7@c? -s/j-:rrM&<rr3"Nm/?qjU<NXUs4*P;s,-e\!/pjV"@#[e>`o$c!.XuQ!1*T`"0V[da8Gr<btn6: -!QA,ks+13Ls*t~> -#NS0Qs.PG>*W#d:UNuP4!e+Bor;QbBr`fGnp&>'O48f*[Bj?GlrrFS4r`fH/rVlrS>c%E!#M_TE -s2a:$pAJ50_Z0XSs3"YPaT)6E>a;X#c2[h+rEKWWs79J\-<sg.rr3#!F8PtRh9c29n,Mjis8U'5 -o)IQO1br<@mf3=+rEKd&s5qB%s1G-(s3748r`fI&rr3)Dir?1SrrHE?qu72Dli+*fs+cm>s7@c? -s/j-:rrM&<rr3"Nm/?qjU<NXUs4*P;s,-e\!/pjV"@#[e>`o$c!.XuQ!1*T`"0V[da8Gr<btn6: -!QA,ks+13Ls*t~> -#NS0Qs.PG>*W#d:UNuP4!e+Bor;QbBr`fGnp&>'O48f*[Bj?GlrrFS4r`fH/rVlrS>c%E!#M_TE -s2a:$pAJ50_Z0XSs3"YPaT)6E>a;X#c2[h+rEKWWs79J\-<sg.rr3#!F8PtRh9c29n,Mjis8U'5 -o)IQO1br<@mf3=+rEKd&s5qB%s1G-(s3748r`fI&rr3)Dir?1SrrHE?qu72Dli+*fs+cm>s7@c? -s/j-:rrM&<rr3"Nm/?qjU<NXUs4*P;s,-e\!/pjV"@#[e>`o$c!.XuQ!1*T`"0V[da8Gr<btn6: -!QA,ks+13Ls*t~> -#NS0Qs4Zf=*W#d;ql"c5rrUHX,5qNBGcC\W!KEfHrrM(?r;Qi:U."t[!@^M;^B&_orr3&@!-8&< -!Sfs<rr>sq^CtP2s0b1+^V=R6rYN>5HbX4Is7Q?*#\)ch^P5S"^[V7&!NL5.rsA82Ch^Z!q#AcZ -rP&>0q#:E#)he4*!*/Ie!F<J5rs=H,5X7I]s8RS?rr3#E:B(7o4Q68=$#ZsH*WNf[s,<HH^BC!c -s+R-F^BBjes*pjD^B'Ldp&>';:](.m`E.WjJcD_LJ,~> -#NS0Qs4Zf=*W#d;ql"c5rrUHX,5qNBGcC\W!KEfHrrM(?r;Qi:U."t[!@^M;^B&_orr3&@!-8&< -!Sfs<rr>sq^CtP2s0b1+^V=R6rYN>5HbX4Is7Q?*#\)ch^P5S"^[V7&!NL5.rsA82Ch^Z!q#AcZ -rP&>0q#:E#)he4*!*/Ie!F<J5rs=H,5X7I]s8RS?rr3#E:B(7o4Q68=$#ZsH*WNf[s,<HH^BC!c -s+R-F^BBjes*pjD^B'Ldp&>';:](.m`E.WjJcD_LJ,~> -#NS0Qs4Zf=*W#d;ql"c5rrUHX,5qNBGcC\W!KEfHrrM(?r;Qi:U."t[!@^M;^B&_orr3&@!-8&< -!Sfs<rr>sq^CtP2s0b1+^V=R6rYN>5HbX4Is7Q?*#\)ch^P5S"^[V7&!NL5.rsA82Ch^Z!q#AcZ -rP&>0q#:E#)he4*!*/Ie!F<J5rs=H,5X7I]s8RS?rr3#E:B(7o4Q68=$#ZsH*WNf[s,<HH^BC!c -s+R-F^BBjes*pjD^B'Ldp&>';:](.m`E.WjJcD_LJ,~> -!ouXLrVlj<q>UKC9D/;cqBGn<!DCl?rrJR?pAY0P48]$YfgPf=!@cOtrrIA?rr3&og["k="?7J0 -,`Vg'!$2%<&RW6Okq)#ts&R/=+9/3^s%^l:rrkjCs8TB>qu6\sF6ii,OGs/<!Q&%=rrCsOrrTH1 -mf*4d*U`q.?,-(4%YoShgX7PNs6XR>s6`D>rr3!]iVic_DQj'\s1Me>A[h[="LVqC?,6I>"KZSB -<65(=!V5UMrrLA?qu6]9=+UC&s/H$K~> -!ouXLrVlj<q>UKC9D/;cqBGn<!DCl?rrJR?pAY0P48]$YfgPf=!@cOtrrIA?rr3&og["k="?7J0 -,`Vg'!$2%<&RW6Okq)#ts&R/=+9/3^s%^l:rrkjCs8TB>qu6\sF6ii,OGs/<!Q&%=rrCsOrrTH1 -mf*4d*U`q.?,-(4%YoShgX7PNs6XR>s6`D>rr3!]iVic_DQj'\s1Me>A[h[="LVqC?,6I>"KZSB -<65(=!V5UMrrLA?qu6]9=+UC&s/H$K~> -!ouXLrVlj<q>UKC9D/;cqBGn<!DCl?rrJR?pAY0P48]$YfgPf=!@cOtrrIA?rr3&og["k="?7J0 -,`Vg'!$2%<&RW6Okq)#ts&R/=+9/3^s%^l:rrkjCs8TB>qu6\sF6ii,OGs/<!Q&%=rrCsOrrTH1 -mf*4d*U`q.?,-(4%YoShgX7PNs6XR>s6`D>rr3!]iVic_DQj'\s1Me>A[h[="LVqC?,6I>"KZSB -<65(=!V5UMrrLA?qu6]9=+UC&s/H$K~> -!ouXLrVlj<qYpWR7/HTV!W"#=rrGd@rr3"fK(f3FiB-r:!SKm>rrFS?rr3"DV"jrmpsqY'qu6Tq -*W?!@?bQL(1]IC^qYgC8s/(#?:<rj^r>YtHfDklVCA\#3WcRM+!K`9=rrN(ur>Ygmq#:B"C]485 -*U`q.?,-(4"c&BCs7@c>rrd5Er7aR:rrG7@rVm1AYQ#XC]jLeN\,QC1[:oSG_#F?=XDn4+7iWLI -oD\j9:](.m`E.WjJcD_LJ,~> -!ouXLrVlj<qYpWR7/HTV!W"#=rrGd@rr3"fK(f3FiB-r:!SKm>rrFS?rr3"DV"jrmpsqY'qu6Tq -*W?!@?bQL(1]IC^qYgC8s/(#?:<rj^r>YtHfDklVCA\#3WcRM+!K`9=rrN(ur>Ygmq#:B"C]485 -*U`q.?,-(4"c&BCs7@c>rrd5Er7aR:rrG7@rVm1AYQ#XC]jLeN\,QC1[:oSG_#F?=XDn4+7iWLI -oD\j9:](.m`E.WjJcD_LJ,~> -!ouXLrVlj<qYpWR7/HTV!W"#=rrGd@rr3"fK(f3FiB-r:!SKm>rrFS?rr3"DV"jrmpsqY'qu6Tq -*W?!@?bQL(1]IC^qYgC8s/(#?:<rj^r>YtHfDklVCA\#3WcRM+!K`9=rrN(ur>Ygmq#:B"C]485 -*U`q.?,-(4"c&BCs7@c>rrd5Er7aR:rrG7@rVm1AYQ#XC]jLeN\,QC1[:oSG_#F?=XDn4+7iWLI -oD\j9:](.m`E.WjJcD_LJ,~> -!ouXLrVlj<qu6]J7e?W\qBGn<!DCl?rrJR?pAY0P48]$YfgPf=!@c+hrrIA?rr3&lcfG')!Sfs< -rr=)<rrc]Bs62?;rsAZHs/(#?:<rM[rr3Mf37n31Z>0F>gA1dK\p3N,rs+XEs8UUKoAKTI!$1k7 -!O6G=rr=)4rrV!%r;HWr?,-(4"c&BCs7@c=rrPU@*W5pJ4Q6.is8Qu?ruM-Q?iO]Hrr3,/B`DAR -rr3,&E;rnYrr3#ejn8WSbtn9;!rAp@r;Qincf'HTJcE"TJ,~> -!ouXLrVlj<qu6]J7e?W\qBGn<!DCl?rrJR?pAY0P48]$YfgPf=!@c+hrrIA?rr3&lcfG')!Sfs< -rr=)<rrc]Bs62?;rsAZHs/(#?:<rM[rr3Mf37n31Z>0F>gA1dK\p3N,rs+XEs8UUKoAKTI!$1k7 -!O6G=rr=)4rrV!%r;HWr?,-(4"c&BCs7@c=rrPU@*W5pJ4Q6.is8Qu?ruM-Q?iO]Hrr3,/B`DAR -rr3,&E;rnYrr3#ejn8WSbtn9;!rAp@r;Qincf'HTJcE"TJ,~> -!ouXLrVlj<qu6]J7e?W\qBGn<!DCl?rrJR?pAY0P48]$YfgPf=!@c+hrrIA?rr3&lcfG')!Sfs< -rr=)<rrc]Bs62?;rsAZHs/(#?:<rM[rr3Mf37n31Z>0F>gA1dK\p3N,rs+XEs8UUKoAKTI!$1k7 -!O6G=rr=)4rrV!%r;HWr?,-(4"c&BCs7@c=rrPU@*W5pJ4Q6.is8Qu?ruM-Q?iO]Hrr3,/B`DAR -rr3,&E;rnYrr3#ejn8WSbtn9;!rAp@r;Qincf'HTJcE"TJ,~> -&*-#Ys3n9Z'XG$js8U/Gp@S@j_ljo_cN!oqT`24tMQ$:L"M:6SZI]$T#e?<UZIo3W.8^#L!I_`^ -rrU7AErH"=gd(f;!+#Q/"EMoikqi8;$2u%H_4Ua&cM1ZDZ3k[gfDklmGI"Mcl2L\`N0*E+#I/ug -Z<@4XE;d'B>PS4!Z"s=<!$1b4!_EC^rVln(^\.U1X,+'eY%dk7!dpbLr;R:NVPeS:s-]%U'XF*e -s-&cJZN$0ks,EHGZ36;Zs+m3EZ2ouZp&>';:]14ni]?u:!nmW^JcC<$Z2]=~> -&*-#Ys3n9Z'XG$js8U/Gp@S@j_ljo_cN!oqT`24tMQ$:L"M:6SZI]$T#e?<UZIo3W.8^#L!I_`^ -rrU7AErH"=gd(f;!+#Q/"EMoikqi8;$2u%H_4Ua&cM1ZDZ3k[gfDklmGI"Mcl2L\`N0*E+#I/ug -Z<@4XE;d'B>PS4!Z"s=<!$1b4!_EC^rVln(^\.U1X,+'eY%dk7!dpbLr;R:NVPeS:s-]%U'XF*e -s-&cJZN$0ks,EHGZ36;Zs+m3EZ2ouZp&>';:]14ni]?u:!nmW^JcC<$Z2]=~> -&*-#Ys3n9Z'XG$js8U/Gp@S@j_ljo_cN!oqT`24tMQ$:L"M:6SZI]$T#e?<UZIo3W.8^#L!I_`^ -rrU7AErH"=gd(f;!+#Q/"EMoikqi8;$2u%H_4Ua&cM1ZDZ3k[gfDklmGI"Mcl2L\`N0*E+#I/ug -Z<@4XE;d'B>PS4!Z"s=<!$1b4!_EC^rVln(^\.U1X,+'eY%dk7!dpbLr;R:NVPeS:s-]%U'XF*e -s-&cJZN$0ks,EHGZ36;Zs+m3EZ2ouZp&>';:]14ni]?u:!nmW^JcC<$Z2]=~> -"QVjNs/Gp1"JPkqP32B9!/UVg!2'8j!/(8b!i5k1qj%?1s8RBDB`P=8rr@0?B`tdBs+0V;B`P[A -rrV%kXSr/"l]1oC!;HG+"NUQBnq[/C!WIHErr\VIs60Gr!l^>crr3)9Bkc?#rrTfah<b.GXD)D; -s8U7?B`Rf#rrLNGrVlkDp&>)W'(Pr#"'oo4T_ABfVeKj`qu6\Hq>:0mH?oJh"INm3Boi8n!0dCr -!0mK_!07%m!1Eid!/^\h!1roa"/_B0:]14nOc0,:!pOEmJcC<$Z2]=~> -"QVjNs/Gp1"JPkqP32B9!/UVg!2'8j!/(8b!i5k1qj%?1s8RBDB`P=8rr@0?B`tdBs+0V;B`P[A -rrV%kXSr/"l]1oC!;HG+"NUQBnq[/C!WIHErr\VIs60Gr!l^>crr3)9Bkc?#rrTfah<b.GXD)D; -s8U7?B`Rf#rrLNGrVlkDp&>)W'(Pr#"'oo4T_ABfVeKj`qu6\Hq>:0mH?oJh"INm3Boi8n!0dCr -!0mK_!07%m!1Eid!/^\h!1roa"/_B0:]14nOc0,:!pOEmJcC<$Z2]=~> -"QVjNs/Gp1"JPkqP32B9!/UVg!2'8j!/(8b!i5k1qj%?1s8RBDB`P=8rr@0?B`tdBs+0V;B`P[A -rrV%kXSr/"l]1oC!;HG+"NUQBnq[/C!WIHErr\VIs60Gr!l^>crr3)9Bkc?#rrTfah<b.GXD)D; -s8U7?B`Rf#rrLNGrVlkDp&>)W'(Pr#"'oo4T_ABfVeKj`qu6\Hq>:0mH?oJh"INm3Boi8n!0dCr -!0mK_!07%m!1Eid!/^\h!1roa"/_B0:]14nOc0,:!pOEmJcC<$Z2]=~> -!ouXLg&D&Xqh5$jXoAF5fYd^i/+NT<"0mQ6OPKj9*SgYsQ\C-or;Qb\JcC<$VuM8~> -!ouXLg&D&Xqh5$jXoAF5fYd^i/+NT<"0mQ6OPKj9*SgYsQ\C-or;Qb\JcC<$VuM8~> -!ouXLg&D&Xqh5$jXoAF5fYd^i/+NT<"0mQ6OPKj9*SgYsQ\C-or;Qb\JcC<$VuM8~> -!ouXLJcEIa!fWE@eGfLhJcC<$JcGNFJ,~> -!ouXLJcEIa!fWE@eGfLhJcC<$JcGNFJ,~> -!ouXLJcEIa!fWE@eGfLhJcC<$JcGNFJ,~> -!ouXLJcEIa!RUJfrrBugs+13$s7lVE~> -!ouXLJcEIa!RUJfrrBugs+13$s7lVE~> -!ouXLJcEIa!RUJfrrBugs+13$s7lVE~> -!ouXLrr3't-73*urrYk?->%i/VuHj>-71q,rrYb<->A&2JcDGDJ,~> -!ouXLrr3't-73*urrYk?->%i/VuHj>-71q,rrYb<->A&2JcDGDJ,~> -!ouXLrr3't-73*urrYk?->%i/VuHj>-71q,rrYb<->A&2JcDGDJ,~> -!ouXLrr3'UiUusDrs(%<p%\ReoM>H+"C_!'=r@2X",R!J62hi)Dt`u+!V%uF*!C]as#M+t*!3#; -5lDZ''<(^+!:'M&".8rb)Z1QV`W%.\*!$Vlq>UKpc2AUb[/Bt%Y5TCHaoDD<V]6\VqtU0lr3?2? -"47(m&H*RN*rl,4r>Ygpq#:BkQMhd'mem(cnKn27"SQdS*96en!V^p>*!%tfr;Qr]1&q:ID#PCZ -7.^HYq"=;#o`#1Qo$gS;s)5IHo)Ae=s1<%Frr_uf`D;'bJcDGDJ,~> -!ouXLrr3'UiUusDrs(%<p%\ReoM>H+"C_!'=r@2X",R!J62hi)Dt`u+!V%uF*!C]as#M+t*!3#; -5lDZ''<(^+!:'M&".8rb)Z1QV`W%.\*!$Vlq>UKpc2AUb[/Bt%Y5TCHaoDD<V]6\VqtU0lr3?2? -"47(m&H*RN*rl,4r>Ygpq#:BkQMhd'mem(cnKn27"SQdS*96en!V^p>*!%tfr;Qr]1&q:ID#PCZ -7.^HYq"=;#o`#1Qo$gS;s)5IHo)Ae=s1<%Frr_uf`D;'bJcDGDJ,~> -!ouXLrr3'UiUusDrs(%<p%\ReoM>H+"C_!'=r@2X",R!J62hi)Dt`u+!V%uF*!C]as#M+t*!3#; -5lDZ''<(^+!:'M&".8rb)Z1QV`W%.\*!$Vlq>UKpc2AUb[/Bt%Y5TCHaoDD<V]6\VqtU0lr3?2? -"47(m&H*RN*rl,4r>Ygpq#:BkQMhd'mem(cnKn27"SQdS*96en!V^p>*!%tfr;Qr]1&q:ID#PCZ -7.^HYq"=;#o`#1Qo$gS;s)5IHo)Ae=s1<%Frr_uf`D;'bJcDGDJ,~> -!ouXLrr3!SlMLS^.eEW=!F3J?rrfBBs&7&:rrGd@rr3"fK)#?H3o^/="Gr?B1$eT6!pk9@q#:=7 -rVlt4[Jq9@rrV=od/!\BodB_<"@^r?ZYfX?#PtQEs8R)Bqt^6mdn0N<"!m]c;>^@o_HQp<!$1k7 -!O6G=rr=)<rsRa\$ig7pJueqO6eV87!K`<?rrM"?rVm)U2_"e,J"HZ>!R=I<rr=)3rrXeBdRsN= -!C,E2rrX;A\7GO;!R4Dks+13Ds*t~> -!ouXLrr3!SlMLS^.eEW=!F3J?rrfBBs&7&:rrGd@rr3"fK)#?H3o^/="Gr?B1$eT6!pk9@q#:=7 -rVlt4[Jq9@rrV=od/!\BodB_<"@^r?ZYfX?#PtQEs8R)Bqt^6mdn0N<"!m]c;>^@o_HQp<!$1k7 -!O6G=rr=)<rsRa\$ig7pJueqO6eV87!K`<?rrM"?rVm)U2_"e,J"HZ>!R=I<rr=)3rrXeBdRsN= -!C,E2rrX;A\7GO;!R4Dks+13Ds*t~> -!ouXLrr3!SlMLS^.eEW=!F3J?rrfBBs&7&:rrGd@rr3"fK)#?H3o^/="Gr?B1$eT6!pk9@q#:=7 -rVlt4[Jq9@rrV=od/!\BodB_<"@^r?ZYfX?#PtQEs8R)Bqt^6mdn0N<"!m]c;>^@o_HQp<!$1k7 -!O6G=rr=)<rsRa\$ig7pJueqO6eV87!K`<?rrM"?rVm)U2_"e,J"HZ>!R=I<rr=)3rrXeBdRsN= -!C,E2rrX;A\7GO;!R4Dks+13Ds*t~> -!ouXLrr3!SlMLS^.eEW=!F3J?rt2;Os&7%P[C+B]s*rr+s6>N>q#:@Uir8r\M3Irokl.sigACmN -QA>57!$2%<"D>.B*V99<!9="W!oJ@Aq#:Bf-iO&I7G$o\qu6of@a>#Rc!]u8rrLS?rVm!Gq9)Xm -rj;k&s2%t<rr=)7rrKB?rVlj<rr3DWCt]p0s1Eg>s8TE?q#:AVNW/qYh`^u=#KpH:.KBEtT)S`j -c;+<;!$1_3").AbJ,]HLGGkH=rrp[9]sY8prVlt,s36](rrLA?JcC<$U&TW~> -!ouXLrr3!SlMLS^.eEW=!F3J?rt2;Os&7%P[C+B]s*rr+s6>N>q#:@Uir8r\M3Irokl.sigACmN -QA>57!$2%<"D>.B*V99<!9="W!oJ@Aq#:Bf-iO&I7G$o\qu6of@a>#Rc!]u8rrLS?rVm!Gq9)Xm -rj;k&s2%t<rr=)7rrKB?rVlj<rr3DWCt]p0s1Eg>s8TE?q#:AVNW/qYh`^u=#KpH:.KBEtT)S`j -c;+<;!$1_3").AbJ,]HLGGkH=rrp[9]sY8prVlt,s36](rrLA?JcC<$U&TW~> -!ouXLrr3!SlMLS^.eEW=!F3J?rt2;Os&7%P[C+B]s*rr+s6>N>q#:@Uir8r\M3Irokl.sigACmN -QA>57!$2%<"D>.B*V99<!9="W!oJ@Aq#:Bf-iO&I7G$o\qu6of@a>#Rc!]u8rrLS?rVm!Gq9)Xm -rj;k&s2%t<rr=)7rrKB?rVlj<rr3DWCt]p0s1Eg>s8TE?q#:AVNW/qYh`^u=#KpH:.KBEtT)S`j -c;+<;!$1_3").AbJ,]HLGGkH=rrp[9]sY8prVlt,s36](rrLA?JcC<$U&TW~> -!ouXLrr3!SlMLS^.eEW=%U?jMs6409s&42YAnH;1rVlreMkg%H#s.)Gs4gM9s"TH2Ac[D5rVlrX -KX^^U!$2(="jT<>rtpjsAc[YJrr3)X=&duMrrMd?rr3/<.bF&Ai;T,MkPtS<GF=kBht[3Pdn0Q= -"g\s>b;"YKAcn:t_HQs=!N[(3rrKB?rr38#*WQ/eI*Va4rr3,/Kpe?Lp\u"jNW9%XL->S:_fb#3 -.KBEtT)\ibHqsV>!$1G+!ILR>rruGICM%1'an>Z7btiojJcDGDJ,~> -!ouXLrr3!SlMLS^.eEW=%U?jMs6409s&42YAnH;1rVlreMkg%H#s.)Gs4gM9s"TH2Ac[D5rVlrX -KX^^U!$2(="jT<>rtpjsAc[YJrr3)X=&duMrrMd?rr3/<.bF&Ai;T,MkPtS<GF=kBht[3Pdn0Q= -"g\s>b;"YKAcn:t_HQs=!N[(3rrKB?rr38#*WQ/eI*Va4rr3,/Kpe?Lp\u"jNW9%XL->S:_fb#3 -.KBEtT)\ibHqsV>!$1G+!ILR>rruGICM%1'an>Z7btiojJcDGDJ,~> -!ouXLrr3!SlMLS^.eEW=%U?jMs6409s&42YAnH;1rVlreMkg%H#s.)Gs4gM9s"TH2Ac[D5rVlrX -KX^^U!$2(="jT<>rtpjsAc[YJrr3)X=&duMrrMd?rr3/<.bF&Ai;T,MkPtS<GF=kBht[3Pdn0Q= -"g\s>b;"YKAcn:t_HQs=!N[(3rrKB?rr38#*WQ/eI*Va4rr3,/Kpe?Lp\u"jNW9%XL->S:_fb#3 -.KBEtT)\ibHqsV>!$1G+!ILR>rruGICM%1'an>Z7btiojJcDGDJ,~> -!ouXLrr3!SlMLS^.eEW=%^1>0I=>7;s/UbCs8Sj?rr3&pI\lc<$(cToI=F_Ms-e`BrrI\@rVlo) -C%hQ,NrFG1Il4Y6MO4>B!FNP>rrgr*Jq)eUrrMtIrd=s#,M2<!If=p)Idd<rItE9%jS8`Ul@Jq_ -"b@<^jat#ArrX;AiILoU!J(s[rrLf'rd>''*<4K;aoDA]rr3,:J:`B,p\t98J,Xj)E]sH>JD'tp -.KBF`J,XisBhnU+!$1D*!$2%<##o4*s8Sm?p&>';:P&Oss.TIC~> -!ouXLrr3!SlMLS^.eEW=%^1>0I=>7;s/UbCs8Sj?rr3&pI\lc<$(cToI=F_Ms-e`BrrI\@rVlo) -C%hQ,NrFG1Il4Y6MO4>B!FNP>rrgr*Jq)eUrrMtIrd=s#,M2<!If=p)Idd<rItE9%jS8`Ul@Jq_ -"b@<^jat#ArrX;AiILoU!J(s[rrLf'rd>''*<4K;aoDA]rr3,:J:`B,p\t98J,Xj)E]sH>JD'tp -.KBF`J,XisBhnU+!$1D*!$2%<##o4*s8Sm?p&>';:P&Oss.TIC~> -!ouXLrr3!SlMLS^.eEW=%^1>0I=>7;s/UbCs8Sj?rr3&pI\lc<$(cToI=F_Ms-e`BrrI\@rVlo) -C%hQ,NrFG1Il4Y6MO4>B!FNP>rrgr*Jq)eUrrMtIrd=s#,M2<!If=p)Idd<rItE9%jS8`Ul@Jq_ -"b@<^jat#ArrX;AiILoU!J(s[rrLf'rd>''*<4K;aoDA]rr3,:J:`B,p\t98J,Xj)E]sH>JD'tp -.KBF`J,XisBhnU+!$1D*!$2%<##o4*s8Sm?p&>';:P&Oss.TIC~> -!ouXLrr3!SlMLS^.eET<"h("oZ`A*CrrJm@rr3":Y4V_tWLf]sM3Ii:!J-d?rrV2$`qKE4rViAi -c"FH]rrHK?qu6[t`q]Q6nG]!_fgXN[*kVFO#sI26DSQ5O]l*B8rrD*YSc\%"q>:0o*WQ/qrgj/X -*V]R6f)D6Do,[k1/H0&a$[;WhJ(Xf$TR?M6(!<,>TAMg3LH/dO>n;nos21G"VTqs7rr=)*rrJR? -rr3!uaSu2?Uj;Y5!R4Dks+13Ds*t~> -!ouXLrr3!SlMLS^.eET<"h("oZ`A*CrrJm@rr3":Y4V_tWLf]sM3Ii:!J-d?rrV2$`qKE4rViAi -c"FH]rrHK?qu6[t`q]Q6nG]!_fgXN[*kVFO#sI26DSQ5O]l*B8rrD*YSc\%"q>:0o*WQ/qrgj/X -*V]R6f)D6Do,[k1/H0&a$[;WhJ(Xf$TR?M6(!<,>TAMg3LH/dO>n;nos21G"VTqs7rr=)*rrJR? -rr3!uaSu2?Uj;Y5!R4Dks+13Ds*t~> -!ouXLrr3!SlMLS^.eET<"h("oZ`A*CrrJm@rr3":Y4V_tWLf]sM3Ii:!J-d?rrV2$`qKE4rViAi -c"FH]rrHK?qu6[t`q]Q6nG]!_fgXN[*kVFO#sI26DSQ5O]l*B8rrD*YSc\%"q>:0o*WQ/qrgj/X -*V]R6f)D6Do,[k1/H0&a$[;WhJ(Xf$TR?M6(!<,>TAMg3LH/dO>n;nos21G"VTqs7rr=)*rrJR? -rr3!uaSu2?Uj;Y5!R4Dks+13Ds*t~> -!ouXLrr3!SlMLS^.eEH8!NU5;rro0Ds8Q]>n,EEFPPb@\JXl`>!ROO0rrHc?qu6\(^&.g1<lXb2 -"%Ci/0`D"R4l><[rVlmUkk"fT,l.?;!$1q9!$1Y1!Zh=)rON+H`;cKXrr3#g/G&lDh`_"srji0: -*mOT^!R=I<rr=)*rs&7lJcGaLaSu2?Uj;Y5!R4Dks+13Ds*t~> -!ouXLrr3!SlMLS^.eEH8!NU5;rro0Ds8Q]>n,EEFPPb@\JXl`>!ROO0rrHc?qu6\(^&.g1<lXb2 -"%Ci/0`D"R4l><[rVlmUkk"fT,l.?;!$1q9!$1Y1!Zh=)rON+H`;cKXrr3#g/G&lDh`_"srji0: -*mOT^!R=I<rr=)*rs&7lJcGaLaSu2?Uj;Y5!R4Dks+13Ds*t~> -!ouXLrr3!SlMLS^.eEH8!NU5;rro0Ds8Q]>n,EEFPPb@\JXl`>!ROO0rrHc?qu6\(^&.g1<lXb2 -"%Ci/0`D"R4l><[rVlmUkk"fT,l.?;!$1q9!$1Y1!Zh=)rON+H`;cKXrr3#g/G&lDh`_"srji0: -*mOT^!R=I<rr=)*rs&7lJcGaLaSu2?Uj;Y5!R4Dks+13Ds*t~> -!ouXLrr3!SlMLS^.eEK9"jpfCs)6?brro0Ds(Jn>qYpWa.;\k\"grXDs&/:ars"%Es8VR[iqr`W -cmaeIrre^NoD_,TrrZWAs$?V`!ER5>rrTPVV>C2pI&Hf-0`D"X4l?.@E<#rUZMXY$!gkFQqYp\* -^AcSurVlj<qu6[obPhGBHR[hJrrGL?qu6sCs8VS?WW2u_h>R?T?RbjKrrW-`i;ETUoI9\;!q&\@ -r;Qa;qu6[Y8Flcaa]&6><65(=!Mk#6rrLA?JcC<$U&TW~> -!ouXLrr3!SlMLS^.eEK9"jpfCs)6?brro0Ds(Jn>qYpWa.;\k\"grXDs&/:ars"%Es8VR[iqr`W -cmaeIrre^NoD_,TrrZWAs$?V`!ER5>rrTPVV>C2pI&Hf-0`D"X4l?.@E<#rUZMXY$!gkFQqYp\* -^AcSurVlj<qu6[obPhGBHR[hJrrGL?qu6sCs8VS?WW2u_h>R?T?RbjKrrW-`i;ETUoI9\;!q&\@ -r;Qa;qu6[Y8Flcaa]&6><65(=!Mk#6rrLA?JcC<$U&TW~> -!ouXLrr3!SlMLS^.eEK9"jpfCs)6?brro0Ds(Jn>qYpWa.;\k\"grXDs&/:ars"%Es8VR[iqr`W -cmaeIrre^NoD_,TrrZWAs$?V`!ER5>rrTPVV>C2pI&Hf-0`D"X4l?.@E<#rUZMXY$!gkFQqYp\* -^AcSurVlj<qu6[obPhGBHR[hJrrGL?qu6sCs8VS?WW2u_h>R?T?RbjKrrW-`i;ETUoI9\;!q&\@ -r;Qa;qu6[Y8Flcaa]&6><65(=!Mk#6rrLA?JcC<$U&TW~> -!ouXLrr3'Uh!jt7rrXb3nFZ_[#ZMc`9(r;bp.>6'"D[`3<Z:oV!0I3[!p^7krVm-\*#rScs8VaD -rYu)rqZ$T`48SsYb6i:hrrDop*!Cojs8V9\*!;u6s5<hq!NQ(srrT5,PlC[_h#88rTD\`gb5E:c -^]"31WuN+^r;HWsM[$WhrrBe4*!EqQs89%u*!>!rs0)DA!QtE?rrQs=fDbdMT`,o2f_YUJ/,oPO -q/RGpkPY>]9EP%hrrdon*$4bLrrMaLrr3+B*#pR'rr3)nnF.IqrrF>jo)AmkXapFYbPqMBWIagD -"7krt:P&Oss.TIC~> -!ouXLrr3'Uh!jt7rrXb3nFZ_[#ZMc`9(r;bp.>6'"D[`3<Z:oV!0I3[!p^7krVm-\*#rScs8VaD -rYu)rqZ$T`48SsYb6i:hrrDop*!Cojs8V9\*!;u6s5<hq!NQ(srrT5,PlC[_h#88rTD\`gb5E:c -^]"31WuN+^r;HWsM[$WhrrBe4*!EqQs89%u*!>!rs0)DA!QtE?rrQs=fDbdMT`,o2f_YUJ/,oPO -q/RGpkPY>]9EP%hrrdon*$4bLrrMaLrr3+B*#pR'rr3)nnF.IqrrF>jo)AmkXapFYbPqMBWIagD -"7krt:P&Oss.TIC~> -!ouXLrr3'Uh!jt7rrXb3nFZ_[#ZMc`9(r;bp.>6'"D[`3<Z:oV!0I3[!p^7krVm-\*#rScs8VaD -rYu)rqZ$T`48SsYb6i:hrrDop*!Cojs8V9\*!;u6s5<hq!NQ(srrT5,PlC[_h#88rTD\`gb5E:c -^]"31WuN+^r;HWsM[$WhrrBe4*!EqQs89%u*!>!rs0)DA!QtE?rrQs=fDbdMT`,o2f_YUJ/,oPO -q/RGpkPY>]9EP%hrrdon*$4bLrrMaLrr3+B*#pR'rr3)nnF.IqrrF>jo)AmkXapFYbPqMBWIagD -"7krt:P&Oss.TIC~> -!ouXLrr3(#.k>-.rrZ"H.r+/F!R+C#rrK-?j8T.9PiMcEB=?k#!CbW#rrFA?k5PO+.k=!arrMm@ -h>[RM.k>Ifs+13Ds*t~> -!ouXLrr3(#.k>-.rrZ"H.r+/F!R+C#rrK-?j8T.9PiMcEB=?k#!CbW#rrFA?k5PO+.k=!arrMm@ -h>[RM.k>Ifs+13Ds*t~> -!ouXLrr3(#.k>-.rrZ"H.r+/F!R+C#rrK-?j8T.9PiMcEB=?k#!CbW#rrFA?k5PO+.k=!arrMm@ -h>[RM.k>Ifs+13Ds*t~> -!ouXLf)GgITn2;)!q2;?jSo;DK<jS<!o8"AjSo;1B&htP!m4UAh#@Dd@Y+Q1s+14Gs*t~> -!ouXLf)GgITn2;)!q2;?jSo;DK<jS<!o8"AjSo;1B&htP!m4UAh#@Dd@Y+Q1s+14Gs*t~> -!ouXLf)GgITn2;)!q2;?jSo;DK<jS<!o8"AjSo;1B&htP!m4UAh#@Dd@Y+Q1s+14Gs*t~> -!ouXLf)GdAK&ck3h3[1%!QhG'rrKLBj8T.Pe`6Z1MsB's!-NkmJcC<$q#>j~> -!ouXLf)GdAK&ck3h3[1%!QhG'rrKLBj8T.Pe`6Z1MsB's!-NkmJcC<$q#>j~> -!ouXLf)GdAK&ck3h3[1%!QhG'rrKLBj8T.Pe`6Z1MsB's!-NkmJcC<$q#>j~> -!ouXLL&V1cdZa\2NkPGmp\oXB_RKOFg\u[&hYmHTaMn&V!Q<*2s-Nb9~> -!ouXLL&V1cdZa\2NkPGmp\oXB_RKOFg\u[&hYmHTaMn&V!Q<*2s-Nb9~> -!ouXLL&V1cdZa\2NkPGmp\oXB_RKOFg\u[&hYmHTaMn&V!Q<*2s-Nb9~> -"lqsOs8S>_@fVJerrJ2Co)A`3GfB[_Fl<9$^4G)<rrUAR]C#V#c!t8,r;Qi,A'k5T!S*h3rrSHq -B[m%_G,G3;",$1/N;ih\O(7qad\H:2"b:Cgs2))8rr[EDb,b79"R[8"SC%98#OmHI]"7qAr;L1+ -Xks'Xj8EHfg%PFR`if?(j8]/>S=DXRrVlrKIEMKf!muA@JcD,;J,~> -"lqsOs8S>_@fVJerrJ2Co)A`3GfB[_Fl<9$^4G)<rrUAR]C#V#c!t8,r;Qi,A'k5T!S*h3rrSHq -B[m%_G,G3;",$1/N;ih\O(7qad\H:2"b:Cgs2))8rr[EDb,b79"R[8"SC%98#OmHI]"7qAr;L1+ -Xks'Xj8EHfg%PFR`if?(j8]/>S=DXRrVlrKIEMKf!muA@JcD,;J,~> -"lqsOs8S>_@fVJerrJ2Co)A`3GfB[_Fl<9$^4G)<rrUAR]C#V#c!t8,r;Qi,A'k5T!S*h3rrSHq -B[m%_G,G3;",$1/N;ih\O(7qad\H:2"b:Cgs2))8rr[EDb,b79"R[8"SC%98#OmHI]"7qAr;L1+ -Xks'Xj8EHfg%PFR`if?(j8]/>S=DXRrVlrKIEMKf!muA@JcD,;J,~> -"lqsOs-ArL\-AC]s8U8Gp@J:b^VS_*!V0skrr=(trrJC?r;Qi]\DZcS"6d6nWVlbthRMhLrrK]? -nc&`YPdn,0hZ!V$j8/cV*rc*;$o@/%6N@(IR(-<![:oR>!FYftrs!qIZ=X'*JbK*G*WN'\pAY0] -0)PYRju37"NrT,`rjVu\[,Crbo3D7(!Ft<errf'Bs%:`;rrHK?r;Qe>W;HSqCpAQkR/_[~> -"lqsOs-ArL\-AC]s8U8Gp@J:b^VS_*!V0skrr=(trrJC?r;Qi]\DZcS"6d6nWVlbthRMhLrrK]? -nc&`YPdn,0hZ!V$j8/cV*rc*;$o@/%6N@(IR(-<![:oR>!FYftrs!qIZ=X'*JbK*G*WN'\pAY0] -0)PYRju37"NrT,`rjVu\[,Crbo3D7(!Ft<errf'Bs%:`;rrHK?r;Qe>W;HSqCpAQkR/_[~> -"lqsOs-ArL\-AC]s8U8Gp@J:b^VS_*!V0skrr=(trrJC?r;Qi]\DZcS"6d6nWVlbthRMhLrrK]? -nc&`YPdn,0hZ!V$j8/cV*rc*;$o@/%6N@(IR(-<![:oR>!FYftrs!qIZ=X'*JbK*G*WN'\pAY0] -0)PYRju37"NrT,`rjVu\[,Crbo3D7(!Ft<errf'Bs%:`;rrHK?r;Qe>W;HSqCpAQkR/_[~> -"lqsOs)#">rrKc?rr3#R6HoH1m4[i+!$1%u!l&a@j8T.$WV6>m\mk40"E(CBpa"_r!CYT;rrHE@ -qu7'\l.SK(s(/\>b:?iZrr3!CqXsjm9[NaJ.rFSFrrX;AW-.h6!U_T;rs.[Es,N->ruM+=!JZs! -rrW,+CB"56hYHpG"JU5B9%*Y;!FNP<rrV@pci!eEk$Q\js-`n;~> -"lqsOs)#">rrKc?rr3#R6HoH1m4[i+!$1%u!l&a@j8T.$WV6>m\mk40"E(CBpa"_r!CYT;rrHE@ -qu7'\l.SK(s(/\>b:?iZrr3!CqXsjm9[NaJ.rFSFrrX;AW-.h6!U_T;rs.[Es,N->ruM+=!JZs! -rrW,+CB"56hYHpG"JU5B9%*Y;!FNP<rrV@pci!eEk$Q\js-`n;~> -"lqsOs)#">rrKc?rr3#R6HoH1m4[i+!$1%u!l&a@j8T.$WV6>m\mk40"E(CBpa"_r!CYT;rrHE@ -qu7'\l.SK(s(/\>b:?iZrr3!CqXsjm9[NaJ.rFSFrrX;AW-.h6!U_T;rs.[Es,N->ruM+=!JZs! -rrW,+CB"56hYHpG"JU5B9%*Y;!FNP<rrV@pci!eEk$Q\js-`n;~> -$KOKTs7Z<hs3js?rVlrY6h^6R!_aRerVlol6N.r*EV]V6!p^A$rr3!gf_uj%J,91/m/R*]LB%9= -)?h*#PkY1Xn,44/S,<4+cMedcU\t/Xr>Z3Fr;JVFV]6\]rVt:D*!NkNs8TB>r;QlD*#=n?rrN)2 -r>Yj_rq??mF/f-<"0W[Z[f6:.\m:*j*!#='rrQ^Air/l^DQa$>o)JQDrYuV$o`(\EnkpG]p&C<n -*5aNjp&G&<rYu2-pA^*l#8eFOrVlm\iVWWkoD\^\s8Vfis8MN[s8R!W"<8Cns8VdbrYuMaq#C!a -s8DH\s8PmA,9.\Iq>UBrnc/I[pAY0]0)PYRrU^'aq#C@7rpg2AQiI(9rVm%8_#OG]>5\C+/,u]" -s'!eLdet-g\,H<I&cNaP^A\&Lr>Z*^rr<#op]'m`r;Qe)^&.g1=2t.;!D^pks-`n;~> -$KOKTs7Z<hs3js?rVlrY6h^6R!_aRerVlol6N.r*EV]V6!p^A$rr3!gf_uj%J,91/m/R*]LB%9= -)?h*#PkY1Xn,44/S,<4+cMedcU\t/Xr>Z3Fr;JVFV]6\]rVt:D*!NkNs8TB>r;QlD*#=n?rrN)2 -r>Yj_rq??mF/f-<"0W[Z[f6:.\m:*j*!#='rrQ^Air/l^DQa$>o)JQDrYuV$o`(\EnkpG]p&C<n -*5aNjp&G&<rYu2-pA^*l#8eFOrVlm\iVWWkoD\^\s8Vfis8MN[s8R!W"<8Cns8VdbrYuMaq#C!a -s8DH\s8PmA,9.\Iq>UBrnc/I[pAY0]0)PYRrU^'aq#C@7rpg2AQiI(9rVm%8_#OG]>5\C+/,u]" -s'!eLdet-g\,H<I&cNaP^A\&Lr>Z*^rr<#op]'m`r;Qe)^&.g1=2t.;!D^pks-`n;~> -$KOKTs7Z<hs3js?rVlrY6h^6R!_aRerVlol6N.r*EV]V6!p^A$rr3!gf_uj%J,91/m/R*]LB%9= -)?h*#PkY1Xn,44/S,<4+cMedcU\t/Xr>Z3Fr;JVFV]6\]rVt:D*!NkNs8TB>r;QlD*#=n?rrN)2 -r>Yj_rq??mF/f-<"0W[Z[f6:.\m:*j*!#='rrQ^Air/l^DQa$>o)JQDrYuV$o`(\EnkpG]p&C<n -*5aNjp&G&<rYu2-pA^*l#8eFOrVlm\iVWWkoD\^\s8Vfis8MN[s8R!W"<8Cns8VdbrYuMaq#C!a -s8DH\s8PmA,9.\Iq>UBrnc/I[pAY0]0)PYRrU^'aq#C@7rpg2AQiI(9rVm%8_#OG]>5\C+/,u]" -s'!eLdet-g\,H<I&cNaP^A\&Lr>Z*^rr<#op]'m`r;Qe)^&.g1=2t.;!D^pks-`n;~> -!ouXLrVloJ8c&Gfd7a04!W"#=rrGd@rr3"fK)YcOm3ulNrrG/,rr37\PQ*B's8S[>rr3#d0D,8E -*W?!@JAM6u:&b.n?bQ@:$Wb:Ii!OT+s%^l4-iO&KA)71bZ>079!K<->rrLn@rVlmLnFlk_F/f': -!Gf"?rrTbCPlC[_*W?!JF=H>Os7+ZGP1KO1s.Fc=rt4`Os8REaaoCN]s,`6?39B$\qu6]J7fNDg -7+hD:!Bf?,rrF>?rVlmtaSu2?ZB"_[#"'X1s8S:?n,EFV0(f/D)ZD/q'pnt#5McA>l>"B>s34C< -rs0\GVfi#8c1WL_rrYdBmP"P="$YT'2uWaW<pTGY!FNP;rrH3@qu6[lc@Q"`s*t~> -!ouXLrVloJ8c&Gfd7a04!W"#=rrGd@rr3"fK)YcOm3ulNrrG/,rr37\PQ*B's8S[>rr3#d0D,8E -*W?!@JAM6u:&b.n?bQ@:$Wb:Ii!OT+s%^l4-iO&KA)71bZ>079!K<->rrLn@rVlmLnFlk_F/f': -!Gf"?rrTbCPlC[_*W?!JF=H>Os7+ZGP1KO1s.Fc=rt4`Os8REaaoCN]s,`6?39B$\qu6]J7fNDg -7+hD:!Bf?,rrF>?rVlmtaSu2?ZB"_[#"'X1s8S:?n,EFV0(f/D)ZD/q'pnt#5McA>l>"B>s34C< -rs0\GVfi#8c1WL_rrYdBmP"P="$YT'2uWaW<pTGY!FNP;rrH3@qu6[lc@Q"`s*t~> -!ouXLrVloJ8c&Gfd7a04!W"#=rrGd@rr3"fK)YcOm3ulNrrG/,rr37\PQ*B's8S[>rr3#d0D,8E -*W?!@JAM6u:&b.n?bQ@:$Wb:Ii!OT+s%^l4-iO&KA)71bZ>079!K<->rrLn@rVlmLnFlk_F/f': -!Gf"?rrTbCPlC[_*W?!JF=H>Os7+ZGP1KO1s.Fc=rt4`Os8REaaoCN]s,`6?39B$\qu6]J7fNDg -7+hD:!Bf?,rrF>?rVlmtaSu2?ZB"_[#"'X1s8S:?n,EFV0(f/D)ZD/q'pnt#5McA>l>"B>s34C< -rs0\GVfi#8c1WL_rrYdBmP"P="$YT'2uWaW<pTGY!FNP;rrH3@qu6[lc@Q"`s*t~> -!ouXLrr3#?<Vl^sqPAT4rrMs?rVlmkdf07IRY(>7!BT6>rs4IFs"Wj>s.4]=rrML?o`"n3qYpO9 -rVlt,^&Rp,\cb7;s56$=rr[`)pfIF*!:9^b!O?J:rrJ(?rr3#S62gfa/F`B5!HY7;rrHo@rr3#1 -@f?<,*VfX@^dT:t7IU9VTR?b='_![Rs*LL?s6a\&OH'9"jki6$s4@7]!M)^ZrrGO?qu6[ZiTpLE -,5V9<!J7$E\cC4RrrGL?rr3"_MXUQGmOn/3!$2(=$&4fHs7.Z>s*^O=rrLA@r;R$Ds/1#>:X/S[ -rVlsif'Y3irrYIAlsB\&!9s+T!FNP;rrH3@qu6[lc@Q"`s*t~> -!ouXLrr3#?<Vl^sqPAT4rrMs?rVlmkdf07IRY(>7!BT6>rs4IFs"Wj>s.4]=rrML?o`"n3qYpO9 -rVlt,^&Rp,\cb7;s56$=rr[`)pfIF*!:9^b!O?J:rrJ(?rr3#S62gfa/F`B5!HY7;rrHo@rr3#1 -@f?<,*VfX@^dT:t7IU9VTR?b='_![Rs*LL?s6a\&OH'9"jki6$s4@7]!M)^ZrrGO?qu6[ZiTpLE -,5V9<!J7$E\cC4RrrGL?rr3"_MXUQGmOn/3!$2(=$&4fHs7.Z>s*^O=rrLA@r;R$Ds/1#>:X/S[ -rVlsif'Y3irrYIAlsB\&!9s+T!FNP;rrH3@qu6[lc@Q"`s*t~> -!ouXLrr3#?<Vl^sqPAT4rrMs?rVlmkdf07IRY(>7!BT6>rs4IFs"Wj>s.4]=rrML?o`"n3qYpO9 -rVlt,^&Rp,\cb7;s56$=rr[`)pfIF*!:9^b!O?J:rrJ(?rr3#S62gfa/F`B5!HY7;rrHo@rr3#1 -@f?<,*VfX@^dT:t7IU9VTR?b='_![Rs*LL?s6a\&OH'9"jki6$s4@7]!M)^ZrrGO?qu6[ZiTpLE -,5V9<!J7$E\cC4RrrGL?rr3"_MXUQGmOn/3!$2(=$&4fHs7.Z>s*^O=rrLA@r;R$Ds/1#>:X/S[ -rVlsif'Y3irrYIAlsB\&!9s+T!FNP;rrH3@qu6[lc@Q"`s*t~> -#NS0Qs8T`Ko(r@eT76G4!W"#=rrGd@rr3"fK)#?H3o^/=#`4cF1$el>Spp\=!UVQ4rr=)9rr=)< -rrZWAs-rsq"<kebi&po:!9a;h"c`$Os0?D9rrJ(?rr3#S62gfa/F`B5!HY7;rrHo@rr3#1@f?<, -*VTL6DQ`s<!MFi>rrqmCs8RG?r;R(bNW2!"h`_"eHN%=R;+CQb!CYT;rrG7@li-u0qY^?nKDo9[ -GKfj^!CPQ>rrJ=@n,EFV0(f/D*WH'FL6qr?pM7=mJ&M?d!R4I=rs0\GVfi#8c1WL_rrYdBmP"P= -"$YT7OoAbhbk(i8?bQ@:!E[;<rrGm?JcD/<J,~> -#NS0Qs8T`Ko(r@eT76G4!W"#=rrGd@rr3"fK)#?H3o^/=#`4cF1$el>Spp\=!UVQ4rr=)9rr=)< -rrZWAs-rsq"<kebi&po:!9a;h"c`$Os0?D9rrJ(?rr3#S62gfa/F`B5!HY7;rrHo@rr3#1@f?<, -*VTL6DQ`s<!MFi>rrqmCs8RG?r;R(bNW2!"h`_"eHN%=R;+CQb!CYT;rrG7@li-u0qY^?nKDo9[ -GKfj^!CPQ>rrJ=@n,EFV0(f/D*WH'FL6qr?pM7=mJ&M?d!R4I=rs0\GVfi#8c1WL_rrYdBmP"P= -"$YT7OoAbhbk(i8?bQ@:!E[;<rrGm?JcD/<J,~> -#NS0Qs8T`Ko(r@eT76G4!W"#=rrGd@rr3"fK)#?H3o^/=#`4cF1$el>Spp\=!UVQ4rr=)9rr=)< -rrZWAs-rsq"<kebi&po:!9a;h"c`$Os0?D9rrJ(?rr3#S62gfa/F`B5!HY7;rrHo@rr3#1@f?<, -*VTL6DQ`s<!MFi>rrqmCs8RG?r;R(bNW2!"h`_"eHN%=R;+CQb!CYT;rrG7@li-u0qY^?nKDo9[ -GKfj^!CPQ>rrJ=@n,EFV0(f/D*WH'FL6qr?pM7=mJ&M?d!R4I=rs0\GVfi#8c1WL_rrYdBmP"P= -"$YT7OoAbhbk(i8?bQ@:!E[;<rrGm?JcD/<J,~> -#38'Ps0mL>qYpS^L@bEGqBGn<!DCl?rrJR?rr3&lJuSbL!BP?%rs4IFs"Wj>s.4]=rrqdCs8Td\ -q#:=7rVlu-g]&;mrrZWAruM+="hQ>*s56$;rrMt]rVm'jfDkm'J&M6a!K<->rrLn@rVlmLnFlk_ -F/f':!Gf"?rrTbe_>aH7*W?!>Y(H0ArrI#?rVlnkI/a-Ln1=V>H_UH<%Zl\M39B$\s+cm>s7pM$ -rr3*hf`1-5rr3%hJc>$9#9s$Ef"(g]jSo/[Uj;b8!CPQ>rrJ=@pAY3]K!G:S!U_T4rr=)=rrIk@ -rr3)N8",&.rt!@Ns1?e\ruV3<FoP7^p*Tb<"%Ur10E(nQ52PB[rVlngoC`+a?bQC;!kWsAr;Qi- -J*-\/!jdLEJcDGDJ,~> -#38'Ps0mL>qYpS^L@bEGqBGn<!DCl?rrJR?rr3&lJuSbL!BP?%rs4IFs"Wj>s.4]=rrqdCs8Td\ -q#:=7rVlu-g]&;mrrZWAruM+="hQ>*s56$;rrMt]rVm'jfDkm'J&M6a!K<->rrLn@rVlmLnFlk_ -F/f':!Gf"?rrTbe_>aH7*W?!>Y(H0ArrI#?rVlnkI/a-Ln1=V>H_UH<%Zl\M39B$\s+cm>s7pM$ -rr3*hf`1-5rr3%hJc>$9#9s$Ef"(g]jSo/[Uj;b8!CPQ>rrJ=@pAY3]K!G:S!U_T4rr=)=rrIk@ -rr3)N8",&.rt!@Ns1?e\ruV3<FoP7^p*Tb<"%Ur10E(nQ52PB[rVlngoC`+a?bQC;!kWsAr;Qi- -J*-\/!jdLEJcDGDJ,~> -#38'Ps0mL>qYpS^L@bEGqBGn<!DCl?rrJR?rr3&lJuSbL!BP?%rs4IFs"Wj>s.4]=rrqdCs8Td\ -q#:=7rVlu-g]&;mrrZWAruM+="hQ>*s56$;rrMt]rVm'jfDkm'J&M6a!K<->rrLn@rVlmLnFlk_ -F/f':!Gf"?rrTbe_>aH7*W?!>Y(H0ArrI#?rVlnkI/a-Ln1=V>H_UH<%Zl\M39B$\s+cm>s7pM$ -rr3*hf`1-5rr3%hJc>$9#9s$Ef"(g]jSo/[Uj;b8!CPQ>rrJ=@pAY3]K!G:S!U_T4rr=)=rrIk@ -rr3)N8",&.rt!@Ns1?e\ruV3<FoP7^p*Tb<"%Ur10E(nQ52PB[rVlngoC`+a?bQC;!kWsAr;Qi- -J*-\/!jdLEJcDGDJ,~> -"lqsOs(t7(Ad+k-s,iH?o`#2OA.E4Bs8TQHral1Wc2R_Ekl=QZrrG0:ralakeGlXcAnH==_uKc3 -OCi*Ug\CdKUA\[+K]2qOrVlt,^&OGuAd*cC`rFsZr;QfpOo8kmMspZF!L8H=rs7Fm3'([/s7)'H -Ac\"hq#:rOAhHJ`n,NF0Ah-A`o`)KaJ,X$[TDeck=odLZrrI#?rVloOJGs-aBrh:6H_UH<%Zl\M -YOp^8s3H%(AnZ`brr3,.DJ!jtrVlm>rp9XiVe9Uc`rEYjral1UanYl:6eVJ=!L&E7rrUmS@/^*+ -mOn/3!2BI)!IiSurr_\IHHlEg&*o'aK&$D+P@d08Ar5jNOo8koMsgA%rFQ.ko(A%AAc[qbp&>&" -^&7m2FK#*:!Go%<rrQ[1eq*jps*t~> -"lqsOs(t7(Ad+k-s,iH?o`#2OA.E4Bs8TQHral1Wc2R_Ekl=QZrrG0:ralakeGlXcAnH==_uKc3 -OCi*Ug\CdKUA\[+K]2qOrVlt,^&OGuAd*cC`rFsZr;QfpOo8kmMspZF!L8H=rs7Fm3'([/s7)'H -Ac\"hq#:rOAhHJ`n,NF0Ah-A`o`)KaJ,X$[TDeck=odLZrrI#?rVloOJGs-aBrh:6H_UH<%Zl\M -YOp^8s3H%(AnZ`brr3,.DJ!jtrVlm>rp9XiVe9Uc`rEYjral1UanYl:6eVJ=!L&E7rrUmS@/^*+ -mOn/3!2BI)!IiSurr_\IHHlEg&*o'aK&$D+P@d08Ar5jNOo8koMsgA%rFQ.ko(A%AAc[qbp&>&" -^&7m2FK#*:!Go%<rrQ[1eq*jps*t~> -"lqsOs(t7(Ad+k-s,iH?o`#2OA.E4Bs8TQHral1Wc2R_Ekl=QZrrG0:ralakeGlXcAnH==_uKc3 -OCi*Ug\CdKUA\[+K]2qOrVlt,^&OGuAd*cC`rFsZr;QfpOo8kmMspZF!L8H=rs7Fm3'([/s7)'H -Ac\"hq#:rOAhHJ`n,NF0Ah-A`o`)KaJ,X$[TDeck=odLZrrI#?rVloOJGs-aBrh:6H_UH<%Zl\M -YOp^8s3H%(AnZ`brr3,.DJ!jtrVlm>rp9XiVe9Uc`rEYjral1UanYl:6eVJ=!L&E7rrUmS@/^*+ -mOn/3!2BI)!IiSurr_\IHHlEg&*o'aK&$D+P@d08Ar5jNOo8koMsgA%rFQ.ko(A%AAc[qbp&>&" -^&7m2FK#*:!Go%<rrQ[1eq*jps*t~> -"QVjNs4.%T"O[8Lb4G6)!6+rF!7:`F!5e`C!7LiG!r97Jrr3![ir6=cfDbdR]=#&og>`,3"nB". -h#DQp8cls2rVa,+iW&qlrVluIm/Qn\[0>C3mf3"#qu6ZgrNuXkrVlrWI)#[\!:Tlo"7Z?jm/=<m -o(DiO!rDr[rNub%s8V3Z[06@+ldFMd[/g.'rrUNSpAP!le`Zl1!8IL\!4i+/!SQQ3rrM$6rr3;u -a8c1h[C*O9ao25@^pV)XrrKi?li.$p[E\^N!6+rF!7:K?!Qk!5rrM'6pAY3dL1'u["RZ^k8u_Rb -!<2u*!8.5L!UA,1rrVDlh>[E[p9f'B[HRYjo_l0"li7"TrNuaps8VQd[/f[irrgP<8fYPCrrTHY -k5>5\X,-!:rrUWVo7?q8s*t~> -"QVjNs4.%T"O[8Lb4G6)!6+rF!7:`F!5e`C!7LiG!r97Jrr3![ir6=cfDbdR]=#&og>`,3"nB". -h#DQp8cls2rVa,+iW&qlrVluIm/Qn\[0>C3mf3"#qu6ZgrNuXkrVlrWI)#[\!:Tlo"7Z?jm/=<m -o(DiO!rDr[rNub%s8V3Z[06@+ldFMd[/g.'rrUNSpAP!le`Zl1!8IL\!4i+/!SQQ3rrM$6rr3;u -a8c1h[C*O9ao25@^pV)XrrKi?li.$p[E\^N!6+rF!7:K?!Qk!5rrM'6pAY3dL1'u["RZ^k8u_Rb -!<2u*!8.5L!UA,1rrVDlh>[E[p9f'B[HRYjo_l0"li7"TrNuaps8VQd[/f[irrgP<8fYPCrrTHY -k5>5\X,-!:rrUWVo7?q8s*t~> -"QVjNs4.%T"O[8Lb4G6)!6+rF!7:`F!5e`C!7LiG!r97Jrr3![ir6=cfDbdR]=#&og>`,3"nB". -h#DQp8cls2rVa,+iW&qlrVluIm/Qn\[0>C3mf3"#qu6ZgrNuXkrVlrWI)#[\!:Tlo"7Z?jm/=<m -o(DiO!rDr[rNub%s8V3Z[06@+ldFMd[/g.'rrUNSpAP!le`Zl1!8IL\!4i+/!SQQ3rrM$6rr3;u -a8c1h[C*O9ao25@^pV)XrrKi?li.$p[E\^N!6+rF!7:K?!Qk!5rrM'6pAY3dL1'u["RZ^k8u_Rb -!<2u*!8.5L!UA,1rrVDlh>[E[p9f'B[HRYjo_l0"li7"TrNuaps8VQd[/f[irrgP<8fYPCrrTHY -k5>5\X,-!:rrUWVo7?q8s*t~> -!ouXLd/O,-ip?[Fh##J!nC@O>kO7p?!;u]@!<0,#!7o'f!PDh=rrDurd/`FerrK$?h>[KGrm:`; -rVloalMLS^lKj*%QiDR~> -!ouXLd/O,-ip?[Fh##J!nC@O>kO7p?!;u]@!<0,#!7o'f!PDh=rrDurd/`FerrK$?h>[KGrm:`; -rVloalMLS^lKj*%QiDR~> -!ouXLd/O,-ip?[Fh##J!nC@O>kO7p?!;u]@!<0,#!7o'f!PDh=rrDurd/`FerrK$?h>[KGrm:`; -rVloalMLS^lKj*%QiDR~> -!ouXLd/O,5jFR>grrV%Uo[NmAWdB<sJcF[.J,~> -!ouXLd/O,5jFR>grrV%Uo[NmAWdB<sJcF[.J,~> -!ouXLd/O,5jFR>grrV%Uo[NmAWdB<sJcF[.J,~> -!ouXLJcC<$JcC<$K`?Q~> -!ouXLJcC<$JcC<$K`?Q~> -!ouXLJcC<$JcC<$K`?Q~> -#in9Rs8UK[]uSt/!65#O!82r'JcC<$JcE(VJ,~> -#in9Rs8UK[]uSt/!65#O!82r'JcC<$JcE(VJ,~> -#in9Rs8UK[]uSt/!65#O!82r'JcC<$JcE(VJ,~> -$04BSs2f=g?>-n,rrKZDra#VPe:IXNs+13$s0VfV~> -$04BSs2f=g?>-n,rrKZDra#VPe:IXNs+13$s0VfV~> -$04BSs2f=g?>-n,rrKZDra#VPe:IXNs+13$s0VfV~> -$04BSs)#"?s4Kcsrs#$Hqu?]9_L_`<s+13$s0VfV~> -$04BSs)#"?s4Kcsrs#$Hqu?]9_L_`<s+13$s0VfV~> -$04BSs)#"?s4Kcsrs#$Hqu?]9_L_`<s+13$s0VfV~> -%HKfWs)#"?s4Kd>s.n3ErrLQFrr3"HrVca&Rc"$ks2_GEs+C;b!2TVo"=7VnBrV+3!."QX!3UnQ -JcC<$JcE@^J,~> -%HKfWs)#"?s4Kd>s.n3ErrLQFrr3"HrVca&Rc"$ks2_GEs+C;b!2TVo"=7VnBrV+3!."QX!3UnQ -JcC<$JcE@^J,~> -%HKfWs)#"?s4Kd>s.n3ErrLQFrr3"HrVca&Rc"$ks2_GEs+C;b!2TVo"=7VnBrV+3!."QX!3UnQ -JcC<$JcE@^J,~> -%HKfWs(sVe9L2&Gs(&Y=rrKK@rr3!Bqu-O$;p"k[s.ao?JY<"K"H'/XH_gYI"Ga/[Ff55F!J@_0 -s+13$s+13_s*t~> -%HKfWs(sVe9L2&Gs(&Y=rrKK@rr3!Bqu-O$;p"k[s.ao?JY<"K"H'/XH_gYI"Ga/[Ff55F!J@_0 -s+13$s+13_s*t~> -%HKfWs(sVe9L2&Gs(&Y=rrKK@rr3!Bqu-O$;p"k[s.ao?JY<"K"H'/XH_gYI"Ga/[Ff55F!J@_0 -s+13$s+13_s*t~> -'')>\s)!eBc`fe:s5I;>s4Ui>rr3!Bqu-O$;p"k[s.ao?9@Eh>"IarB6JDG="Hn]C3o^/=!U8m# -s+13$s+13_s*t~> -'')>\s)!eBc`fe:s5I;>s4Ui>rr3!Bqu-O$;p"k[s.ao?9@Eh>"IarB6JDG="Hn]C3o^/=!U8m# -s+13$s+13_s*t~> -'')>\s)!eBc`fe:s5I;>s4Ui>rr3!Bqu-O$;p"k[s.ao?9@Eh>"IarB6JDG="Hn]C3o^/=!U8m# -s+13$s+13_s*t~> -$04BSs)#"?s4Kd=rrc$Brle7<rrF;?rVm1&and4]UO)r5df07LRY(Q+gA_*WP)KA)1)q9LJcC<$ -JcC<$])R9~> -$04BSs)#"?s4Kd=rrc$Brle7<rrF;?rVm1&and4]UO)r5df07LRY(Q+gA_*WP)KA)1)q9LJcC<$ -JcC<$])R9~> -$04BSs)#"?s4Kd=rrc$Brle7<rrF;?rVm1&and4]UO)r5df07LRY(Q+gA_*WP)KA)1)q9LJcC<$ -JcC<$])R9~> -$04BSs)#"?s4Kd<rrO\00)Y_[+oD#ss8Q$>qBGs7HN-Xdrr3+iK)\0krr3+aMuPitrr3#fn:CUj -s+13$s1JA^~> -$04BSs)#"?s4Kd<rrO\00)Y_[+oD#ss8Q$>qBGs7HN-Xdrr3+iK)\0krr3+aMuPitrr3#fn:CUj -s+13$s1JA^~> -$04BSs)#"?s4Kd<rrO\00)Y_[+oD#ss8Q$>qBGs7HN-Xdrr3+iK)\0krr3+aMuPitrr3#fn:CUj -s+13$s1JA^~> -$04BSs,i\`^T;JSrrQQ4@f66:=hUV\s8RPD]G\JHS,[`4rkASqU&T,7rkASmW;gY<rkAJeY(?V( -s+13$s1JA^~> -$04BSs,i\`^T;JSrrQQ4@f66:=hUV\s8RPD]G\JHS,[`4rkASqU&T,7rkASmW;gY<rkAJeY(?V( -s+13$s1JA^~> -$04BSs,i\`^T;JSrrQQ4@f66:=hUV\s8RPD]G\JHS,[`4rkASqU&T,7rkASmW;gY<rkAJeY(?V( -s+13$s1JA^~> -#in9Rs8S-b>]flC!,q`6!FC9SrrddS$s].urr@?D>QC;nrr@0?>QCMtrr?s9>QC[Qs+13$s+13^ -s*t~> -#in9Rs8S-b>]flC!,q`6!FC9SrrddS$s].urr@?D>QC;nrr@0?>QCMtrr?s9>QC[Qs+13$s+13^ -s*t~> -#in9Rs8S-b>]flC!,q`6!FC9SrrddS$s].urr@?D>QC;nrr@0?>QCMtrr?s9>QC[Qs+13$s+13^ -s*t~> -!ouXLk5PJY,(]cFs+13$s.TIC~> -!ouXLk5PJY,(]cFs+13$s.TIC~> -!ouXLk5PJY,(]cFs+13$s.TIC~> -!ouXLk5PJY,(]cFs+13$s.TIC~> -!ouXLk5PJY,(]cFs+13$s.TIC~> -!ouXLk5PJY,(]cFs+13$s.TIC~> -!ouXLk5PJ]\q0m4s+13$s.TIC~> -!ouXLk5PJ]\q0m4s+13$s.TIC~> -!ouXLk5PJ]\q0m4s+13$s.TIC~> -!ouXLJcC<$JcC<$K`?Q~> -!ouXLJcC<$JcC<$K`?Q~> -!ouXLJcC<$JcC<$K`?Q~> -!ouXLq#:A8^4H<8s+13$s,[21~> -!ouXLq#:A8^4H<8s+13$s,[21~> -!ouXLq#:A8^4H<8s+13$s,[21~> -!ouXLq#:A*\:O[2s+13$s,[21~> -!ouXLq#:A*\:O[2s+13$s,[21~> -!ouXLq#:A*\:O[2s+13$s,[21~> -"lqsOs8UXK_?A<es(&Y=rrMNAJcC<$JcC<$PlH7~> -"lqsOs8UXK_?A<es(&Y=rrMNAJcC<$JcC<$PlH7~> -"lqsOs8UXK_?A<es(&Y=rrMNAJcC<$JcC<$PlH7~> -"lqsOs3,K^=UArcs(&Y>q504DJcC<$JcC<$PlH7~> -"lqsOs3,K^=UArcs(&Y>q504DJcC<$JcC<$PlH7~> -"lqsOs3,K^=UArcs(&Y>q504DJcC<$JcC<$PlH7~> -"lqsOs)#">rs68Fs(&X1Lio;?s+13$s+136s*t~> -"lqsOs)#">rs68Fs(&X1Lio;?s+13$s+136s*t~> -"lqsOs)#">rs68Fs(&X1Lio;?s+13$s+136s*t~> -"lqsOs)#">rs$,Ds("oaR=YBhs+13$s,m>3~> -"lqsOs)#">rs$,Ds("oaR=YBhs+13$s,m>3~> -"lqsOs)#">rs$,Ds("oaR=YBhs+13$s,m>3~> -"lqsOs)#">rs$,Ds($Od?%N$,s+13$s,m>3~> -"lqsOs)#">rs$,Ds($Od?%N$,s+13$s,m>3~> -"lqsOs)#">rs$,Ds($Od?%N$,s+13$s,m>3~> -"lqsOs)#">rs68Fs(&Xf^c$1`s+13$s+136s*t~> -"lqsOs)#">rs68Fs(&Xf^c$1`s+13$s+136s*t~> -"lqsOs)#">rs68Fs(&Xf^c$1`s+13$s+136s*t~> -"lqsOs6s7l*!oL2s(8hArT1&)JcC<$JcC<$PlH7~> -"lqsOs6s7l*!oL2s(8hArT1&)JcC<$JcC<$PlH7~> -"lqsOs6s7l*!oL2s(8hArT1&)JcC<$JcC<$PlH7~> -!ouXLJcC<$JcC<$K`?Q~> -!ouXLJcC<$JcC<$K`?Q~> -!ouXLJcC<$JcC<$K`?Q~> -!ouXLJcC<$JcC<$K`?Q~> -!ouXLJcC<$JcC<$K`?Q~> -!ouXLJcC<$JcC<$K`?Q~> -!ouXLJcC<$JcC<$K`?Q~> -!ouXLJcC<$JcC<$K`?Q~> -!ouXLJcC<$JcC<$K`?Q~> -!ouXLJcC<$JcC<$K`?Q~> -!ouXLJcC<$JcC<$K`?Q~> -!ouXLJcC<$JcC<$K`?Q~> -"QVjNs/l3<"K)5!R-9,$JcC<$JcCi3J,~> -"QVjNs/l3<"K)5!R-9,$JcC<$JcCi3J,~> -"QVjNs/l3<"K)5!R-9,$JcC<$JcCi3J,~> -"QVjNs3LYE#(Bt[s2&7>JcC<$JcC<$OoKq~> -"QVjNs3LYE#(Bt[s2&7>JcC<$JcC<$OoKq~> -"QVjNs3LYE#(Bt[s2&7>JcC<$JcC<$OoKq~> -!ouXLr;QiX?JPP[!ROMks+13$s+134s*t~> -!ouXLr;QiX?JPP[!ROMks+13$s+134s*t~> -!ouXLr;QiX?JPP[!ROMks+13$s+134s*t~> -!ouXLrVlrn3U6PB!S9bks+13$s+135s*t~> -!ouXLrVlrn3U6PB!S9bks+13$s+135s*t~> -!ouXLrVlrn3U6PB!S9bks+13$s+135s*t~> -%%EndData -showpage -%%Trailer -end -%%EOF diff --git a/lib/stdlib/doc/src/zip.xml b/lib/stdlib/doc/src/zip.xml index cf0d581352..484bf0e080 100644 --- a/lib/stdlib/doc/src/zip.xml +++ b/lib/stdlib/doc/src/zip.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2006</year><year>2011</year> + <year>2006</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -217,7 +217,7 @@ <tag><c>{uncompress, <anno>What</anno>}</c></tag> <item> <p>Controls what types of files will be uncompressed. It is by - default set to <c>[".Z",".zip",".zoo",".arc",".lzh",".arj"]</c>. + default set to <c>[".Z", ".zip", ".zoo", ".arc", ".lzh", ".arj"]</c>. The following values of <c>What</c> are allowed:</p> <taglist> <tag><c>all</c></tag> @@ -355,7 +355,7 @@ {ok,{"dummy.zip", <<80,75,3,4,20,0,0,0,0,0,74,152,97,60,171,39,212,26,3,0, 0,0,3,0,0,...>>}} -> <input>catch zip:foldl(fun("foo", _, B, _) -> throw(B()); (_, _, _, Acc) -> Acc end, [], {Name, Bin}). </input> +> <input>catch zip:foldl(fun("foo", _, B, _) -> throw(B()); (_,_,_,Acc) -> Acc end, [], {Name, Bin}). </input> <<"FOO">> </pre> </desc> diff --git a/lib/stdlib/examples/erl_id_trans.erl b/lib/stdlib/examples/erl_id_trans.erl index 72e41d6473..51def8c8e1 100644 --- a/lib/stdlib/examples/erl_id_trans.erl +++ b/lib/stdlib/examples/erl_id_trans.erl @@ -153,7 +153,6 @@ pattern({record,Line,Name,Pfs0}) -> pattern({record_index,Line,Name,Field0}) -> Field1 = pattern(Field0), {record_index,Line,Name,Field1}; -%% record_field occurs in query expressions pattern({record_field,Line,Rec0,Name,Field0}) -> Rec1 = expr(Rec0), Field1 = expr(Field0), @@ -283,15 +282,6 @@ gexpr({call,Line,{remote,La,{atom,Lb,erlang},{atom,Lc,F}},As0}) -> true -> As1 = gexpr_list(As0), {call,Line,{remote,La,{atom,Lb,erlang},{atom,Lc,F}},As1} end; -% Unfortunately, writing calls as {M,F}(...) is also allowed. -gexpr({call,Line,{tuple,La,[{atom,Lb,erlang},{atom,Lc,F}]},As0}) -> - case erl_internal:guard_bif(F, length(As0)) or - erl_internal:arith_op(F, length(As0)) or - erl_internal:comp_op(F, length(As0)) or - erl_internal:bool_op(F, length(As0)) of - true -> As1 = gexpr_list(As0), - {call,Line,{tuple,La,[{atom,Lb,erlang},{atom,Lc,F}]},As1} - end; gexpr({bin,Line,Fs}) -> Fs2 = pattern_grp(Fs), {bin,Line,Fs2}; @@ -440,10 +430,6 @@ expr({'catch',Line,E0}) -> %% No new variables added. E1 = expr(E0), {'catch',Line,E1}; -expr({'query', Line, E0}) -> - %% lc expression - E = expr(E0), - {'query', Line, E}; expr({match,Line,P0,E0}) -> E1 = expr(E0), P1 = pattern(P0), diff --git a/lib/stdlib/src/Makefile b/lib/stdlib/src/Makefile index 14304824d3..f3387d669b 100644 --- a/lib/stdlib/src/Makefile +++ b/lib/stdlib/src/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1996-2012. All Rights Reserved. +# Copyright Ericsson AB 1996-2013. 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 @@ -149,7 +149,7 @@ APPUP_TARGET= $(EBIN)/$(APPUP_FILE) ifeq ($(NATIVE_LIBS_ENABLED),yes) ERL_COMPILE_FLAGS += +native endif -ERL_COMPILE_FLAGS += -I../include -I../../kernel/include +ERL_COMPILE_FLAGS += -I../include -I../../kernel/include -Werror # ---------------------------------------------------- # Targets @@ -171,27 +171,29 @@ primary_bootstrap_compiler: \ $(BOOTSTRAP_COMPILER)/ebin/erl_scan.beam \ $(BOOTSTRAP_COMPILER)/ebin/erl_parse.beam \ $(BOOTSTRAP_COMPILER)/ebin/erl_lint.beam \ + $(BOOTSTRAP_COMPILER)/ebin/io.beam \ $(BOOTSTRAP_COMPILER)/ebin/otp_internal.beam $(BOOTSTRAP_COMPILER)/ebin/erl_parse.beam: erl_parse.yrl - $(ERLC) -o $(BOOTSTRAP_COMPILER)/egen erl_parse.yrl - $(ERLC) -o $(BOOTSTRAP_COMPILER)/ebin $(BOOTSTRAP_COMPILER)/egen/erl_parse.erl + $(gen_verbose) + $(V_at)$(ERLC) -o $(BOOTSTRAP_COMPILER)/egen erl_parse.yrl + $(V_at)$(ERLC) -o $(BOOTSTRAP_COMPILER)/ebin $(BOOTSTRAP_COMPILER)/egen/erl_parse.erl $(BOOTSTRAP_TOP)/lib/stdlib/egen/erl_parse.erl: erl_parse.yrl - $(ERLC) $(YRL_FLAGS) -o$(BOOTSTRAP_TOP)/lib/stdlib/egen erl_parse.yrl + $(yecc_verbose)$(ERLC) $(YRL_FLAGS) -o$(BOOTSTRAP_TOP)/lib/stdlib/egen erl_parse.yrl $(BOOTSTRAP_COMPILER)/ebin/%.beam: %.erl - $(ERLC) -o $(BOOTSTRAP_COMPILER)/ebin $< + $(V_ERLC) -o $(BOOTSTRAP_COMPILER)/ebin $< # ---------------------------------------------------- # Special Build Targets # ---------------------------------------------------- $(APP_TARGET): $(APP_SRC) ../vsn.mk - sed -e 's;%VSN%;$(VSN);' $< > $@ + $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@ $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk - sed -e 's;%VSN%;$(VSN);' $< > $@ + $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@ # ---------------------------------------------------- # Release Target diff --git a/lib/stdlib/src/base64.erl b/lib/stdlib/src/base64.erl index 5d800e87b8..7bf281bd8a 100644 --- a/lib/stdlib/src/base64.erl +++ b/lib/stdlib/src/base64.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2011. All Rights Reserved. +%% Copyright Ericsson AB 2007-2013. 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 @@ -29,6 +29,7 @@ %%------------------------------------------------------------------------- -type ascii_string() :: [1..255]. +-type ascii_binary() :: binary(). %%------------------------------------------------------------------------- %% encode_to_string(ASCII) -> Base64String @@ -39,7 +40,7 @@ %%------------------------------------------------------------------------- -spec encode_to_string(Data) -> Base64String when - Data :: string() | binary(), + Data :: ascii_string() | ascii_binary(), Base64String :: ascii_string(). encode_to_string(Bin) when is_binary(Bin) -> @@ -56,15 +57,15 @@ encode_to_string(List) when is_list(List) -> %%------------------------------------------------------------------------- -spec encode(Data) -> Base64 when - Data :: string() | binary(), - Base64 :: binary(). + Data :: ascii_string() | ascii_binary(), + Base64 :: ascii_binary(). encode(Bin) when is_binary(Bin) -> encode_binary(Bin); encode(List) when is_list(List) -> list_to_binary(encode_l(List)). --spec encode_l(string()) -> ascii_string(). +-spec encode_l(ascii_string()) -> ascii_string(). encode_l([]) -> []; @@ -107,8 +108,8 @@ encode_binary(Bin) -> %%------------------------------------------------------------------------- -spec decode(Base64) -> Data when - Base64 :: string() | binary(), - Data :: binary(). + Base64 :: ascii_string() | ascii_binary(), + Data :: ascii_binary(). decode(Bin) when is_binary(Bin) -> decode_binary(<<>>, Bin); @@ -116,21 +117,21 @@ decode(List) when is_list(List) -> list_to_binary(decode_l(List)). -spec mime_decode(Base64) -> Data when - Base64 :: string() | binary(), - Data :: binary(). + Base64 :: ascii_string() | ascii_binary(), + Data :: ascii_binary(). mime_decode(Bin) when is_binary(Bin) -> mime_decode_binary(<<>>, Bin); mime_decode(List) when is_list(List) -> mime_decode(list_to_binary(List)). --spec decode_l(string()) -> string(). +-spec decode_l(ascii_string()) -> ascii_string(). decode_l(List) -> L = strip_spaces(List, []), decode(L, []). --spec mime_decode_l(string()) -> string(). +-spec mime_decode_l(ascii_string()) -> ascii_string(). mime_decode_l(List) -> L = strip_illegal(List, [], 0), @@ -148,8 +149,8 @@ mime_decode_l(List) -> %%------------------------------------------------------------------------- -spec decode_to_string(Base64) -> DataString when - Base64 :: string() | binary(), - DataString :: string(). + Base64 :: ascii_string() | ascii_binary(), + DataString :: ascii_string(). decode_to_string(Bin) when is_binary(Bin) -> decode_to_string(binary_to_list(Bin)); @@ -157,8 +158,8 @@ decode_to_string(List) when is_list(List) -> decode_l(List). -spec mime_decode_to_string(Base64) -> DataString when - Base64 :: string() | binary(), - DataString :: string(). + Base64 :: ascii_string() | ascii_binary(), + DataString :: ascii_string(). mime_decode_to_string(Bin) when is_binary(Bin) -> mime_decode_to_string(binary_to_list(Bin)); diff --git a/lib/stdlib/src/beam_lib.erl b/lib/stdlib/src/beam_lib.erl index e9a5e6831e..fe7e0f8e60 100644 --- a/lib/stdlib/src/beam_lib.erl +++ b/lib/stdlib/src/beam_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2011. All Rights Reserved. +%% Copyright Ericsson AB 2000-2013. 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 @@ -240,21 +240,21 @@ format_error({error, Error}) -> format_error({error, Module, Error}) -> Module:format_error(Error); format_error({unknown_chunk, File, ChunkName}) -> - io_lib:format("~p: Cannot find chunk ~p~n", [File, ChunkName]); + io_lib:format("~tp: Cannot find chunk ~p~n", [File, ChunkName]); format_error({invalid_chunk, File, ChunkId}) -> - io_lib:format("~p: Invalid contents of chunk ~p~n", [File, ChunkId]); + io_lib:format("~tp: Invalid contents of chunk ~p~n", [File, ChunkId]); format_error({not_a_beam_file, File}) -> - io_lib:format("~p: Not a BEAM file~n", [File]); + io_lib:format("~tp: Not a BEAM file~n", [File]); format_error({file_error, File, Reason}) -> - io_lib:format("~p: ~p~n", [File, file:format_error(Reason)]); + io_lib:format("~tp: ~tp~n", [File, file:format_error(Reason)]); format_error({missing_chunk, File, ChunkId}) -> - io_lib:format("~p: Not a BEAM file: no IFF \"~s\" chunk~n", + io_lib:format("~tp: Not a BEAM file: no IFF \"~s\" chunk~n", [File, ChunkId]); format_error({invalid_beam_file, File, Pos}) -> - io_lib:format("~p: Invalid format of BEAM file near byte number ~p~n", + io_lib:format("~tp: Invalid format of BEAM file near byte number ~p~n", [File, Pos]); format_error({chunk_too_big, File, ChunkId, Size, Len}) -> - io_lib:format("~p: Size of chunk \"~s\" is ~p bytes, " + io_lib:format("~tp: Size of chunk \"~s\" is ~p bytes, " "but only ~p bytes could be read~n", [File, ChunkId, Size, Len]); format_error({chunks_different, Id}) -> @@ -265,16 +265,16 @@ format_error({modules_different, Module1, Module2}) -> io_lib:format("Module names ~p and ~p differ in the two files~n", [Module1, Module2]); format_error({not_a_directory, Name}) -> - io_lib:format("~p: Not a directory~n", [Name]); + io_lib:format("~tp: Not a directory~n", [Name]); format_error({key_missing_or_invalid, File, abstract_code}) -> - io_lib:format("~p: Cannot decrypt abstract code because key is missing or invalid", + io_lib:format("~tp: Cannot decrypt abstract code because key is missing or invalid", [File]); format_error(badfun) -> "not a fun or the fun has the wrong arity"; format_error(exists) -> "a fun has already been installed"; format_error(E) -> - io_lib:format("~p~n", [E]). + io_lib:format("~tp~n", [E]). %% %% Exported functions for encrypted debug info. @@ -324,13 +324,13 @@ diff_directories(Dir1, Dir2) -> {OnlyDir1, OnlyDir2, Diff} = compare_dirs(Dir1, Dir2), diff_only(Dir1, OnlyDir1), diff_only(Dir2, OnlyDir2), - foreach(fun(D) -> io:format("** different: ~p~n", [D]) end, Diff), + foreach(fun(D) -> io:format("** different: ~tp~n", [D]) end, Diff), ok. diff_only(_Dir, []) -> ok; diff_only(Dir, Only) -> - io:format("Only in ~p: ~p~n", [Dir, Only]). + io:format("Only in ~tp: ~tp~n", [Dir, Only]). %% -> {OnlyInDir1, OnlyInDir2, Different} | throw(Error) compare_dirs(Dir1, Dir2) -> @@ -1030,11 +1030,11 @@ f_p_s(P, F) -> {error, enoent} -> {error, enoent}; {error, {Line, _Mod, _Term}=E} -> - error("file:path_script(~p,~p): error on line ~p: ~s~n", + error("file:path_script(~tp,~tp): error on line ~p: ~ts~n", [P, F, Line, file:format_error(E)]), ok; {error, E} when is_atom(E) -> - error("file:path_script(~p,~p): ~s~n", + error("file:path_script(~tp,~tp): ~ts~n", [P, F, file:format_error(E)]), ok; Other -> diff --git a/lib/stdlib/src/binary.erl b/lib/stdlib/src/binary.erl index cb1e12ae46..4850a59eb6 100644 --- a/lib/stdlib/src/binary.erl +++ b/lib/stdlib/src/binary.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2013. 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 @@ -18,29 +18,187 @@ %% -module(binary). %% -%% The following functions implemented as BIF's -%% binary:compile_pattern/1 -%% binary:match/{2,3} -%% binary:matches/{2,3} -%% binary:longest_common_prefix/1 -%% binary:longest_common_suffix/1 -%% binary:first/1 -%% binary:last/1 -%% binary:at/2 -%% binary:part/{2,3} -%% binary:bin_to_list/{1,2,3} -%% binary:list_to_bin/1 -%% binary:copy/{1,2} -%% binary:referenced_byte_size/1 -%% binary:decode_unsigned/{1,2} -%% - Not yet: -%% %% Implemented in this module: -export([split/2,split/3,replace/3,replace/4]). --opaque cp() :: tuple(). +-export_type([cp/0]). + +-opaque cp() :: {'am' | 'bm', binary()}. -type part() :: {Start :: non_neg_integer(), Length :: integer()}. +%%% BIFs. + +-export([at/2, bin_to_list/1, bin_to_list/2, bin_to_list/3, + compile_pattern/1, copy/1, copy/2, decode_unsigned/1, + decode_unsigned/2, encode_unsigned/1, encode_unsigned/2, + first/1, last/1, list_to_bin/1, longest_common_prefix/1, + longest_common_suffix/1, match/2, match/3, matches/2, + matches/3, part/2, part/3, referenced_byte_size/1]). + +-spec at(Subject, Pos) -> byte() when + Subject :: binary(), + Pos :: non_neg_integer(). + +at(_, _) -> + erlang:nif_error(undef). + +-spec bin_to_list(Subject) -> [byte()] when + Subject :: binary(). + +bin_to_list(_) -> + erlang:nif_error(undef). + +-spec bin_to_list(Subject, PosLen) -> [byte()] when + Subject :: binary(), + PosLen :: part(). + +bin_to_list(_, _) -> + erlang:nif_error(undef). + +-spec bin_to_list(Subject, Pos, Len) -> [byte()] when + Subject :: binary(), + Pos :: non_neg_integer(), + Len :: integer(). + +bin_to_list(_, _, _) -> + erlang:nif_error(undef). + +-spec compile_pattern(Pattern) -> cp() when + Pattern :: binary() | [binary()]. + +compile_pattern(_) -> + erlang:nif_error(undef). + +-spec copy(Subject) -> binary() when + Subject :: binary(). + +copy(_) -> + erlang:nif_error(undef). + +-spec copy(Subject, N) -> binary() when + Subject :: binary(), + N :: non_neg_integer(). + +copy(_, _) -> + erlang:nif_error(undef). + +-spec decode_unsigned(Subject) -> Unsigned when + Subject :: binary(), + Unsigned :: non_neg_integer(). + +decode_unsigned(_) -> + erlang:nif_error(undef). + +-spec decode_unsigned(Subject, Endianess) -> Unsigned when + Subject :: binary(), + Endianess :: big | little, + Unsigned :: non_neg_integer(). + +decode_unsigned(_, _) -> + erlang:nif_error(undef). + +-spec encode_unsigned(Unsigned) -> binary() when + Unsigned :: non_neg_integer(). + +encode_unsigned(_) -> + erlang:nif_error(undef). + +-spec encode_unsigned(Unsigned, Endianess) -> binary() when + Unsigned :: non_neg_integer(), + Endianess :: big | little. + +encode_unsigned(_, _) -> + erlang:nif_error(undef). + +-spec first(Subject) -> byte() when + Subject :: binary(). + +first(_) -> + erlang:nif_error(undef). + +-spec last(Subject) -> byte() when + Subject :: binary(). + +last(_) -> + erlang:nif_error(undef). + +-spec list_to_bin(ByteList) -> binary() when + ByteList :: iodata(). + +list_to_bin(_) -> + erlang:nif_error(undef). + +-spec longest_common_prefix(Binaries) -> non_neg_integer() when + Binaries :: [binary()]. + +longest_common_prefix(_) -> + erlang:nif_error(undef). + +-spec longest_common_suffix(Binaries) -> non_neg_integer() when + Binaries :: [binary()]. + +longest_common_suffix(_) -> + erlang:nif_error(undef). + +-spec match(Subject, Pattern) -> Found | nomatch when + Subject :: binary(), + Pattern :: binary() | [binary()] | cp(), + Found :: part(). + +match(_, _) -> + erlang:nif_error(undef). + +-spec match(Subject, Pattern, Options) -> Found | nomatch when + Subject :: binary(), + Pattern :: binary() | [binary()] | cp(), + Found :: part(), + Options :: [Option], + Option :: {scope, part()}. + +match(_, _, _) -> + erlang:nif_error(undef). + +-spec matches(Subject, Pattern) -> Found when + Subject :: binary(), + Pattern :: binary() | [binary()] | cp(), + Found :: [part()]. + +matches(_, _) -> + erlang:nif_error(undef). + +-spec matches(Subject, Pattern, Options) -> Found when + Subject :: binary(), + Pattern :: binary() | [binary()] | cp(), + Found :: [part()], + Options :: [Option], + Option :: {scope, part()}. + +matches(_, _, _) -> + erlang:nif_error(undef). + +-spec part(Subject, PosLen) -> binary() when + Subject :: binary(), + PosLen :: part(). + +part(_, _) -> + erlang:nif_error(undef). + +-spec part(Subject, Pos, Len) -> binary() when + Subject :: binary(), + Pos :: non_neg_integer(), + Len :: integer(). + +part(_, _, _) -> + erlang:nif_error(undef). + +-spec referenced_byte_size(Binary) -> non_neg_integer() when + Binary :: binary(). + +referenced_byte_size(_) -> + erlang:nif_error(undef). + +%%% End of BIFs. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% split %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/lib/stdlib/src/c.erl b/lib/stdlib/src/c.erl index a920921a5e..91d317489c 100644 --- a/lib/stdlib/src/c.erl +++ b/lib/stdlib/src/c.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2013. 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 @@ -116,13 +116,13 @@ machine_load(Mod, File, Opts) -> File2 = filename:join(Dir, filename:basename(File, ".erl")), case compile:output_generated(Opts) of true -> - Base = packages:last(Mod), + Base = atom_to_list(Mod), case filename:basename(File, ".erl") of Base -> code:purge(Mod), check_load(code:load_abs(File2,Mod), Mod); _OtherMod -> - format("** Module name '~p' does not match file name '~p' **~n", + format("** Module name '~p' does not match file name '~tp' **~n", [Mod,File]), {error, badfile} end; @@ -203,11 +203,11 @@ make_term(Str) -> case erl_parse:parse_term(Tokens ++ [{dot, 1}]) of {ok, Term} -> Term; {error, {_,_,Reason}} -> - io:format("~s: ~s~n", [Reason, Str]), + io:format("~ts: ~ts~n", [Reason, Str]), throw(error) end; {error, {_,_,Reason}, _} -> - io:format("~s: ~s~n", [Reason, Str]), + io:format("~ts: ~ts~n", [Reason, Str]), throw(error) end. @@ -475,11 +475,11 @@ f_p_e(P, F) -> {error, enoent} = Enoent -> Enoent; {error, E={Line, _Mod, _Term}} -> - error("file:path_eval(~p,~p): error on line ~p: ~s~n", + error("file:path_eval(~tp,~tp): error on line ~p: ~ts~n", [P, F, Line, file:format_error(E)]), ok; {error, E} -> - error("file:path_eval(~p,~p): ~s~n", + error("file:path_eval(~tp,~tp): ~ts~n", [P, F, file:format_error(E)]), ok; Other -> @@ -512,7 +512,7 @@ m(M) -> print_object_file(Mod) -> case code:is_loaded(Mod) of {file,File} -> - format("Object file: ~s\n", [File]); + format("Object file: ~ts\n", [File]); _ -> ignore end. @@ -588,7 +588,12 @@ month(12) -> "December". flush() -> receive X -> - format("Shell got ~p~n",[X]), + case lists:keyfind(encoding, 1, io:getopts()) of + {encoding,unicode} -> + format("Shell got ~tp~n",[X]); + _ -> + format("Shell got ~p~n",[X]) + end, flush() after 0 -> ok @@ -680,7 +685,7 @@ portformat(Name, Id, Cmd) -> pwd() -> case file:get_cwd() of {ok, Str} -> - ok = io:format("~ts\n", [fixup_one_bin(Str)]); + ok = io:format("~ts\n", [Str]); {error, _} -> ok = io:format("Cannot determine current directory\n") end. @@ -707,27 +712,11 @@ ls() -> ls(Dir) -> case file:list_dir(Dir) of {ok, Entries} -> - ls_print(sort(fixup_bin(Entries))); + ls_print(sort(Entries)); {error,_E} -> format("Invalid directory\n") end. -fixup_one_bin(X) when is_binary(X) -> - L = binary_to_list(X), - [ if - El > 127 -> - $?; - true -> - El - end || El <- L]; -fixup_one_bin(X) -> - X. -fixup_bin([H|T]) -> - [fixup_one_bin(H) | fixup_bin(T)]; -fixup_bin([]) -> - []. - - ls_print([]) -> ok; ls_print(L) -> Width = min([max(lengths(L, [])), 40]) + 5, diff --git a/lib/stdlib/src/dets.erl b/lib/stdlib/src/dets.erl index c0f9ce34b0..ecb509f4b5 100644 --- a/lib/stdlib/src/dets.erl +++ b/lib/stdlib/src/dets.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2013. 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 @@ -88,7 +88,8 @@ %% Not documented, or not ready for publication. -export([lookup_keys/2]). --export_type([tab_name/0]). +-export_type([bindings_cont/0, cont/0, object_cont/0, select_cont/0, + tab_name/0]). -compile({inline, [{einval,2},{badarg,2},{undefined,1}, {badarg_exit,2},{lookup_reply,2}]}). @@ -319,7 +320,7 @@ foldr(Fun, Acc, Tab) -> foldl(Fun, Acc, Tab) -> Ref = make_ref(), - do_traverse(Fun, Acc, Tab, Ref). + badarg(do_traverse(Fun, Acc, Tab, Ref), [Fun, Acc, Tab]). -spec from_ets(Name, EtsTab) -> 'ok' | {'error', Reason} when Name :: tab_name(), @@ -515,7 +516,7 @@ match(Tab, Pat) -> Reason :: term(). match(Tab, Pat, N) -> - badarg(init_chunk_match(Tab, Pat, bindings, N), [Tab, Pat, N]). + badarg(init_chunk_match(Tab, Pat, bindings, N, no_safe), [Tab, Pat, N]). -spec match(Continuation) -> {[Match], Continuation2} | '$end_of_table' | {'error', Reason} when @@ -525,7 +526,7 @@ match(Tab, Pat, N) -> Reason :: term(). match(State) when State#dets_cont.what =:= bindings -> - badarg(chunk_match(State), [State]); + badarg(chunk_match(State, no_safe), [State]); match(Term) -> erlang:error(badarg, [Term]). @@ -538,26 +539,26 @@ match_delete(Tab, Pat) -> badarg(match_delete(Tab, Pat, delete), [Tab, Pat]). match_delete(Tab, Pat, What) -> - safe_fixtable(Tab, true), case compile_match_spec(What, Pat) of {Spec, MP} -> - Proc = dets_server:get_pid(Tab), - R = req(Proc, {match_delete_init, MP, Spec}), - do_match_delete(Tab, Proc, R, What, 0); + case catch dets_server:get_pid(Tab) of + {'EXIT', _Reason} -> + badarg; + Proc -> + R = req(Proc, {match_delete_init, MP, Spec}), + do_match_delete(Proc, R, What, 0) + end; badarg -> badarg end. -do_match_delete(Tab, _Proc, {done, N1}, select, N) -> - safe_fixtable(Tab, false), +do_match_delete(_Proc, {done, N1}, select, N) -> N + N1; -do_match_delete(Tab, _Proc, {done, _N1}, _What, _N) -> - safe_fixtable(Tab, false), +do_match_delete(_Proc, {done, _N1}, _What, _N) -> ok; -do_match_delete(Tab, Proc, {cont, State, N1}, What, N) -> - do_match_delete(Tab, Proc, req(Proc, {match_delete, State}), What, N+N1); -do_match_delete(Tab, _Proc, Error, _What, _N) -> - safe_fixtable(Tab, false), +do_match_delete(Proc, {cont, State, N1}, What, N) -> + do_match_delete(Proc, req(Proc, {match_delete, State}), What, N+N1); +do_match_delete(_Proc, Error, _What, _N) -> Error. -spec match_object(Name, Pattern) -> Objects | {'error', Reason} when @@ -579,7 +580,7 @@ match_object(Tab, Pat) -> Reason :: term(). match_object(Tab, Pat, N) -> - badarg(init_chunk_match(Tab, Pat, object, N), [Tab, Pat, N]). + badarg(init_chunk_match(Tab, Pat, object, N, no_safe), [Tab, Pat, N]). -spec match_object(Continuation) -> {Objects, Continuation2} | '$end_of_table' | {'error', Reason} when @@ -589,7 +590,7 @@ match_object(Tab, Pat, N) -> Reason :: term(). match_object(State) when State#dets_cont.what =:= object -> - badarg(chunk_match(State), [State]); + badarg(chunk_match(State, no_safe), [State]); match_object(Term) -> erlang:error(badarg, [Term]). @@ -712,7 +713,7 @@ select(Tab, Pat) -> Reason :: term(). select(Tab, Pat, N) -> - badarg(init_chunk_match(Tab, Pat, select, N), [Tab, Pat, N]). + badarg(init_chunk_match(Tab, Pat, select, N, no_safe), [Tab, Pat, N]). -spec select(Continuation) -> {Selection, Continuation2} | '$end_of_table' | {'error', Reason} when @@ -722,7 +723,7 @@ select(Tab, Pat, N) -> Reason :: term(). select(State) when State#dets_cont.what =:= select -> - badarg(chunk_match(State), [State]); + badarg(chunk_match(State, no_safe), [State]); select(Term) -> erlang:error(badarg, [Term]). @@ -898,7 +899,7 @@ traverse(Tab, Fun) -> throw({Ref, Other}) end end, - do_traverse(TFun, [], Tab, Ref). + badarg(do_traverse(TFun, [], Tab, Ref), [Tab, Fun]). -spec update_counter(Name, Key, Increment) -> Result when Name :: tab_name(), @@ -929,20 +930,21 @@ where(Tab, Object) -> badarg(treq(Tab, {where, Object}), [Tab, Object]). do_traverse(Fun, Acc, Tab, Ref) -> - safe_fixtable(Tab, true), - Proc = dets_server:get_pid(Tab), - try - do_trav(Proc, Acc, Fun) - catch {Ref, Result} -> - Result - after - safe_fixtable(Tab, false) + case catch dets_server:get_pid(Tab) of + {'EXIT', _Reason} -> + badarg; + Proc -> + try + do_trav(Proc, Acc, Fun) + catch {Ref, Result} -> + Result + end end. do_trav(Proc, Acc, Fun) -> {Spec, MP} = compile_match_spec(object, '_'), %% MP not used - case req(Proc, {match, MP, Spec, default}) of + case req(Proc, {match, MP, Spec, default, safe}) of {cont, State} -> do_trav(State, Proc, Acc, Fun); Error -> @@ -952,7 +954,7 @@ do_trav(Proc, Acc, Fun) -> do_trav(#dets_cont{bin = eof}, _Proc, Acc, _Fun) -> Acc; do_trav(State, Proc, Acc, Fun) -> - case req(Proc, {match_init, State}) of + case req(Proc, {match_init, State, safe}) of {cont, {Bins, NewState}} -> do_trav_bins(NewState, Proc, Acc, Fun, lists:reverse(Bins)); Error -> @@ -972,44 +974,47 @@ do_trav_bins(State, Proc, Acc, Fun, [Bin | Bins]) -> end. safe_match(Tab, Pat, What) -> - safe_fixtable(Tab, true), - R = do_safe_match(init_chunk_match(Tab, Pat, What, default), []), - safe_fixtable(Tab, false), - R. + do_safe_match(init_chunk_match(Tab, Pat, What, default, safe), []). do_safe_match({error, Error}, _L) -> {error, Error}; do_safe_match({L, C}, LL) -> - do_safe_match(chunk_match(C), L++LL); + do_safe_match(chunk_match(C, safe), L++LL); do_safe_match('$end_of_table', L) -> L; do_safe_match(badarg, _L) -> badarg. %% What = object | bindings | select -init_chunk_match(Tab, Pat, What, N) when is_integer(N), N >= 0; - N =:= default -> +init_chunk_match(Tab, Pat, What, N, Safe) when is_integer(N), N >= 0; + N =:= default -> case compile_match_spec(What, Pat) of {Spec, MP} -> - Proc = dets_server:get_pid(Tab), - case req(Proc, {match, MP, Spec, N}) of - {done, L} -> - {L, #dets_cont{tab = Tab, proc = Proc, what = What, - bin = eof}}; - {cont, State} -> - chunk_match(State#dets_cont{what = What, tab = Tab, - proc = Proc}); - Error -> - Error + case catch dets_server:get_pid(Tab) of + {'EXIT', _Reason} -> + badarg; + Proc -> + case req(Proc, {match, MP, Spec, N, Safe}) of + {done, L} -> + {L, #dets_cont{tab = Tab, proc = Proc, + what = What, bin = eof}}; + {cont, State} -> + chunk_match(State#dets_cont{what = What, + tab = Tab, + proc = Proc}, + Safe); + Error -> + Error + end end; badarg -> badarg end; -init_chunk_match(_Tab, _Pat, _What, _) -> +init_chunk_match(_Tab, _Pat, _What, _N, _Safe) -> badarg. -chunk_match(#dets_cont{proc = Proc}=State) -> - case req(Proc, {match_init, State}) of +chunk_match(#dets_cont{proc = Proc}=State, Safe) -> + case req(Proc, {match_init, State, Safe}) of '$end_of_table'=Reply -> Reply; {cont, {Bins, NewState}} -> @@ -1024,7 +1029,7 @@ chunk_match(#dets_cont{proc = Proc}=State) -> badarg end; [] -> - chunk_match(NewState); + chunk_match(NewState, Safe); Terms -> {Terms, NewState} end; @@ -1301,7 +1306,7 @@ open_file_loop(Head, N) -> %% - wait 1 ms after each update. %% next is normally followed by lookup, but since lookup is also %% used when not traversing the table, it is not prioritized. - ?DETS_CALL(From, {match_init, _State} = Op) -> + ?DETS_CALL(From, {match_init, _State, _Safe} = Op) -> do_apply_op(Op, From, Head, N); ?DETS_CALL(From, {bchunk, _State} = Op) -> do_apply_op(Op, From, Head, N); @@ -1558,12 +1563,17 @@ apply_op(Op, From, Head, N) -> H2; {lookup_keys, _Keys} -> stream_op(Op, From, [], Head, N); - {match_init, State} -> - {H2, Res} = fmatch_init(Head, State), + {match_init, State, Safe} -> + {H1, Res} = fmatch_init(Head, State), + H2 = case Res of + {cont,_} -> H1; + _ when Safe =:= no_safe-> H1; + _ when Safe =:= safe -> do_safe_fixtable(H1, From, false) + end, From ! {self(), Res}, H2; - {match, MP, Spec, NObjs} -> - {H2, Res} = fmatch(Head, MP, Spec, NObjs), + {match, MP, Spec, NObjs, Safe} -> + {H2, Res} = fmatch(Head, MP, Spec, NObjs, Safe, From), From ! {self(), Res}, H2; {member, Key} when Head#head.version =:= 8 -> @@ -1577,11 +1587,15 @@ apply_op(Op, From, Head, N) -> From ! {self(), Res}, H2; {match_delete, State} when Head#head.update_mode =:= dirty -> - {H2, Res} = fmatch_delete(Head, State), + {H1, Res} = fmatch_delete(Head, State), + H2 = case Res of + {cont,_S,_N} -> H1; + _ -> do_safe_fixtable(H1, From, false) + end, From ! {self(), Res}, {N + 1, H2}; {match_delete_init, MP, Spec} when Head#head.update_mode =:= dirty -> - {H2, Res} = fmatch_delete_init(Head, MP, Spec), + {H2, Res} = fmatch_delete_init(Head, MP, Spec, From), From ! {self(), Res}, {N + 1, H2}; {safe_fixtable, Bool} -> @@ -2229,13 +2243,18 @@ fmatch_init(Head, C) -> end. %% -> {NewHead, Result} -fmatch(Head, MP, Spec, N) -> +fmatch(Head, MP, Spec, N, Safe, From) -> KeyPos = Head#head.keypos, case find_all_keys(Spec, KeyPos, []) of [] -> %% Complete match case catch write_cache(Head) of - {NewHead, []} -> + {Head1, []} -> + NewHead = + case Safe of + safe -> do_safe_fixtable(Head1, From, true); + no_safe -> Head1 + end, C0 = init_scan(NewHead, N), {NewHead, {cont, C0#dets_cont{match_program = MP}}}; {NewHead, _} = HeadError when is_record(NewHead, head) -> @@ -2300,12 +2319,12 @@ contains_variable(_) -> false. %% -> {NewHead, Res} -fmatch_delete_init(Head, MP, Spec) -> +fmatch_delete_init(Head, MP, Spec, From) -> KeyPos = Head#head.keypos, case catch case find_all_keys(Spec, KeyPos, []) of [] -> - do_fmatch_delete_var_keys(Head, MP, Spec); + do_fmatch_delete_var_keys(Head, MP, Spec, From); List -> Keys = lists:usort(List), do_fmatch_constant_keys(Head, Keys, MP) @@ -2336,7 +2355,7 @@ fmatch_delete(Head, C) -> end end. -do_fmatch_delete_var_keys(Head, _MP, ?PATTERN_TO_TRUE_MATCH_SPEC('_')) +do_fmatch_delete_var_keys(Head, _MP, ?PATTERN_TO_TRUE_MATCH_SPEC('_'), _From) when Head#head.fixed =:= false -> %% Handle the case where the file is emptied efficiently. %% Empty the cache just to get the number of objects right. @@ -2348,8 +2367,9 @@ do_fmatch_delete_var_keys(Head, _MP, ?PATTERN_TO_TRUE_MATCH_SPEC('_')) Reply -> Reply end; -do_fmatch_delete_var_keys(Head, MP, _Spec) -> - {NewHead, []} = write_cache(Head), +do_fmatch_delete_var_keys(Head, MP, _Spec, From) -> + Head1 = do_safe_fixtable(Head, From, true), + {NewHead, []} = write_cache(Head1), C0 = init_scan(NewHead, default), {NewHead, {cont, C0#dets_cont{match_program = MP}, 0}}. @@ -2484,7 +2504,7 @@ fopen2(Fname, Tab) -> end, case Do of {repair, Mess} -> - io:format(user, "dets: file ~p~s~n", [Fname, Mess]), + io:format(user, "dets: file ~tp~s~n", [Fname, Mess]), Version = default, case fsck(Fd, Tab, Fname, FH, default, default, Version) of ok -> @@ -2579,7 +2599,7 @@ fopen_existing_file(Tab, OpenArgs) -> _ when FH#fileheader.keypos =/= Kp -> throw({error, {keypos_mismatch, Fname}}); {compact, SourceHead} -> - io:format(user, "dets: file ~p is now compacted ...~n", [Fname]), + io:format(user, "dets: file ~tp is now compacted ...~n", [Fname]), {ok, NewSourceHead} = open_final(SourceHead, Fname, read, false, ?DEFAULT_CACHE, Tab, Debug), case catch compact(NewSourceHead) of @@ -2589,14 +2609,14 @@ fopen_existing_file(Tab, OpenArgs) -> _Err -> _ = file:close(Fd), dets_utils:stop_disk_map(), - io:format(user, "dets: compaction of file ~p failed, " + io:format(user, "dets: compaction of file ~tp failed, " "now repairing ...~n", [Fname]), {ok, Fd2, _FH} = read_file_header(Fname, Acc, Ram), do_repair(Fd2, Tab, Fname, FH, MinSlots, MaxSlots, Version, OpenArgs) end; {repair, Mess} -> - io:format(user, "dets: file ~p~s~n", [Fname, Mess]), + io:format(user, "dets: file ~tp~s~n", [Fname, Mess]), do_repair(Fd, Tab, Fname, FH, MinSlots, MaxSlots, Version, OpenArgs); _ when FH#fileheader.version =/= Version, Version =/= default -> diff --git a/lib/stdlib/src/dets_utils.erl b/lib/stdlib/src/dets_utils.erl index 5db2ad3049..7bbb34dd15 100644 --- a/lib/stdlib/src/dets_utils.erl +++ b/lib/stdlib/src/dets_utils.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2009. All Rights Reserved. +%% Copyright Ericsson AB 2001-2013. 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 @@ -395,7 +395,7 @@ corrupt_reason(Head, Reason0) -> corrupt(Head, Error) -> case get(verbose) of yes -> - error_logger:format("** dets: Corrupt table ~p: ~p\n", + error_logger:format("** dets: Corrupt table ~p: ~tp\n", [Head#head.name, Error]); _ -> ok end, diff --git a/lib/stdlib/src/dets_v8.erl b/lib/stdlib/src/dets_v8.erl index 3e962a1c8b..24d6e06ec8 100644 --- a/lib/stdlib/src/dets_v8.erl +++ b/lib/stdlib/src/dets_v8.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2011. All Rights Reserved. +%% Copyright Ericsson AB 2001-2013. 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 @@ -1492,7 +1492,7 @@ scan_next_allocated(Bin, From0, _To, <<From:32, To:32, L/binary>>, Ts, R) -> %% Read term from file at position Pos prterm(Head, Pos, ReadAhead) -> Res = dets_utils:pread(Head, Pos, ?OHDSZ, ReadAhead), - ?DEBUGF("file:pread(~p, ~p, ?) -> ~p~n", [Head#head.filename, Pos, Res]), + ?DEBUGF("file:pread(~tp, ~p, ?) -> ~p~n", [Head#head.filename, Pos, Res]), {ok, <<Next:32, Sz:32, _Status:32, Bin0/binary>>} = Res, ?DEBUGF("{Next, Sz} = ~p~n", [{Next, Sz}]), Bin = case byte_size(Bin0) of diff --git a/lib/stdlib/src/dets_v9.erl b/lib/stdlib/src/dets_v9.erl index f577b4410f..308f81c23b 100644 --- a/lib/stdlib/src/dets_v9.erl +++ b/lib/stdlib/src/dets_v9.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2011. All Rights Reserved. +%% Copyright Ericsson AB 2001-2013. 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 @@ -2662,7 +2662,7 @@ v_segment(H, SegNo, SegPos, SegSlot) -> {'EXIT', Reason} -> dets_utils:vformat("** dets: Corrupt or truncated dets file~n", []), - io:format("~nERROR ~p~n", [Reason]); + io:format("~nERROR ~tp~n", [Reason]); [] -> %% don't print empty buckets true; {Size, CollP, Objects} -> diff --git a/lib/stdlib/src/dict.erl b/lib/stdlib/src/dict.erl index 2e9eba4bfa..4f8d45dc8d 100644 --- a/lib/stdlib/src/dict.erl +++ b/lib/stdlib/src/dict.erl @@ -1,7 +1,8 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2011. All Rights Reserved. +%% Copyright Ericsson AB 2000-2012. 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 @@ -17,7 +18,7 @@ %% %CopyrightEnd% %% -%% We use the dynamic hashing techniques by Per-�ke Larsson as +%% We use the dynamic hashing techniques by Per-Åke Larsson as %% described in "The Design and Implementation of Dynamic Hashing for %% Sets and Tables in Icon" by Griswold and Townsend. Much of the %% terminology comes from that paper as well. diff --git a/lib/stdlib/src/edlin.erl b/lib/stdlib/src/edlin.erl index 026bd9038f..3192879f09 100644 --- a/lib/stdlib/src/edlin.erl +++ b/lib/stdlib/src/edlin.erl @@ -1,7 +1,8 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 @@ -21,10 +22,10 @@ %% A simple Emacs-like line editor. %% About Latin-1 characters: see the beginning of erl_scan.erl. --export([init/0,start/1,edit_line/2,prefix_arg/1]). +-export([init/0,start/1,start/2,edit_line/2,prefix_arg/1]). -export([erase_line/1,erase_inp/1,redraw_line/1]). -export([length_before/1,length_after/1,prompt/1]). --export([current_line/1]). +-export([current_line/1, current_chars/1]). %%-export([expand/1]). -export([edit_line1/2]). @@ -53,7 +54,12 @@ init() -> %% {undefined,Char,Rest,Cont,Requests} start(Pbs) -> - {more_chars,{line,Pbs,{[],[]},none},[{put_chars,unicode,Pbs}]}. + start(Pbs, none). + +%% Only two modes used: 'none' and 'search'. Other modes can be +%% handled inline through specific character handling. +start(Pbs, Mode) -> + {more_chars,{line,Pbs,{[],[]},Mode},[{put_chars,unicode,Pbs}]}. edit_line(Cs, {line,P,L,{blink,N}}) -> edit(Cs, P, L, none, [{move_rel,N}]); @@ -75,6 +81,10 @@ edit([C|Cs], P, {Bef,Aft}, Prefix, Rs0) -> edit(Cs, P, {Bef,Aft}, meta, Rs0); meta_left_sq_bracket -> edit(Cs, P, {Bef,Aft}, meta_left_sq_bracket, Rs0); + search_meta -> + edit(Cs, P, {Bef,Aft}, search_meta, Rs0); + search_meta_left_sq_bracket -> + edit(Cs, P, {Bef,Aft}, search_meta_left_sq_bracket, Rs0); ctlx -> edit(Cs, P, {Bef,Aft}, ctlx, Rs0); new_line -> @@ -114,6 +124,8 @@ edit([C|Cs], P, {Bef,Aft}, Prefix, Rs0) -> case do_op(Op, Bef, Aft, Rs0) of {blink,N,Line,Rs} -> edit(Cs, P, Line, {blink,N}, Rs); + {Line, Rs, Mode} -> % allow custom modes from do_op + edit(Cs, P, Line, Mode, Rs); {Line,Rs} -> edit(Cs, P, Line, none, Rs) end @@ -167,9 +179,15 @@ key_map($\^], none) -> auto_blink; key_map($\^X, none) -> ctlx; key_map($\^Y, none) -> yank; key_map($\e, none) -> meta; -key_map($), Prefix) when Prefix =/= meta -> {blink,$),$(}; -key_map($}, Prefix) when Prefix =/= meta -> {blink,$},${}; -key_map($], Prefix) when Prefix =/= meta -> {blink,$],$[}; +key_map($), Prefix) when Prefix =/= meta, + Prefix =/= search, + Prefix =/= search_meta -> {blink,$),$(}; +key_map($}, Prefix) when Prefix =/= meta, + Prefix =/= search, + Prefix =/= search_meta -> {blink,$},${}; +key_map($], Prefix) when Prefix =/= meta, + Prefix =/= search, + Prefix =/= search_meta -> {blink,$],$[}; key_map($B, meta) -> backward_word; key_map($D, meta) -> kill_word; key_map($F, meta) -> forward_word; @@ -187,6 +205,32 @@ key_map($D, meta_left_sq_bracket) -> backward_char; key_map($C, meta_left_sq_bracket) -> forward_char; key_map(C, none) when C >= $\s -> {insert,C}; +%% for search, we need smarter line handling and so +%% we cheat a bit on the dispatching, and allow to +%% return a mode. +key_map($\^H, search) -> {search, backward_delete_char}; +key_map($\177, search) -> {search, backward_delete_char}; +key_map($\^R, search) -> {search, skip_up}; +key_map($\^S, search) -> {search, skip_down}; +key_map($\n, search) -> {search, search_found}; +key_map($\r, search) -> {search, search_found}; +key_map($\^A, search) -> {search, search_quit}; +key_map($\^B, search) -> {search, search_quit}; +key_map($\^D, search) -> {search, search_quit}; +key_map($\^E, search) -> {search, search_quit}; +key_map($\^F, search) -> {search, search_quit}; +key_map($\t, search) -> {search, search_quit}; +key_map($\^L, search) -> {search, search_quit}; +key_map($\^T, search) -> {search, search_quit}; +key_map($\^U, search) -> {search, search_quit}; +key_map($\^], search) -> {search, search_quit}; +key_map($\^X, search) -> {search, search_quit}; +key_map($\^Y, search) -> {search, search_quit}; +key_map($\e, search) -> search_meta; +key_map($[, search_meta) -> search_meta_left_sq_bracket; +key_map(_, search_meta) -> {search, search_quit}; +key_map(_C, search_meta_left_sq_bracket) -> {search, search_quit}; +key_map(C, search) -> {insert_search,C}; key_map(C, _) -> {undefined,C}. %% do_op(Action, Before, After, Requests) @@ -195,6 +239,57 @@ do_op({insert,C}, Bef, [], Rs) -> {{[C|Bef],[]},[{put_chars, unicode,[C]}|Rs]}; do_op({insert,C}, Bef, Aft, Rs) -> {{[C|Bef],Aft},[{insert_chars, unicode, [C]}|Rs]}; +%% Search mode prompt always looks like (search)`$TERMS': $RESULT. +%% the {insert_search, _} handlings allow to share this implementation +%% correctly with group.erl. This module provides $TERMS, and group.erl +%% is in charge of providing $RESULT. +%% This require a bit of trickery. Because search disables moving around +%% on the line (left/right arrow keys and other shortcuts that just exit +%% search mode), we can use the Bef and Aft variables to hold each +%% part of the line. Bef takes charge of "(search)`$TERMS" and Aft +%% takes charge of "': $RESULT". +do_op({insert_search, C}, Bef, [], Rs) -> + Aft="': ", + {{[C|Bef],Aft}, + [{insert_chars, unicode, [C]++Aft}, {delete_chars,-3} | Rs], + search}; +do_op({insert_search, C}, Bef, Aft, Rs) -> + Offset= length(Aft), + NAft = "': ", + {{[C|Bef],NAft}, + [{insert_chars, unicode, [C]++NAft}, {delete_chars,-Offset} | Rs], + search}; +do_op({search, backward_delete_char}, [_|Bef], Aft, Rs) -> + Offset= length(Aft)+1, + NAft = "': ", + {{Bef,NAft}, + [{insert_chars, unicode, NAft}, {delete_chars,-Offset}|Rs], + search}; +do_op({search, backward_delete_char}, [], _Aft, Rs) -> + Aft="': ", + {{[],Aft}, Rs, search}; +do_op({search, skip_up}, Bef, Aft, Rs) -> + Offset= length(Aft), + NAft = "': ", + {{[$\^R|Bef],NAft}, % we insert ^R as a flag to whoever called us + [{insert_chars, unicode, NAft}, {delete_chars,-Offset}|Rs], + search}; +do_op({search, skip_down}, Bef, Aft, Rs) -> + Offset= length(Aft), + NAft = "': ", + {{[$\^S|Bef],NAft}, % we insert ^S as a flag to whoever called us + [{insert_chars, unicode, NAft}, {delete_chars,-Offset}|Rs], + search}; +do_op({search, search_found}, _Bef, Aft, Rs) -> + "': "++NAft = Aft, + {{[],NAft}, + [{put_chars, unicode, "\n"}, {move_rel,-length(Aft)} | Rs], + search_found}; +do_op({search, search_quit}, _Bef, Aft, Rs) -> + "': "++NAft = Aft, + {{[],NAft}, + [{put_chars, unicode, "\n"}, {move_rel,-length(Aft)} | Rs], + search_quit}; %% do blink after $$ do_op({blink,C,M}, Bef=[$$,$$|_], Aft, Rs) -> N = over_paren(Bef, C, M), @@ -317,9 +412,9 @@ over_non_word([], Stack, N) -> {[],Stack,N}. word_char(C) when C >= $A, C =< $Z -> true; -word_char(C) when C >= $�, C =< $�, C =/= $� -> true; +word_char(C) when C >= $À, C =< $Þ, C =/= $× -> true; word_char(C) when C >= $a, C =< $z -> true; -word_char(C) when C >= $�, C =< $�, C =/= $� -> true; +word_char(C) when C >= $ß, C =< $ÿ, C =/= $÷ -> true; word_char(C) when C >= $0, C =< $9 -> true; word_char(C) when C =:= $_ -> true; word_char(C) when C =:= $. -> true; % accept dot-separated names @@ -452,6 +547,9 @@ prompt({line,Pbs,_,_}) -> current_line({line,_,{Bef, Aft},_}) -> reverse(Bef, Aft ++ "\n"). +current_chars({line,_,{Bef,Aft},_}) -> + reverse(Bef, Aft). + %% %% expand(CurrentBefore) -> %% %% {yes,Expansion} | no %% %% Try to expand the word before as either a module name or a function diff --git a/lib/stdlib/src/epp.erl b/lib/stdlib/src/epp.erl index 2c8d84a9e1..0a1caa7178 100644 --- a/lib/stdlib/src/epp.erl +++ b/lib/stdlib/src/epp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2012. All Rights Reserved. +%% Copyright Ericsson AB 1996-2013. 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,13 +23,19 @@ -export([open/2,open/3,open/5,close/1,format_error/1]). -export([scan_erl_form/1,parse_erl_form/1,macro_defs/1]). -export([parse_file/1, parse_file/3]). +-export([default_encoding/0, encoding_to_string/1, + read_encoding_from_binary/1, read_encoding_from_binary/2, + set_encoding/1, read_encoding/1, read_encoding/2]). -export([interpret_file_attribute/1]). -export([normalize_typed_record_fields/1,restore_typed_record_fields/1]). %%------------------------------------------------------------------------ +-export_type([source_encoding/0]). + -type macros() :: [{atom(), term()}]. -type epp_handle() :: pid(). +-type source_encoding() :: latin1 | utf8. %% Epp state record. -record(epp, {file, %Current file @@ -213,6 +219,201 @@ parse_file(Epp) -> [{eof,Location}] end. +-define(DEFAULT_ENCODING, latin1). + +-spec default_encoding() -> source_encoding(). + +default_encoding() -> + ?DEFAULT_ENCODING. + +-spec encoding_to_string(Encoding) -> string() when + Encoding :: source_encoding(). + +encoding_to_string(latin1) -> "coding: latin-1"; +encoding_to_string(utf8) -> "coding: utf-8". + +-spec read_encoding(FileName) -> source_encoding() | none when + FileName :: file:name(). + +read_encoding(Name) -> + read_encoding(Name, []). + +-spec read_encoding(FileName, Options) -> source_encoding() | none when + FileName :: file:name(), + Options :: [Option], + Option :: {in_comment_only, boolean()}. + +read_encoding(Name, Options) -> + InComment = proplists:get_value(in_comment_only, Options, true), + case file:open(Name, [read]) of + {ok,File} -> + try read_encoding_from_file(File, InComment) + after ok = file:close(File) + end; + _Error -> + none + end. + +-spec set_encoding(File) -> source_encoding() | none when + File :: io:device(). % pid(); raw files don't work + +set_encoding(File) -> + Encoding = read_encoding_from_file(File, true), + Enc = case Encoding of + none -> default_encoding(); + Encoding -> Encoding + end, + ok = io:setopts(File, [{encoding, Enc}]), + Encoding. + +-spec read_encoding_from_binary(Binary) -> source_encoding() | none when + Binary :: binary(). + +-define(ENC_CHUNK, 32). +-define(N_ENC_CHUNK, 16). % a total of 512 bytes + +read_encoding_from_binary(Binary) -> + read_encoding_from_binary(Binary, []). + +-spec read_encoding_from_binary(Binary, Options) -> + source_encoding() | none when + Binary :: binary(), + Options :: [Option], + Option :: {in_comment_only, boolean()}. + +read_encoding_from_binary(Binary, Options) -> + InComment = proplists:get_value(in_comment_only, Options, true), + try + com_nl(Binary, fake_reader(0), 0, InComment) + catch + throw:no -> + none + end. + +fake_reader(N) -> + fun() when N =:= ?N_ENC_CHUNK -> + throw(no); + () -> + {<<>>, fake_reader(N+1)} + end. + +-spec read_encoding_from_file(File, InComment) -> source_encoding() | none when + File :: io:device(), + InComment :: boolean(). + +read_encoding_from_file(File, InComment) -> + {ok, Pos0} = file:position(File, cur), + Opts = io:getopts(File), + Encoding0 = lists:keyfind(encoding, 1, Opts), + Binary0 = lists:keyfind(binary, 1, Opts), + ok = io:setopts(File, [binary, {encoding, latin1}]), + try + {B, Fun} = (reader(File, 0))(), + com_nl(B, Fun, 0, InComment) + catch + throw:no -> + none + after + {ok, Pos0} = file:position(File, Pos0), + ok = io:setopts(File, [Binary0, Encoding0]) + end. + +reader(Fd, N) -> + fun() when N =:= ?N_ENC_CHUNK -> + throw(no); + () -> + case file:read(Fd, ?ENC_CHUNK) of + eof -> + {<<>>, reader(Fd, N+1)}; + {ok, Bin} -> + {Bin, reader(Fd, N+1)}; + {error, _} -> + throw(no) % ignore errors + end + end. + +com_nl(_, _, 2, _) -> + throw(no); +com_nl(B, Fun, N, false=Com) -> + com_c(B, Fun, N, Com); +com_nl(B, Fun, N, true=Com) -> + com(B, Fun, N, Com). + +com(<<"\n",B/binary>>, Fun, N, Com) -> + com_nl(B, Fun, N+1, Com); +com(<<"%", B/binary>>, Fun, N, Com) -> + com_c(B, Fun, N, Com); +com(<<_:1/unit:8,B/binary>>, Fun, N, Com) -> + com(B, Fun, N, Com); +com(<<>>, Fun, N, Com) -> + {B, Fun1} = Fun(), + com(B, Fun1, N, Com). + +com_c(<<"c",B/binary>>, Fun, N, Com) -> + com_oding(B, Fun, N, Com); +com_c(<<"\n",B/binary>>, Fun, N, Com) -> + com_nl(B, Fun, N+1, Com); +com_c(<<_:1/unit:8,B/binary>>, Fun, N, Com) -> + com_c(B, Fun, N, Com); +com_c(<<>>, Fun, N, Com) -> + {B, Fun1} = Fun(), + com_c(B, Fun1, N, Com). + +com_oding(<<"oding",B/binary>>, Fun, N, Com) -> + com_sep(B, Fun, N, Com); +com_oding(B, Fun, N, Com) when byte_size(B) >= length("oding") -> + com_c(B, Fun, N, Com); +com_oding(B, Fun, N, Com) -> + {B1, Fun1} = Fun(), + com_oding(list_to_binary([B, B1]), Fun1, N, Com). + +com_sep(<<":",B/binary>>, Fun, N, Com) -> + com_space(B, Fun, N, Com); +com_sep(<<"=",B/binary>>, Fun, N, Com) -> + com_space(B, Fun, N, Com); +com_sep(<<"\s",B/binary>>, Fun, N, Com) -> + com_sep(B, Fun, N, Com); +com_sep(<<>>, Fun, N, Com) -> + {B, Fun1} = Fun(), + com_sep(B, Fun1, N, Com); +com_sep(B, Fun, N, Com) -> + com_c(B, Fun, N, Com). + +com_space(<<"\s",B/binary>>, Fun, N, Com) -> + com_space(B, Fun, N, Com); +com_space(<<>>, Fun, N, Com) -> + {B, Fun1} = Fun(), + com_space(B, Fun1, N, Com); +com_space(B, Fun, N, _Com) -> + com_enc(B, Fun, N, [], []). + +com_enc(<<C:1/unit:8,B/binary>>, Fun, N, L, Ps) when C >= $a, C =< $z; + C >= $A, C =< $Z; + C >= $0, C =< $9 -> + com_enc(B, Fun, N, [C | L], Ps); +com_enc(<<>>, Fun, N, L, Ps) -> + case Fun() of + {<<>>, _} -> + com_enc_end([L | Ps]); + {B, Fun1} -> + com_enc(B, Fun1, N, L, Ps) + end; +com_enc(<<"-",B/binary>>, Fun, N, L, Ps) -> + com_enc(B, Fun, N, [], [L | Ps]); +com_enc(_B, _Fun, _N, L, Ps) -> + com_enc_end([L | Ps]). + +com_enc_end(Ps0) -> + Ps = lists:reverse([lists:reverse(string:to_lower(P)) || P <- Ps0]), + com_encoding(Ps). + +com_encoding(["latin","1"|_]) -> + latin1; +com_encoding(["utf","8"|_]) -> + utf8; +com_encoding(_) -> + throw(no). % Don't try any further + normalize_typed_record_fields([]) -> {typed, []}; normalize_typed_record_fields(Fields) -> @@ -266,14 +467,17 @@ init_server(Pid, Name, File, AtLocation, Path, Pdm, Pre) -> Ms0 = predef_macros(Name), case user_predef(Pdm, Ms0) of {ok,Ms1} -> - epp_reply(Pid, {ok,self()}), - %% ensure directory of current source file is first in path + _ = set_encoding(File), + epp_reply(Pid, {ok,self()}), + %% ensure directory of current source file is + %% first in path Path1 = [filename:dirname(Name) | Path], - St = #epp{file=File, location=AtLocation, delta=0, name=Name, - name2=Name, path=Path1, macs=Ms1, pre_opened = Pre}, - From = wait_request(St), - enter_file_reply(From, Name, AtLocation, AtLocation), - wait_req_scan(St); + St = #epp{file=File, location=AtLocation, delta=0, + name=Name, name2=Name, path=Path1, macs=Ms1, + pre_opened = Pre}, + From = wait_request(St), + enter_file_reply(From, Name, AtLocation, AtLocation), + wait_req_scan(St); {error,E} -> epp_reply(Pid, {error,E}) end. @@ -385,19 +589,20 @@ enter_file(NewName, Inc, From, St) -> %% enter_file2(File, FullName, From, EppState, AtLocation) -> EppState. %% Set epp to use this file and "enter" it. -enter_file2(NewF, Pname, From, St, AtLocation) -> +enter_file2(NewF, Pname, From, St0, AtLocation) -> Loc = start_loc(AtLocation), enter_file_reply(From, Pname, Loc, AtLocation), - Ms = dict:store({atom,'FILE'}, {none,[{string,Loc,Pname}]}, St#epp.macs), + Ms = dict:store({atom,'FILE'}, {none,[{string,Loc,Pname}]}, St0#epp.macs), %% update the head of the include path to be the directory of the new %% source file, so that an included file can always include other files %% relative to its current location (this is also how C does it); note %% that the directory of the parent source file (the previous head of %% the path) must be dropped, otherwise the path used within the current %% file will depend on the order of file inclusions in the parent files - Path = [filename:dirname(Pname) | tl(St#epp.path)], + Path = [filename:dirname(Pname) | tl(St0#epp.path)], + _ = set_encoding(NewF), #epp{file=NewF,location=Loc,name=Pname,delta=0, - sstk=[St|St#epp.sstk],path=Path,macs=Ms}. + sstk=[St0|St0#epp.sstk],path=Path,macs=Ms}. enter_file_reply(From, Name, Location, AtLocation) -> Attr = loc_attr(AtLocation), @@ -409,7 +614,7 @@ enter_file_reply(From, Name, Location, AtLocation) -> %% Flatten filename to a string. Must be a valid filename. -file_name([C | T]) when is_integer(C), C > 0, C =< 255 -> +file_name([C | T]) when is_integer(C), C > 0 -> [C | file_name(T)]; file_name([H|T]) -> file_name(H) ++ file_name(T); @@ -1048,8 +1253,6 @@ macro_arg([{'try',Lr}|Toks], E, Arg) -> macro_arg(Toks, ['end'|E], [{'try',Lr}|Arg]); macro_arg([{'cond',Lr}|Toks], E, Arg) -> macro_arg(Toks, ['end'|E], [{'cond',Lr}|Arg]); -macro_arg([{'query',Lr}|Toks], E, Arg) -> - macro_arg(Toks, ['end'|E], [{'query',Lr}|Arg]); macro_arg([{Rb,Lrb}|Toks], [Rb|E], Arg) -> %Found matching close macro_arg(Toks, E, [{Rb,Lrb}|Arg]); macro_arg([T|Toks], E, Arg) -> @@ -1094,6 +1297,7 @@ expand_arg([], Ts, L, Rest, Bs) -> %%% tokenized would yield the token list Ts. %% erl_scan:token_info(T, text) is not backward compatible with this. +%% Note that escaped characters will be replaced by themselves. token_src({dot, _}) -> "."; token_src({X, _}) when is_atom(X) -> @@ -1103,14 +1307,14 @@ token_src({var, _, X}) -> token_src({char,_,C}) -> io_lib:write_char(C); token_src({string, _, X}) -> - lists:flatten(io_lib:format("~p", [X])); + io_lib:write_string(X); token_src({_, _, X}) -> - lists:flatten(io_lib:format("~w", [X])). + io_lib:format("~w", [X]). stringify1([]) -> []; stringify1([T | Tokens]) -> - [io_lib:format(" ~s", [token_src(T)]) | stringify1(Tokens)]. + [io_lib:format(" ~ts", [token_src(T)]) | stringify1(Tokens)]. stringify(Ts, L) -> [$\s | S] = lists:flatten(stringify1(Ts)), diff --git a/lib/stdlib/src/erl_compile.erl b/lib/stdlib/src/erl_compile.erl index 81bec21a3f..8c3d59467b 100644 --- a/lib/stdlib/src/erl_compile.erl +++ b/lib/stdlib/src/erl_compile.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2012. All Rights Reserved. +%% Copyright Ericsson AB 1997-2013. 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 @@ -68,7 +68,7 @@ compile(List) -> {'EXIT', Pid, {compiler_result, Result}} -> Result; {'EXIT', Pid, Reason} -> - io:format("Runtime error: ~p~n", [Reason]), + io:format("Runtime error: ~tp~n", [Reason]), error end. @@ -170,12 +170,12 @@ compile3([], _Cwd, _Options) -> ok. %% Invokes the appropriate compiler, depending on the file extension. compile_file("", Input, _Output, _Options) -> - io:format("File has no extension: ~s~n", [Input]), + io:format("File has no extension: ~ts~n", [Input]), error; compile_file(Ext, Input, Output, Options) -> case compiler(Ext) of no -> - io:format("Unknown extension: '~s'\n", [Ext]), + io:format("Unknown extension: '~ts'\n", [Ext]), error; {M, F} -> case catch M:F(Input, Output, Options) of @@ -215,10 +215,10 @@ make_term(Str) -> case erl_parse:parse_term(Tokens ++ [{dot, 1}]) of {ok, Term} -> Term; {error, {_,_,Reason}} -> - io:format("~s: ~s~n", [Reason, Str]), + io:format("~ts: ~ts~n", [Reason, Str]), throw(error) end; {error, {_,_,Reason}, _} -> - io:format("~s: ~s~n", [Reason, Str]), + io:format("~ts: ~ts~n", [Reason, Str]), throw(error) end. diff --git a/lib/stdlib/src/erl_eval.erl b/lib/stdlib/src/erl_eval.erl index 95ba6b1096..0b57af1b6d 100644 --- a/lib/stdlib/src/erl_eval.erl +++ b/lib/stdlib/src/erl_eval.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2013. 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 @@ -227,13 +227,6 @@ expr({bc,_,E,Qs}, Bs, Lf, Ef, RBs) -> expr({tuple,_,Es}, Bs0, Lf, Ef, RBs) -> {Vs,Bs} = expr_list(Es, Bs0, Lf, Ef), ret_expr(list_to_tuple(Vs), Bs, RBs); -expr({record_field,_,_,_}=Mod, Bs, _Lf, _Ef, RBs) -> - case expand_module_name(Mod, Bs) of - {atom,_,A} -> - ret_expr(A, Bs, RBs); %% This is the "x.y" syntax - _ -> - erlang:raise(error, {badexpr, '.'}, stacktrace()) - end; expr({record_field,_,_,Name,_}, _Bs, _Lf, _Ef, _RBs) -> erlang:raise(error, {undef_record,Name}, stacktrace()); expr({record_index,_,Name,_}, _Bs, _Lf, _Ef, _RBs) -> @@ -332,8 +325,7 @@ expr({call,L1,{remote,L2,{record_field,_,{atom,_,''},{atom,_,qlc}=Mod}, Bs, Lf, Ef, RBs) when length(As0) =< 1 -> expr({call,L1,{remote,L2,Mod,Func},As}, Bs, Lf, Ef, RBs); expr({call,_,{remote,_,Mod,Func},As0}, Bs0, Lf, Ef, RBs) -> - Mod1 = expand_module_name(Mod, Bs0), - {value,M,Bs1} = expr(Mod1, Bs0, Lf, Ef, none), + {value,M,Bs1} = expr(Mod, Bs0, Lf, Ef, none), {value,F,Bs2} = expr(Func, Bs0, Lf, Ef, none), {As,Bs3} = expr_list(As0, merge_bindings(Bs1, Bs2), Lf, Ef), %% M could be a parameterized module (not an atom). @@ -354,7 +346,12 @@ expr({call,_,{atom,_,Func},As0}, Bs0, Lf, Ef, RBs) -> expr({call,_,Func0,As0}, Bs0, Lf, Ef, RBs) -> % function or {Mod,Fun} {value,Func,Bs1} = expr(Func0, Bs0, Lf, Ef, none), {As,Bs2} = expr_list(As0, Bs1, Lf, Ef), - do_apply(Func, As, Bs2, Ef, RBs); + case Func of + {M,F} when is_atom(M), is_atom(F) -> + erlang:raise(error, {badfun,Func}, stacktrace()); + _ -> + do_apply(Func, As, Bs2, Ef, RBs) + end; expr({'catch',_,Expr}, Bs0, Lf, Ef, RBs) -> Ref = make_ref(), case catch {Ref,expr(Expr, Bs0, Lf, Ef, none)} of @@ -1210,41 +1207,6 @@ ret_expr(_Old, New) -> line(Expr) -> element(2, Expr). -%% In syntax trees, module/package names are atoms or lists of atoms. - -expand_module_name({atom,L,A} = M, Bs) -> - case binding({module,A}, Bs) of - {value, A1} -> - {atom,L,A1}; - unbound -> - case packages:is_segmented(A) of - true -> - M; - false -> -%%% P = case binding({module,'$package'}, Bs) of -%%% {value, P1} -> P1; -%%% unbound -> "" -%%% end, -%%% A1 = list_to_atom(packages:concat(P, A)), -%%% {atom,L,list_to_atom(A1)} - {atom,L,A} - end - end; -expand_module_name(M, _) -> - case erl_parse:package_segments(M) of - error -> - M; - M1 -> - L = element(2,M), - Mod = packages:concat(M1), - case packages:is_valid(Mod) of - true -> - {atom,L,list_to_atom(Mod)}; - false -> - erlang:raise(error, {bad_module_name, Mod}, stacktrace()) - end - end. - %% {?MODULE,expr,3} is still the stacktrace, despite the %% fact that expr() now takes two, three or four arguments... stacktrace() -> [{?MODULE,expr,3}]. diff --git a/lib/stdlib/src/erl_expand_records.erl b/lib/stdlib/src/erl_expand_records.erl index 9759a8f001..d05f630d8e 100644 --- a/lib/stdlib/src/erl_expand_records.erl +++ b/lib/stdlib/src/erl_expand_records.erl @@ -135,8 +135,6 @@ pattern({tuple,Line,Ps}, St0) -> %%pattern({struct,Line,Tag,Ps}, St0) -> %% {TPs,TPsvs,St1} = pattern_list(Ps, St0), %% {{struct,Line,Tag,TPs},TPsvs,St1}; -pattern({record_field,_,_,_}=M, St) -> - {M,St}; % must be a package name pattern({record_index,Line,Name,Field}, St) -> {index_expr(Line, Field, Name, record_fields(Name, St)),St}; pattern({record,Line,Name,Pfs}, St0) -> @@ -306,8 +304,6 @@ expr({tuple,Line,Es0}, St0) -> %%expr({struct,Line,Tag,Es0}, Vs, St0) -> %% {Es1,Esvs,Esus,St1} = expr_list(Es0, Vs, St0), %% {{struct,Line,Tag,Es1},Esvs,Esus,St1}; -expr({record_field,_,_,_}=M, St) -> - {M,St}; % must be a package name expr({record_index,Line,Name,F}, St) -> I = index_expr(Line, F, Name, record_fields(Name, St)), expr(I, St); @@ -375,15 +371,9 @@ expr({call,Line,{atom,_La,N}=Atom,As0}, St0) -> end end end; -expr({call,Line,{record_field,_,_,_}=M,As0}, St0) -> - {As,St1} = expr_list(As0, St0), - {{call,Line,M,As},St1}; expr({call,Line,{remote,Lr,M,F},As0}, St0) -> {[M1,F1 | As1],St1} = expr_list([M,F | As0], St0), {{call,Line,{remote,Lr,M1,F1},As1},St1}; -expr({call,Line,{tuple,Lt,[{atom,_,_}=M,{atom,_,_}=F]},As0}, St0) -> - {As,St1} = expr_list(As0, St0), - {{call,Line,{tuple,Lt,[M,F]},As},St1}; expr({call,Line,F,As0}, St0) -> {[Fun1 | As1],St1} = expr_list([F | As0], St0), {{call,Line,Fun1,As1},St1}; diff --git a/lib/stdlib/src/erl_internal.erl b/lib/stdlib/src/erl_internal.erl index 3063881890..378e629ac9 100644 --- a/lib/stdlib/src/erl_internal.erl +++ b/lib/stdlib/src/erl_internal.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2012. All Rights Reserved. +%% Copyright Ericsson AB 1998-2013. 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 @@ -254,6 +254,9 @@ bif(binary_part, 2) -> true; bif(binary_part, 3) -> true; bif(binary_to_atom, 2) -> true; bif(binary_to_existing_atom, 2) -> true; +bif(binary_to_integer, 1) -> true; +bif(binary_to_integer, 2) -> true; +bif(binary_to_float, 1) -> true; bif(binary_to_list, 1) -> true; bif(binary_to_list, 3) -> true; bif(binary_to_term, 1) -> true; @@ -278,6 +281,9 @@ bif(exit, 1) -> true; bif(exit, 2) -> true; bif(float, 1) -> true; bif(float_to_list, 1) -> true; +bif(float_to_list, 2) -> true; +bif(float_to_binary, 1) -> true; +bif(float_to_binary, 2) -> true; bif(garbage_collect, 0) -> true; bif(garbage_collect, 1) -> true; bif(get, 0) -> true; @@ -289,6 +295,8 @@ bif(halt, 0) -> true; bif(halt, 1) -> true; bif(halt, 2) -> true; bif(hd, 1) -> true; +bif(integer_to_binary, 1) -> true; +bif(integer_to_binary, 2) -> true; bif(integer_to_list, 1) -> true; bif(integer_to_list, 2) -> true; bif(iolist_size, 1) -> true; diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl index 648ff349a4..68a8534f15 100644 --- a/lib/stdlib/src/erl_lint.erl +++ b/lib/stdlib/src/erl_lint.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2012. All Rights Reserved. +%% Copyright Ericsson AB 1996-2013. 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 @@ -94,12 +94,9 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) -> %% the other function collections contain {Function, Arity}. -record(lint, {state=start :: 'start' | 'attribute' | 'function', module=[], %Module - package="", %Module package - extends=[], %Extends behaviour=[], %Behaviour exports=gb_sets:empty() :: gb_set(), %Exports imports=[], %Imports - mod_imports=dict:new() :: dict(), %Module Imports compile=[], %Compile flags records=dict:new() :: dict(), %Record definitions locals=gb_sets:empty() :: gb_set(), %All defined functions (prescanned) @@ -114,7 +111,6 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) -> enabled_warnings=[], %All enabled warnings (ordset). errors=[], %Current errors warnings=[], %Current warnings - global_vt=[], %The global VarTable file = "" :: string(), %From last file attribute recdef_top=false :: boolean(), %true in record initialisation %outside any fun or lc @@ -140,14 +136,10 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) -> format_error(undefined_module) -> "no module definition"; -format_error({bad_module_name, M}) -> - io_lib:format("bad module name '~s'", [M]); format_error(redefine_module) -> "redefining module"; -format_error(redefine_extends) -> - "redefining extends attribute"; -format_error(extends_self) -> - "cannot extend from self"; +format_error(pmod_unsupported) -> + "parameterized modules are no longer supported"; %% format_error({redefine_mod_import, M, P}) -> %% io_lib:format("module '~s' already imported from package '~s'", [M, P]); @@ -160,18 +152,12 @@ format_error({attribute,A}) -> io_lib:format("attribute '~w' after function definitions", [A]); format_error({missing_qlc_hrl,A}) -> io_lib:format("qlc:q/~w called, but \"qlc.hrl\" not included", [A]); -format_error({redefine_import,{bif,{F,A},M}}) -> - io_lib:format("function ~w/~w already auto-imported from ~w", [F,A,M]); format_error({redefine_import,{{F,A},M}}) -> io_lib:format("function ~w/~w already imported from ~w", [F,A,M]); format_error({bad_inline,{F,A}}) -> io_lib:format("inlined function ~w/~w undefined", [F,A]); format_error({invalid_deprecated,D}) -> io_lib:format("badly formed deprecated attribute ~w", [D]); -format_error(invalid_extends) -> - "badly formed extends attribute"; -format_error(define_instance) -> - "defining instance function not allowed in abstract module"; format_error({bad_deprecated,{F,A}}) -> io_lib:format("deprecated function ~w/~w undefined or not exported", [F,A]); format_error({bad_nowarn_unused_function,{F,A}}) -> @@ -234,8 +220,6 @@ format_error({removed, MFA, String}) when is_list(String) -> io_lib:format("~s: ~s", [format_mfa(MFA), String]); format_error({obsolete_guard, {F, A}}) -> io_lib:format("~p/~p obsolete", [F, A]); -format_error({reserved_for_future,K}) -> - io_lib:format("atom ~w: future reserved keyword - rename or quote", [K]); format_error({too_many_arguments,Arity}) -> io_lib:format("too many arguments (~w) - " "maximum allowed is ~w", [Arity,?MAX_ARGUMENTS]); @@ -248,13 +232,6 @@ format_error({illegal_guard_local_call, {F,A}}) -> io_lib:format("call to local/imported function ~w/~w is illegal in guard", [F,A]); format_error(illegal_guard_expr) -> "illegal guard expression"; -format_error(deprecated_tuple_fun) -> - "tuple funs are deprecated and will be removed in R16"; -%% --- exports --- -format_error({explicit_export,F,A}) -> - io_lib:format("in this release, the call to ~w/~w must be written " - "like this: erlang:~w/~w", - [F,A,F,A]); %% --- records --- format_error({undefined_record,T}) -> io_lib:format("record ~w undefined", [T]); @@ -292,8 +269,6 @@ format_error({variable_in_record_def,V}) -> %% --- binaries --- format_error({undefined_bittype,Type}) -> io_lib:format("bit type ~w undefined", [Type]); -format_error({bittype_mismatch,T1,T2,What}) -> - io_lib:format("bit type mismatch (~s) between ~p and ~p", [What,T1,T2]); format_error(bittype_unit) -> "a bit unit size must not be specified unless a size is specified too"; format_error(illegal_bitsize) -> @@ -365,6 +340,12 @@ format_error(callback_wrong_arity) -> format_error({imported_predefined_type, Name}) -> io_lib:format("referring to built-in type ~w as a remote type; " "please take out the module name", [Name]); +format_error({not_exported_opaque, {TypeName, Arity}}) -> + io_lib:format("opaque type ~w~s is not exported", + [TypeName, gen_type_paren(Arity)]); +format_error({underspecified_opaque, {TypeName, Arity}}) -> + io_lib:format("opaque type ~w~s is underspecified and therefore meaningless", + [TypeName, gen_type_paren(Arity)]); %% --- obsolete? unused? --- format_error({format_error, {Fmt, Args}}) -> io_lib:format(Fmt, Args); @@ -533,7 +514,6 @@ start(File, Opts) -> end, #lint{state = start, exports = gb_sets:from_list([{module_info,0},{module_info,1}]), - mod_imports = dict:from_list([{erlang,erlang}]), compile = Opts, %% Internal pseudo-functions must appear as defined/reached. defined = gb_sets:from_list(pseudolocals()), @@ -619,8 +599,6 @@ forms(Forms0, St0) -> pre_scan([{function,_L,new,_A,_Cs} | Fs], St) -> pre_scan(Fs, St#lint{new=true}); -pre_scan([{attribute,_L,extends,M} | Fs], St) when is_atom(M) -> - pre_scan(Fs, St#lint{extends=true}); pre_scan([{attribute,L,compile,C} | Fs], St) -> case is_warn_enabled(export_all, St) andalso member(export_all, lists:flatten([C])) of @@ -675,51 +653,15 @@ form(Form, #lint{state=State}=St) -> %% start_state(Form, State) -> State' -start_state({attribute,L,module,{M,Ps}}, St) -> - St1 = set_module(M, L, St), - Arity = length(Ps), - Ps1 = if is_atom(St1#lint.extends) -> - ['BASE', 'THIS' | Ps]; - true -> - ['THIS' | Ps] - end, - Vt = orddict:from_list([{V, {bound, used, []}} || V <- Ps1]), - St2 = add_instance(Arity, St1), - St3 = ensure_new(Arity, St2), - St3#lint{state=attribute, extends=[], global_vt=Vt}; -start_state({attribute,L,module,M}, St) -> - St1 = set_module(M, L, St), - St1#lint{state=attribute, extends=[]}; +start_state({attribute,Line,module,{_,_}}=Form, St0) -> + St1 = add_error(Line, pmod_unsupported, St0), + attribute_state(Form, St1#lint{state=attribute}); +start_state({attribute,_,module,M}, St0) -> + St1 = St0#lint{module=M}, + St1#lint{state=attribute}; start_state(Form, St) -> St1 = add_error(element(2, Form), undefined_module, St), - attribute_state(Form, St1#lint{state=attribute, extends=[]}). - -set_module(M, L, St) -> - M1 = package_to_string(M), - case packages:is_valid(M1) of - true -> - St#lint{module=list_to_atom(M1), - package=packages:strip_last(M1)}; - false -> - add_error(L, {bad_module_name, M1}, St) - end. - -ensure_new(Arity, St) -> - case St#lint.new of - true -> - St; - false -> - add_func(new, Arity, St) - end. - -add_instance(Arity, St) -> - A = Arity + (if is_atom(St#lint.extends) -> 1; true -> 0 end), - add_func(instance, A, St). - -add_func(Name, Arity, St) -> - F = {Name, Arity}, - St#lint{exports = gb_sets:add_element(F, St#lint.exports), - defined = gb_sets:add_element(F, St#lint.defined)}. + attribute_state(Form, St1#lint{state=attribute}). %% attribute_state(Form, State) -> %% State' @@ -728,15 +670,6 @@ attribute_state({attribute,_L,module,_M}, #lint{module=[]}=St) -> St; attribute_state({attribute,L,module,_M}, St) -> add_error(L, redefine_module, St); -attribute_state({attribute,L,extends,M}, #lint{module=M}=St) when is_atom(M) -> - add_error(L, extends_self, St); -attribute_state({attribute,_L,extends,M}, #lint{extends=[]}=St) - when is_atom(M) -> - St#lint{extends=M}; -attribute_state({attribute,L,extends,M}, St) when is_atom(M) -> - add_error(L, redefine_extends, St); -attribute_state({attribute,L,extends,_M}, St) -> - add_error(L, invalid_extends, St); attribute_state({attribute,L,export,Es}, St) -> export(L, Es, St); attribute_state({attribute,L,export_type,Es}, St) -> @@ -851,7 +784,8 @@ post_traversal_check(Forms, St0) -> StC = check_untyped_records(Forms, StB), StD = check_on_load(StC), StE = check_unused_records(Forms, StD), - check_callback_information(StE). + StF = check_local_opaque_types(StE), + check_callback_information(StF). %% check_behaviour(State0) -> State %% Check that the behaviour attribute is valid. @@ -1000,9 +934,9 @@ check_imports(Forms, St0) -> true -> Usage = St0#lint.usage, Unused = ordsets:subtract(St0#lint.imports, Usage#usage.imported), - Imports = [{{FA,list_to_atom(package_to_string(Mod))},L} - || {attribute,L,import,{Mod,Fs}} <- Forms, - FA <- lists:usort(Fs)], + Imports = [{{FA,Mod},L} || + {attribute,L,import,{Mod,Fs}} <- Forms, + FA <- lists:usort(Fs)], Bad = [{FM,L} || FM <- Unused, {FM2,L} <- Imports, FM =:= FM2], func_line_warning(unused_import, Bad, St0) end. @@ -1215,73 +1149,46 @@ export_type(Line, ETs, #lint{usage = Usage, exp_types = ETs0} = St0) -> -spec import(line(), import(), lint_state()) -> lint_state(). import(Line, {Mod,Fs}, St) -> - Mod1 = package_to_string(Mod), - case packages:is_valid(Mod1) of - true -> - Mfs = ordsets:from_list(Fs), - case check_imports(Line, Mfs, St#lint.imports) of - [] -> - St#lint{imports=add_imports(list_to_atom(Mod1), Mfs, - St#lint.imports)}; - Efs -> - {Err, St1} = - foldl(fun ({bif,{F,A},_}, {Err,St0}) -> - %% BifClash - import directive - Warn = is_warn_enabled(bif_clash, St0) - and (not bif_clash_specifically_disabled(St0,{F,A})), - AutoImpSup = is_autoimport_suppressed(St0#lint.no_auto,{F,A}), - OldBif = erl_internal:old_bif(F,A), - {Err,if - Warn and (not AutoImpSup) and OldBif -> - add_error - (Line, - {redefine_old_bif_import, {F,A}}, - St0); - Warn and (not AutoImpSup) -> - add_warning - (Line, - {redefine_bif_import, {F,A}}, - St0); - true -> - St0 - end}; - (Ef, {_Err,St0}) -> - {true,add_error(Line, - {redefine_import,Ef}, - St0)} - end, - {false,St}, Efs), - if - not Err -> - St1#lint{imports= - add_imports(list_to_atom(Mod1), Mfs, + Mfs = ordsets:from_list(Fs), + case check_imports(Line, Mfs, St#lint.imports) of + [] -> + St#lint{imports=add_imports(Mod, Mfs, + St#lint.imports)}; + Efs -> + {Err, St1} = + foldl(fun ({bif,{F,A},_}, {Err,St0}) -> + %% BifClash - import directive + Warn = is_warn_enabled(bif_clash, St0) andalso + (not bif_clash_specifically_disabled(St0,{F,A})), + AutoImpSup = is_autoimport_suppressed(St0#lint.no_auto,{F,A}), + OldBif = erl_internal:old_bif(F,A), + {Err,if + Warn and (not AutoImpSup) and OldBif -> + add_error + (Line, + {redefine_old_bif_import, {F,A}}, + St0); + Warn and (not AutoImpSup) -> + add_warning + (Line, + {redefine_bif_import, {F,A}}, + St0); + true -> + St0 + end}; + (Ef, {_Err,St0}) -> + {true,add_error(Line, + {redefine_import,Ef}, + St0)} + end, + {false,St}, Efs), + if + not Err -> + St1#lint{imports=add_imports(Mod, Mfs, St#lint.imports)}; - true -> - St1 - end - end; - false -> - add_error(Line, {bad_module_name, Mod1}, St) - end; -import(Line, Mod, St) -> - Mod1 = package_to_string(Mod), - case packages:is_valid(Mod1) of - true -> - Key = list_to_atom(packages:last(Mod1)), - Imps = St#lint.mod_imports, -%%% case dict:is_key(Key, Imps) of -%%% true -> -%%% M = packages:last(Mod1), -%%% P = packages:strip_last(Mod1), -%%% add_error(Line, {redefine_mod_import, M, P}, St); -%%% false -> -%%% St#lint{mod_imports = -%%% dict:store(Key, list_to_atom(Mod1), Imps)} -%%% end; - St#lint{mod_imports = dict:store(Key, list_to_atom(Mod1), - Imps)}; - false -> - add_error(Line, {bad_module_name, Mod1}, St) + true -> + St1 + end end. check_imports(_Line, Fs, Is) -> @@ -1355,11 +1262,9 @@ call_function(Line, F, A, #lint{usage=Usage0,called=Cd,func=Func}=St) -> %% function(Line, Name, Arity, Clauses, State) -> State. -function(Line, instance, _Arity, _Cs, St) when St#lint.global_vt =/= [] -> - add_error(Line, define_instance, St); function(Line, Name, Arity, Cs, St0) -> St1 = define_function(Line, Name, Arity, St0#lint{func={Name,Arity}}), - clauses(Cs, St1#lint.global_vt, St1). + clauses(Cs, St1). -spec define_function(line(), atom(), arity(), lint_state()) -> lint_state(). @@ -1382,15 +1287,16 @@ function_check_max_args(Line, Arity, St) when Arity > ?MAX_ARGUMENTS -> add_error(Line, {too_many_arguments,Arity}, St); function_check_max_args(_, _, St) -> St. -%% clauses([Clause], VarTable, State) -> {VarTable, State}. +%% clauses([Clause], State) -> {VarTable, State}. -clauses(Cs, Vt, St) -> +clauses(Cs, St) -> foldl(fun (C, St0) -> - {_,St1} = clause(C, Vt, St0), + {_,St1} = clause(C, St0), St1 end, St, Cs). -clause({clause,_Line,H,G,B}, Vt0, St0) -> +clause({clause,_Line,H,G,B}, St0) -> + Vt0 = [], {Hvt,Binvt,St1} = head(H, Vt0, St0), %% Cannot ignore BinVt since "binsize variables" may have been used. Vt1 = vtupdate(Hvt, vtupdate(Binvt, Vt0)), @@ -1456,13 +1362,6 @@ pattern({record_index,Line,Name,Field}, _Vt, _Old, _Bvt, St) -> pattern_field(Field, Name, Dfs, St1) end), {Vt1,[],St1}; -pattern({record_field,Line,_,_}=M, _Vt, _Old, _Bvt, St0) -> - case expand_package(M, St0) of - {error, St1} -> - {[],[],add_error(Line, illegal_expr, St1)}; - {_, St1} -> - {[],[],St1} - end; pattern({record,Line,Name,Pfs}, Vt, Old, Bvt, St) -> case dict:find(Name, St#lint.records) of {ok,{_Line,Fields}} -> @@ -1844,13 +1743,6 @@ gexpr({tuple,_Line,Es}, Vt, St) -> gexpr({record_index,Line,Name,Field}, _Vt, St) -> check_record(Line, Name, St, fun (Dfs, St1) -> record_field(Field, Name, Dfs, St1) end ); -gexpr({record_field,Line,_,_}=M, _Vt, St0) -> - case expand_package(M, St0) of - {error, St1} -> - {[],add_error(Line, illegal_expr, St1)}; - {_, St1} -> - {[], St1} - end; gexpr({record_field,Line,Rec,Name,Field}, Vt, St0) -> {Rvt,St1} = gexpr(Rec, Vt, St0), {Fvt,St2} = check_record(Line, Name, St1, @@ -1895,11 +1787,9 @@ gexpr({call,Line,{atom,_La,F},As}, Vt, St0) -> %% BifClash - Function called in guard case erl_internal:guard_bif(F, A) andalso no_guard_bif_clash(St1,{F,A}) of true -> - %% Also check that it is auto-imported. - case erl_internal:bif(F, A) of - true -> {Asvt,St1}; - false -> {Asvt,add_error(Line, {explicit_export,F,A}, St1)} - end; + %% Assert that it is auto-imported. + true = erl_internal:bif(F, A), + {Asvt,St1}; false -> case is_local_function(St1#lint.locals,{F,A}) orelse is_imported_function(St1#lint.imports,{F,A}) of @@ -1916,9 +1806,6 @@ gexpr({call,Line,{remote,_Lr,{atom,_Lm,erlang},{atom,_Lf,F}},As}, Vt, St0) -> true -> {Asvt,St1}; false -> {Asvt,add_error(Line, illegal_guard_expr, St1)} end; -gexpr({call,L,{tuple,Lt,[{atom,Lm,erlang},{atom,Lf,F}]},As}, Vt, St0) -> - St = add_warning(L, deprecated_tuple_fun, St0), - gexpr({call,L,{remote,Lt,{atom,Lm,erlang},{atom,Lf,F}},As}, Vt, St); gexpr({op,Line,Op,A}, Vt, St0) -> {Avt,St1} = gexpr(A, Vt, St0), case is_gexpr_op(Op, 1) of @@ -1992,8 +1879,6 @@ is_gexpr({tuple,_L,Es}, RDs) -> is_gexpr_list(Es, RDs); %% is_gexpr_list(Es, RDs); is_gexpr({record_index,_L,_Name,Field}, RDs) -> is_gexpr(Field, RDs); -is_gexpr({record_field,_L,_,_}=M, _RDs) -> - erl_parse:package_segments(M) =/= error; is_gexpr({record_field,_L,Rec,_Name,Field}, RDs) -> is_gexpr_list([Rec,Field], RDs); is_gexpr({record,L,Name,Inits}, RDs) -> @@ -2082,13 +1967,6 @@ expr({record,Line,Name,Inits}, Vt, St) -> fun (Dfs, St1) -> init_fields(Inits, Line, Name, Dfs, Vt, St1) end); -expr({record_field,Line,_,_}=M, _Vt, St0) -> - case expand_package(M, St0) of - {error, St1} -> - {[],add_error(Line, illegal_expr, St1)}; - {_, St1} -> - {[], St1} - end; expr({record_field,Line,Rec,Name,Field}, Vt, St0) -> {Rvt,St1} = record_expr(Line, Rec, Vt, St0), {Fvt,St2} = check_record(Line, Name, St1, @@ -2159,20 +2037,14 @@ expr({call,Line,{remote,_Lr,{atom,_Lm,erlang},{atom,Lf,is_record}},[E,A]}, expr({call,Line,{atom,Lf,is_record},[E,A]}, Vt, St0); expr({call,L,{tuple,Lt,[{atom,Lm,erlang},{atom,Lf,is_record}]},As}, Vt, St) -> expr({call,L,{remote,Lt,{atom,Lm,erlang},{atom,Lf,is_record}},As}, Vt, St); +expr({call,Line,{remote,_Lr,{atom,_Lm,M},{atom,Lf,F}},As}, Vt, St0) -> + St1 = keyword_warning(Lf, F, St0), + St2 = check_remote_function(Line, M, F, As, St1), + expr_list(As, Vt, St2); expr({call,Line,{remote,_Lr,M,F},As}, Vt, St0) -> - case expand_package(M, St0) of - {error, _} -> - expr_list([M,F|As], Vt, St0); - {{atom,_La,M1}, St1} -> - case F of - {atom,Lf,F1} -> - St2 = keyword_warning(Lf, F1, St1), - St3 = check_remote_function(Line, M1, F1, As, St2), - expr_list(As, Vt, St3); - _ -> - expr_list([F|As], Vt, St1) - end - end; + St1 = keyword_warning(Line, M, St0), + St2 = keyword_warning(Line, F, St1), + expr_list([M,F|As], Vt, St2); expr({call,Line,{atom,La,F},As}, Vt, St0) -> St1 = keyword_warning(La, F, St0), {Asvt,St2} = expr_list(As, Vt, St1), @@ -2228,13 +2100,6 @@ expr({call,Line,{atom,La,F},As}, Vt, St0) -> end end} end; -expr({call,Line,{record_field,_,_,_}=F,As}, Vt, St0) -> - case expand_package(F, St0) of - {error, _} -> - expr_list([F|As], Vt, St0); - {A, St1} -> - expr({call,Line,A,As}, Vt, St1) - end; expr({call,Line,F,As}, Vt, St0) -> St = warn_invalid_call(Line,F,St0), expr_list([F|As], Vt, St); %They see the same variables @@ -2277,9 +2142,7 @@ expr({op,_Line,_Op,L,R}, Vt, St) -> expr_list([L,R], Vt, St); %They see the same variables %% The following are not allowed to occur anywhere! expr({remote,Line,_M,_F}, _Vt, St) -> - {[],add_error(Line, illegal_expr, St)}; -expr({'query',Line,_Q}, _Vt, St) -> - {[],add_error(Line, {mnemosyne,"query"}, St)}. + {[],add_error(Line, illegal_expr, St)}. %% expr_list(Expressions, Variables, State) -> %% {UsedVarTable,State} @@ -2557,15 +2420,24 @@ find_field(_F, []) -> error. %% Attr :: 'type' | 'opaque' %% Checks that a type definition is valid. +-record(typeinfo, {attr, line}). + type_def(_Attr, _Line, {record, _RecName}, Fields, [], St0) -> %% The record field names and such are checked in the record format. %% We only need to check the types. Types = [T || {typed_record_field, _, T} <- Fields], check_type({type, -1, product, Types}, St0); -type_def(_Attr, Line, TypeName, ProtoType, Args, St0) -> +type_def(Attr, Line, TypeName, ProtoType, Args, St0) -> TypeDefs = St0#lint.types, Arity = length(Args), TypePair = {TypeName, Arity}, + Info = #typeinfo{attr = Attr, line = Line}, + StoreType = + fun(St) -> + NewDefs = dict:store(TypePair, Info, TypeDefs), + CheckType = {type, -1, product, [ProtoType|Args]}, + check_type(CheckType, St#lint{types=NewDefs}) + end, case (dict:is_key(TypePair, TypeDefs) orelse is_var_arity_type(TypeName)) of true -> case dict:is_key(TypePair, default_types()) of @@ -2575,20 +2447,29 @@ type_def(_Attr, Line, TypeName, ProtoType, Args, St0) -> true -> Warn = {new_builtin_type, TypePair}, St1 = add_warning(Line, Warn, St0), - NewDefs = dict:store(TypePair, Line, TypeDefs), - CheckType = {type, -1, product, [ProtoType|Args]}, - check_type(CheckType, St1#lint{types=NewDefs}); + StoreType(St1); false -> add_error(Line, {builtin_type, TypePair}, St0) end; false -> add_error(Line, {redefine_type, TypePair}, St0) end; false -> - NewDefs = dict:store(TypePair, Line, TypeDefs), - CheckType = {type, -1, product, [ProtoType|Args]}, - check_type(CheckType, St0#lint{types=NewDefs}) + St1 = case + Attr =:= opaque andalso + is_underspecified(ProtoType, Arity) + of + true -> + Warn = {underspecified_opaque, TypePair}, + add_warning(Line, Warn, St0); + false -> St0 + end, + StoreType(St1) end. +is_underspecified({type,_,term,[]}, 0) -> true; +is_underspecified({type,_,any,[]}, 0) -> true; +is_underspecified(_ProtType, _Arity) -> false. + check_type(Types, St) -> {SeenVars, St1} = check_type(Types, dict:new(), St), dict:fold(fun(Var, {seen_once, Line}, AccSt) -> @@ -2898,7 +2779,7 @@ check_unused_types(Forms, #lint{usage=Usage, types=Ts, exp_types=ExpTs}=St) -> fun(_Type, -1, AccSt) -> %% Default type AccSt; - (Type, FileLine, AccSt) -> + (Type, #typeinfo{line = FileLine}, AccSt) -> case loc(FileLine) of {FirstFile, _} -> case gb_sets:is_member(Type, UsedTypes) of @@ -2917,6 +2798,24 @@ check_unused_types(Forms, #lint{usage=Usage, types=Ts, exp_types=ExpTs}=St) -> St end. +check_local_opaque_types(St) -> + #lint{types=Ts, exp_types=ExpTs} = St, + FoldFun = + fun(_Type, -1, AccSt) -> + %% Default type + AccSt; + (_Type, #typeinfo{attr = type}, AccSt) -> + AccSt; + (Type, #typeinfo{attr = opaque, line = FileLine}, AccSt) -> + case gb_sets:is_element(Type, ExpTs) of + true -> AccSt; + false -> + Warn = {not_exported_opaque,Type}, + add_warning(FileLine, Warn, AccSt) + end + end, + dict:fold(FoldFun, St, Ts). + %% icrt_clauses(Clauses, In, ImportVarTable, State) -> %% {NewVts,State}. @@ -3520,7 +3419,7 @@ check_format_3(Fmt, As) -> _Len -> {warn,1,"wrong number of arguments in format call",[]} end; {error,S} -> - {warn,1,"format string invalid (~s)",[S]} + {warn,1,"format string invalid (~ts)",[S]} end. args_list({cons,_L,_H,T}) -> args_list(T); @@ -3578,8 +3477,18 @@ extract_sequence(4, [$t, $c | Fmt], Need) -> extract_sequence(5, [$c|Fmt], Need); extract_sequence(4, [$t, $s | Fmt], Need) -> extract_sequence(5, [$s|Fmt], Need); +extract_sequence(4, [$t, $p | Fmt], Need) -> + extract_sequence(5, [$p|Fmt], Need); +extract_sequence(4, [$t, $P | Fmt], Need) -> + extract_sequence(5, [$P|Fmt], Need); extract_sequence(4, [$t, C | _Fmt], _Need) -> {error,"invalid control ~t" ++ [C]}; +extract_sequence(4, [$l, $p | Fmt], Need) -> + extract_sequence(5, [$p|Fmt], Need); +extract_sequence(4, [$l, $P | Fmt], Need) -> + extract_sequence(5, [$P|Fmt], Need); +extract_sequence(4, [$l, C | _Fmt], _Need) -> + {error,"invalid control ~l" ++ [C]}; extract_sequence(4, Fmt, Need) -> extract_sequence(5, Fmt, Need); extract_sequence(5, [C|Fmt], Need0) -> @@ -3614,49 +3523,6 @@ control_type($n, Need) -> Need; control_type($i, Need) -> [term|Need]; control_type(_C, _Need) -> error. -%% In syntax trees, module/package names are atoms or lists of atoms. - -package_to_string(A) when is_atom(A) -> atom_to_list(A); -package_to_string(L) when is_list(L) -> packages:concat(L). - -expand_package({atom,L,A} = M, St0) -> - St1 = keyword_warning(L, A, St0), - case dict:find(A, St1#lint.mod_imports) of - {ok, A1} -> - {{atom,L,A1}, St1}; - error -> - Name = atom_to_list(A), - case packages:is_valid(Name) of - true -> - case packages:is_segmented(Name) of - true -> - {M, St1}; - false -> - M1 = packages:concat(St1#lint.package, - Name), - {{atom,L,list_to_atom(M1)}, St1} - end; - false -> - St2 = add_error(L, {bad_module_name, Name}, St1), - {error, St2} - end - end; -expand_package(M, St0) -> - L = element(2, M), - case erl_parse:package_segments(M) of - error -> - {error, St0}; - M1 -> - Name = package_to_string(M1), - case packages:is_valid(Name) of - true -> - {{atom,L,list_to_atom(Name)}, St0}; - false -> - St1 = add_error(L, {bad_module_name, Name}, St0), - {error, St1} - end - end. - %% Prebuild set of local functions (to override auto-import) local_functions(Forms) -> diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl index 928c10f7f2..9ff25fcbc5 100644 --- a/lib/stdlib/src/erl_parse.yrl +++ b/lib/stdlib/src/erl_parse.yrl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2013. 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 @@ -26,7 +26,7 @@ attribute attr_val function function_clauses function_clause clause_args clause_guard clause_body expr expr_100 expr_150 expr_160 expr_200 expr_300 expr_400 expr_500 -expr_600 expr_700 expr_800 expr_900 +expr_600 expr_700 expr_800 expr_max list tail list_comprehension lc_expr lc_exprs @@ -36,7 +36,7 @@ tuple record_expr record_tuple record_field record_fields if_expr if_clause if_clauses case_expr cr_clause cr_clauses receive_expr fun_expr fun_clause fun_clauses atom_or_var integer_or_var -try_expr try_catch try_clause try_clauses query_expr +try_expr try_catch try_clause try_clauses function_call argument_list exprs guard atomic strings @@ -54,7 +54,7 @@ char integer float atom string var '(' ')' ',' '->' ':-' '{' '}' '[' ']' '|' '||' '<-' ';' ':' '#' '.' 'after' 'begin' 'case' 'try' 'catch' 'end' 'fun' 'if' 'of' 'receive' 'when' -'andalso' 'orelse' 'query' +'andalso' 'orelse' 'bnot' 'not' '*' '/' 'div' 'rem' 'band' 'and' '+' '-' 'bor' 'bxor' 'bsl' 'bsr' 'or' 'xor' @@ -253,15 +253,9 @@ expr_700 -> function_call : '$1'. expr_700 -> record_expr : '$1'. expr_700 -> expr_800 : '$1'. -expr_800 -> expr_900 ':' expr_max : +expr_800 -> expr_max ':' expr_max : {remote,?line('$2'),'$1','$3'}. -expr_800 -> expr_900 : '$1'. - -expr_900 -> '.' atom : - {record_field,?line('$1'),{atom,?line('$1'),''},'$2'}. -expr_900 -> expr_900 '.' atom : - {record_field,?line('$2'),'$1','$3'}. -expr_900 -> expr_max : '$1'. +expr_800 -> expr_max : '$1'. expr_max -> var : '$1'. expr_max -> atomic : '$1'. @@ -278,7 +272,6 @@ expr_max -> case_expr : '$1'. expr_max -> receive_expr : '$1'. expr_max -> fun_expr : '$1'. expr_max -> try_expr : '$1'. -expr_max -> query_expr : '$1'. list -> '[' ']' : {nil,?line('$1')}. @@ -438,9 +431,6 @@ try_clause -> var ':' expr clause_guard clause_body : L = ?line('$1'), {clause,L,[{tuple,L,['$1','$3',{var,L,'_'}]}],'$4','$5'}. -query_expr -> 'query' list_comprehension 'end' : - {'query',?line('$1'),'$2'}. - argument_list -> '(' ')' : {[],?line('$1')}. argument_list -> '(' exprs ')' : {'$2',?line('$1')}. @@ -510,7 +500,7 @@ Erlang code. -export([parse_form/1,parse_exprs/1,parse_term/1]). -export([normalise/1,abstract/1,tokens/1,tokens/2]). --export([abstract/2, package_segments/1]). +-export([abstract/2]). -export([inop_prec/1,preop_prec/1,func_prec/0,max_prec/0]). -export([set_line/2,get_attribute/2,get_attributes/1]). @@ -526,7 +516,7 @@ Erlang code. -type abstract_form() :: term(). -type error_description() :: term(). -type error_info() :: {erl_scan:line(), module(), error_description()}. --type token() :: {Tag :: atom(), Line :: erl_scan:line()}. +-type token() :: erl_scan:token(). %% mkop(Op, Arg) -> {op,Line,Op,Arg}. %% mkop(Left, Op, Right) -> {op,Line,Op,Left,Right}. @@ -679,20 +669,6 @@ build_attribute({atom,La,module}, Val) -> {attribute,La,module,Module}; [{atom,_Lm,Module},ExpList] -> {attribute,La,module,{Module,var_list(ExpList)}}; - [Name] -> - case package_segments(Name) of - error -> - error_bad_decl(La, module); - Module -> - {attribute,La,module,Module} - end; - [Name,ExpList] -> - case package_segments(Name) of - error -> - error_bad_decl(La, module); - Module -> - {attribute,La,module,{Module,var_list(ExpList)}} - end; _Other -> error_bad_decl(La, module) end; @@ -704,22 +680,8 @@ build_attribute({atom,La,export}, Val) -> end; build_attribute({atom,La,import}, Val) -> case Val of - [Name] -> - case package_segments(Name) of - error -> - error_bad_decl(La, import); - Module -> - {attribute,La,import,Module} - end; [{atom,_Lm,Mod},ImpList] -> {attribute,La,import,{Mod,farity_list(ImpList)}}; - [Name, ImpList] -> - case package_segments(Name) of - error -> - error_bad_decl(La, import); - Module -> - {attribute,La,import,{Module,farity_list(ImpList)}} - end; _Other -> error_bad_decl(La, import) end; build_attribute({atom,La,record}, Val) -> @@ -820,18 +782,6 @@ term(Expr) -> catch _:_R -> ret_err(?line(Expr), "bad attribute") end. -package_segments(Name) -> - package_segments(Name, [], []). - -package_segments({record_field, _, F1, F2}, Fs, As) -> - package_segments(F1, [F2 | Fs], As); -package_segments({atom, _, A}, [F | Fs], As) -> - package_segments(F, Fs, [A | As]); -package_segments({atom, _, A}, [], As) -> - lists:reverse([A | As]); -package_segments(_, _, _) -> - error. - %% build_function([Clause]) -> {function,Line,Name,Arity,[Clause]} build_function(Cs) -> @@ -900,12 +850,6 @@ normalise({cons,_,Head,Tail}) -> [normalise(Head)|normalise(Tail)]; normalise({tuple,_,Args}) -> list_to_tuple(normalise_list(Args)); -%% Atom dot-notation, as in 'foo.bar.baz' -normalise({record_field,_,_,_}=A) -> - case package_segments(A) of - error -> erlang:error({badarg, A}); - As -> list_to_atom(packages:concat(As)) - end; %% Special case for unary +/-. normalise({op,_,'+',{char,_,I}}) -> I; normalise({op,_,'+',{integer,_,I}}) -> I; @@ -923,73 +867,77 @@ normalise_list([]) -> -spec abstract(Data) -> AbsTerm when Data :: term(), AbsTerm :: abstract_expr(). -abstract(T) when is_integer(T) -> {integer,0,T}; -abstract(T) when is_float(T) -> {float,0,T}; -abstract(T) when is_atom(T) -> {atom,0,T}; -abstract([]) -> {nil,0}; -abstract(B) when is_bitstring(B) -> - {bin, 0, [abstract_byte(Byte, 0) || Byte <- bitstring_to_list(B)]}; -abstract([C|T]) when is_integer(C), 0 =< C, C < 256 -> - abstract_string(T, [C]); -abstract([H|T]) -> - {cons,0,abstract(H),abstract(T)}; -abstract(Tuple) when is_tuple(Tuple) -> - {tuple,0,abstract_list(tuple_to_list(Tuple))}. - -abstract_string([C|T], String) when is_integer(C), 0 =< C, C < 256 -> - abstract_string(T, [C|String]); -abstract_string([], String) -> - {string, 0, lists:reverse(String)}; -abstract_string(T, String) -> - not_string(String, abstract(T)). - -not_string([C|T], Result) -> - not_string(T, {cons, 0, {integer, 0, C}, Result}); -not_string([], Result) -> +abstract(T) -> + abstract(T, 0, epp:default_encoding()). + +%%% abstract/2 takes line and encoding options +-spec abstract(Data, Options) -> AbsTerm when + Data :: term(), + Options :: Line | [Option], + Option :: {line, Line} | {encoding, Encoding}, + Encoding :: latin1 | unicode | utf8, + Line :: erl_scan:line(), + AbsTerm :: abstract_expr(). + +abstract(T, Line) when is_integer(Line) -> + abstract(T, Line, epp:default_encoding()); +abstract(T, Options) when is_list(Options) -> + Line = proplists:get_value(line, Options, 0), + Encoding = proplists:get_value(encoding, Options,epp:default_encoding()), + abstract(T, Line, Encoding). + +-define(UNICODE(C), + (C >= 0 andalso C < 16#D800 orelse + C > 16#DFFF andalso C < 16#FFFE orelse + C > 16#FFFF andalso C =< 16#10FFFF)). + +abstract(T, L, _E) when is_integer(T) -> {integer,L,T}; +abstract(T, L, _E) when is_float(T) -> {float,L,T}; +abstract(T, L, _E) when is_atom(T) -> {atom,L,T}; +abstract([], L, _E) -> {nil,L}; +abstract(B, L, _E) when is_bitstring(B) -> + {bin, L, [abstract_byte(Byte, L) || Byte <- bitstring_to_list(B)]}; +abstract([C|T], L, unicode=E) when ?UNICODE(C) -> + abstract_unicode_string(T, [C], L, E); +abstract([C|T], L, utf8=E) when ?UNICODE(C) -> + abstract_unicode_string(T, [C], L, E); +abstract([C|T], L, latin1=E) when is_integer(C), 0 =< C, C < 256 -> + abstract_string(T, [C], L, E); +abstract([H|T], L, E) -> + {cons,L,abstract(H, L, E),abstract(T, L, E)}; +abstract(Tuple, L, E) when is_tuple(Tuple) -> + {tuple,L,abstract_list(tuple_to_list(Tuple), L, E)}. + +abstract_string([C|T], String, L, E) when is_integer(C), 0 =< C, C < 256 -> + abstract_string(T, [C|String], L, E); +abstract_string([], String, L, _E) -> + {string, L, lists:reverse(String)}; +abstract_string(T, String, L, E) -> + not_string(String, abstract(T, L, E), L, E). + +abstract_unicode_string([C|T], String, L, E) when ?UNICODE(C) -> + abstract_unicode_string(T, [C|String], L, E); +abstract_unicode_string([], String, L, _E) -> + {string, L, lists:reverse(String)}; +abstract_unicode_string(T, String, L, E) -> + not_string(String, abstract(T, L, E), L, E). + +not_string([C|T], Result, L, E) -> + not_string(T, {cons, L, {integer, L, C}, Result}, L, E); +not_string([], Result, _L, _E) -> Result. -abstract_list([H|T]) -> - [abstract(H)|abstract_list(T)]; -abstract_list([]) -> +abstract_list([H|T], L, E) -> + [abstract(H, L, E)|abstract_list(T, L, E)]; +abstract_list([], _L, _E) -> []. -abstract_byte(Byte, Line) when is_integer(Byte) -> - {bin_element, Line, {integer, Line, Byte}, default, default}; -abstract_byte(Bits, Line) -> +abstract_byte(Byte, L) when is_integer(Byte) -> + {bin_element, L, {integer, L, Byte}, default, default}; +abstract_byte(Bits, L) -> Sz = bit_size(Bits), <<Val:Sz>> = Bits, - {bin_element, Line, {integer, Line, Val}, {integer, Line, Sz}, default}. - -%%% abstract/2 keeps the line number -abstract(T, Line) when is_integer(T) -> {integer,Line,T}; -abstract(T, Line) when is_float(T) -> {float,Line,T}; -abstract(T, Line) when is_atom(T) -> {atom,Line,T}; -abstract([], Line) -> {nil,Line}; -abstract(B, Line) when is_bitstring(B) -> - {bin, Line, [abstract_byte(Byte, Line) || Byte <- bitstring_to_list(B)]}; -abstract([C|T], Line) when is_integer(C), 0 =< C, C < 256 -> - abstract_string(T, [C], Line); -abstract([H|T], Line) -> - {cons,Line,abstract(H, Line),abstract(T, Line)}; -abstract(Tuple, Line) when is_tuple(Tuple) -> - {tuple,Line,abstract_list(tuple_to_list(Tuple), Line)}. - -abstract_string([C|T], String, Line) when is_integer(C), 0 =< C, C < 256 -> - abstract_string(T, [C|String], Line); -abstract_string([], String, Line) -> - {string, Line, lists:reverse(String)}; -abstract_string(T, String, Line) -> - not_string(String, abstract(T, Line), Line). - -not_string([C|T], Result, Line) -> - not_string(T, {cons, Line, {integer, Line, C}, Result}, Line); -not_string([], Result, _Line) -> - Result. - -abstract_list([H|T], Line) -> - [abstract(H, Line)|abstract_list(T, Line)]; -abstract_list([], _Line) -> - []. + {bin_element, L, {integer, L, Val}, {integer, L, Sz}, default}. %% Generate a list of tokens representing the abstract term. @@ -1079,9 +1027,9 @@ preop_prec('#') -> {700,800}. func_prec() -> {800,700}. --spec max_prec() -> 1000. +-spec max_prec() -> 900. -max_prec() -> 1000. +max_prec() -> 900. %%% [Experimental]. The parser just copies the attributes of the %%% scanner tokens to the abstract format. This design decision has diff --git a/lib/stdlib/src/erl_pp.erl b/lib/stdlib/src/erl_pp.erl index 6b5aa951cf..7c7566e4ec 100644 --- a/lib/stdlib/src/erl_pp.erl +++ b/lib/stdlib/src/erl_pp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2013. 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 @@ -26,7 +26,7 @@ guard/1,guard/2,exprs/1,exprs/2,exprs/3,expr/1,expr/2,expr/3,expr/4]). -import(lists, [append/1,foldr/3,mapfoldl/3,reverse/1,reverse/2]). --import(io_lib, [write/1,format/2,write_char/1,write_string/1]). +-import(io_lib, [write/1,format/2]). -import(erl_parse, [inop_prec/1,preop_prec/1,func_prec/0,max_prec/0]). -define(MAXLINE, 72). @@ -36,7 +36,15 @@ CurrentIndentation :: integer(), CurrentPrecedence :: non_neg_integer(), HookFunction :: hook_function()) -> - io_lib:chars())). + io_lib:chars())). + +-type(option() :: {hook, hook_function()} + | {encoding, latin1 | unicode | utf8}). +-type(options() :: hook_function() | [option()]). + +-record(pp, {string_fun, char_fun}). + +-record(options, {hook, encoding, opts}). %%% %%% Exported functions @@ -48,12 +56,13 @@ form(Thing) -> form(Thing, none). --spec(form(Form, HookFunction) -> io_lib:chars() when +-spec(form(Form, Options) -> io_lib:chars() when Form :: erl_parse:abstract_form(), - HookFunction :: hook_function()). + Options :: options()). -form(Thing, Hook) -> - frmt(lform(Thing, Hook)). +form(Thing, Options) -> + State = state(Options), + frmt(lform(Thing, options(Options), State), State). -spec(attribute(Attribute) -> io_lib:chars() when Attribute :: erl_parse:abstract_form()). @@ -61,12 +70,13 @@ form(Thing, Hook) -> attribute(Thing) -> attribute(Thing, none). --spec(attribute(Attribute, HookFunction) -> io_lib:chars() when +-spec(attribute(Attribute, Options) -> io_lib:chars() when Attribute :: erl_parse:abstract_form(), - HookFunction :: hook_function()). + Options :: options()). -attribute(Thing, Hook) -> - frmt(lattribute(Thing, Hook)). +attribute(Thing, Options) -> + State = state(Options), + frmt(lattribute(Thing, options(Options), State), State). -spec(function(Function) -> io_lib:chars() when Function :: erl_parse:abstract_form()). @@ -74,18 +84,18 @@ attribute(Thing, Hook) -> function(F) -> function(F, none). --spec(function(Function, HookFunction) -> io_lib:chars() when +-spec(function(Function, Options) -> io_lib:chars() when Function :: erl_parse:abstract_form(), - HookFunction :: hook_function()). + Options :: options()). -function(F, Hook) -> - frmt(lfunction(F, Hook)). +function(F, Options) -> + frmt(lfunction(F, options(Options)), state(Options)). rule(R) -> rule(R, none). -rule(R, Hook) -> - frmt(lrule(R, Hook)). +rule(R, Options) -> + frmt(lrule(R, options(Options)), state(Options)). -spec(guard(Guard) -> io_lib:chars() when Guard :: [erl_parse:abstract_expr()]). @@ -93,12 +103,12 @@ rule(R, Hook) -> guard(Gs) -> guard(Gs, none). --spec(guard(Guard, HookFunction) -> io_lib:chars() when +-spec(guard(Guard, Options) -> io_lib:chars() when Guard :: [erl_parse:abstract_expr()], - HookFunction :: hook_function()). + Options :: options()). -guard(Gs, Hook) -> - frmt(lguard(Gs, Hook)). +guard(Gs, Options) -> + frmt(lguard(Gs, options(Options)), state(Options)). -spec(exprs(Expressions) -> io_lib:chars() when Expressions :: [erl_parse:abstract_expr()]). @@ -106,99 +116,129 @@ guard(Gs, Hook) -> exprs(Es) -> exprs(Es, 0, none). --spec(exprs(Expressions, HookFunction) -> io_lib:chars() when +-spec(exprs(Expressions, Options) -> io_lib:chars() when Expressions :: [erl_parse:abstract_expr()], - HookFunction :: hook_function()). + Options :: options()). -exprs(Es, Hook) -> - exprs(Es, 0, Hook). +exprs(Es, Options) -> + exprs(Es, 0, Options). --spec(exprs(Expressions, Indent, HookFunction) -> io_lib:chars() when +-spec(exprs(Expressions, Indent, Options) -> io_lib:chars() when Expressions :: [erl_parse:abstract_expr()], Indent :: integer(), - HookFunction :: hook_function()). + Options :: options()). -exprs(Es, I, Hook) -> - frmt({seq,[],[],[$,],lexprs(Es, Hook)}, I). +exprs(Es, I, Options) -> + frmt({seq,[],[],[$,],lexprs(Es, options(Options))}, I, state(Options)). -spec(expr(Expression) -> io_lib:chars() when Expression :: erl_parse:abstract_expr()). expr(E) -> - frmt(lexpr(E, 0, none)). + frmt(lexpr(E, 0, options(none)), state(none)). --spec(expr(Expression, HookFunction) -> io_lib:chars() when +-spec(expr(Expression, Options) -> io_lib:chars() when Expression :: erl_parse:abstract_expr(), - HookFunction :: hook_function()). + Options :: options()). -expr(E, Hook) -> - frmt(lexpr(E, 0, Hook)). +expr(E, Options) -> + frmt(lexpr(E, 0, options(Options)), state(Options)). --spec(expr(Expression, Indent, HookFunction) -> io_lib:chars() when +-spec(expr(Expression, Indent, Options) -> io_lib:chars() when Expression :: erl_parse:abstract_expr(), Indent :: integer(), - HookFunction :: hook_function()). + Options :: options()). -expr(E, I, Hook) -> - frmt(lexpr(E, 0, Hook), I). +expr(E, I, Options) -> + frmt(lexpr(E, 0, options(Options)), I, state(Options)). --spec(expr(Expression, Indent, Precedence, HookFunction) -> io_lib:chars() when +-spec(expr(Expression, Indent, Precedence, Options) -> io_lib:chars() when Expression :: erl_parse:abstract_expr(), Indent :: integer(), Precedence :: non_neg_integer(), - HookFunction :: hook_function()). + Options :: options()). -expr(E, I, P, Hook) -> - frmt(lexpr(E, P, Hook), I). +expr(E, I, P, Options) -> + frmt(lexpr(E, P, options(Options)), I, state(Options)). %%% %%% Local functions %%% -lform({attribute,Line,Name,Arg}, Hook) -> - lattribute({attribute,Line,Name,Arg}, Hook); -lform({function,Line,Name,Arity,Clauses}, Hook) -> - lfunction({function,Line,Name,Arity,Clauses}, Hook); -lform({rule,Line,Name,Arity,Clauses}, Hook) -> - lrule({rule,Line,Name,Arity,Clauses}, Hook); +options(Options) when is_list(Options) -> + Hook = proplists:get_value(hook, Options, none), + Encoding = encoding(Options), + #options{hook = Hook, encoding = Encoding, opts = Options}; +options(Hook) -> + #options{hook = Hook, encoding = encoding([]), opts = Hook}. + +state(Options) when is_list(Options) -> + case encoding(Options) of + latin1 -> state(); + unicode -> unicode_state() + end; +state(_Hook) -> + state(). + +state() -> + #pp{string_fun = fun io_lib:write_string_as_latin1/1, + char_fun = fun io_lib:write_char_as_latin1/1}. + +unicode_state() -> + #pp{string_fun = fun io_lib:write_string/1, + char_fun = fun io_lib:write_char/1}. + +encoding(Options) -> + case proplists:get_value(encoding, Options, epp:default_encoding()) of + latin1 -> latin1; + utf8 -> unicode; + unicode -> unicode + end. + +lform({attribute,Line,Name,Arg}, Opts, State) -> + lattribute({attribute,Line,Name,Arg}, Opts, State); +lform({function,Line,Name,Arity,Clauses}, Opts, _State) -> + lfunction({function,Line,Name,Arity,Clauses}, Opts); +lform({rule,Line,Name,Arity,Clauses}, Opts, _State) -> + lrule({rule,Line,Name,Arity,Clauses}, Opts); %% These are specials to make it easier for the compiler. -lform({error,E}, _Hook) -> +lform({error,E}, _Opts, _State) -> leaf(format("~p\n", [{error,E}])); -lform({warning,W}, _Hook) -> +lform({warning,W}, _Opts, _State) -> leaf(format("~p\n", [{warning,W}])); -lform({eof,_Line}, _Hook) -> +lform({eof,_Line}, _Opts, _State) -> $\n. -lattribute({attribute,_Line,type,Type}, Hook) -> - [typeattr(type, Type, Hook),leaf(".\n")]; -lattribute({attribute,_Line,opaque,Type}, Hook) -> - [typeattr(opaque, Type, Hook),leaf(".\n")]; -lattribute({attribute,_Line,spec,Arg}, _Hook) -> +lattribute({attribute,_Line,type,Type}, Opts, _State) -> + [typeattr(type, Type, Opts),leaf(".\n")]; +lattribute({attribute,_Line,opaque,Type}, Opts, _State) -> + [typeattr(opaque, Type, Opts),leaf(".\n")]; +lattribute({attribute,_Line,spec,Arg}, _Opts, _State) -> [specattr(Arg),leaf(".\n")]; -lattribute({attribute,_Line,Name,Arg}, Hook) -> - [lattribute(Name, Arg, Hook),leaf(".\n")]. +lattribute({attribute,_Line,Name,Arg}, Opts, State) -> + [lattribute(Name, Arg, Opts, State),leaf(".\n")]. -lattribute(module, {M,Vs}, _Hook) -> +lattribute(module, {M,Vs}, _Opts, _State) -> attr("module",[{var,0,pname(M)}, foldr(fun(V, C) -> {cons,0,{var,0,V},C} end, {nil,0}, Vs)]); -lattribute(module, M, _Hook) -> +lattribute(module, M, _Opts, _State) -> attr("module", [{var,0,pname(M)}]); -lattribute(export, Falist, _Hook) -> +lattribute(export, Falist, _Opts, _State) -> call({var,0,"-export"}, [falist(Falist)], 0, none); -lattribute(import, Name, _Hook) when is_list(Name) -> +lattribute(import, Name, _Opts, _State) when is_list(Name) -> attr("import", [{var,0,pname(Name)}]); -lattribute(import, {From,Falist}, _Hook) -> +lattribute(import, {From,Falist}, _Opts, _State) -> attr("import",[{var,0,pname(From)},falist(Falist)]); -lattribute(file, {Name,Line}, _Hook) -> - attr("file", [{var,0,format("~p", [Name])},{integer,0,Line}]); -lattribute(record, {Name,Is}, Hook) -> +lattribute(file, {Name,Line}, _Opts, State) -> + attr("file", [{var,0,(State#pp.string_fun)(Name)},{integer,0,Line}]); +lattribute(record, {Name,Is}, Opts, _State) -> Nl = leaf(format("-record(~w,", [Name])), - [{first,Nl,record_fields(Is, Hook)},$)]; -lattribute(Name, Arg, _Hook) -> - attr(write(Name), [erl_parse:abstract(Arg)]). + [{first,Nl,record_fields(Is, Opts)},$)]; +lattribute(Name, Arg, #options{encoding = Encoding}, _State) -> + attr(write(Name), [erl_parse:abstract(Arg, [{encoding,Encoding}])]). -typeattr(Tag, {TypeName,Type,Args}, _Hook) -> +typeattr(Tag, {TypeName,Type,Args}, _Opts) -> {first,leaf("-"++atom_to_list(Tag)++" "), typed(call({atom,0,TypeName}, Args, 0, none), Type)}. @@ -293,7 +333,7 @@ guard_type(Before, Gs) -> Gl = {list,[{step,'when',expr_list(Gs, [$,], fun constraint/2, none)}]}, {list,[{step,Before,Gl}]}. -constraint({type,_Line,constraint,[Tag,As]}, _Hook) -> +constraint({type,_Line,constraint,[Tag,As]}, _Opts) -> simple_type(Tag, As). fun_type(Before, {type,_,'fun',[FType,Ret]}) -> @@ -333,231 +373,230 @@ falist([]) -> falist([{Name,Arity}|Falist]) -> {cons,0,{var,0,format("~w/~w", [Name,Arity])},falist(Falist)}. -lfunction({function,_Line,Name,_Arity,Cs}, Hook) -> - Cll = nl_clauses(fun (C, H) -> func_clause(Name, C, H) end, $;, Hook, Cs), +lfunction({function,_Line,Name,_Arity,Cs}, Opts) -> + Cll = nl_clauses(fun (C, H) -> func_clause(Name, C, H) end, $;, Opts, Cs), [Cll,leaf(".\n")]. -func_clause(Name, {clause,Line,Head,Guard,Body}, Hook) -> - Hl = call({atom,Line,Name}, Head, 0, Hook), - Gl = guard_when(Hl, Guard, Hook), - Bl = body(Body, Hook), +func_clause(Name, {clause,Line,Head,Guard,Body}, Opts) -> + Hl = call({atom,Line,Name}, Head, 0, Opts), + Gl = guard_when(Hl, Guard, Opts), + Bl = body(Body, Opts), {step,Gl,Bl}. -lrule({rule,_Line,Name,_Arity,Cs}, Hook) -> - Cll = nl_clauses(fun (C, H) -> rule_clause(Name, C, H) end, $;, Hook, Cs), +lrule({rule,_Line,Name,_Arity,Cs}, Opts) -> + Cll = nl_clauses(fun (C, H) -> rule_clause(Name, C, H) end, $;, Opts, Cs), [Cll,leaf(".\n")]. -rule_clause(Name, {clause,Line,Head,Guard,Body}, Hook) -> - Hl = call({atom,Line,Name}, Head, 0, Hook), - Gl = guard_when(Hl, Guard, Hook, leaf(" :-")), - Bl = rule_body(Body, Hook), +rule_clause(Name, {clause,Line,Head,Guard,Body}, Opts) -> + Hl = call({atom,Line,Name}, Head, 0, Opts), + Gl = guard_when(Hl, Guard, Opts, leaf(" :-")), + Bl = rule_body(Body, Opts), {step,Gl,Bl}. -rule_body(Es, Hook) -> - lc_quals(Es, Hook). +rule_body(Es, Opts) -> + lc_quals(Es, Opts). -guard_when(Before, Guard, Hook) -> - guard_when(Before, Guard, Hook, ' ->'). +guard_when(Before, Guard, Opts) -> + guard_when(Before, Guard, Opts, ' ->'). -guard_when(Before, Guard, Hook, After) -> - Gl = lguard(Guard, Hook), +guard_when(Before, Guard, Opts, After) -> + Gl = lguard(Guard, Opts), [{list,[{step,Before,Gl}]},After]. -lguard([E|Es], Hook) when is_list(E) -> - {list,[{step,'when',expr_list([E|Es], [$;], fun guard0/2, Hook)}]}; -lguard([E|Es], Hook) -> % before R6 - lguard([[E|Es]], Hook); +lguard([E|Es], Opts) when is_list(E) -> + {list,[{step,'when',expr_list([E|Es], [$;], fun guard0/2, Opts)}]}; +lguard([E|Es], Opts) -> % before R6 + lguard([[E|Es]], Opts); lguard([], _) -> []. -guard0(Es, Hook) -> - expr_list(Es, [$,], fun lexpr/2, Hook). +guard0(Es, Opts) -> + expr_list(Es, [$,], fun lexpr/2, Opts). -%% body(Before, Es, Hook) -> [Char]. +%% body(Before, Es, Opts) -> [Char]. -body([E], Hook) -> - lexpr(E, Hook); -body(Es, Hook) -> - {prefer_nl,[$,],lexprs(Es, Hook)}. +body([E], Opts) -> + lexpr(E, Opts); +body(Es, Opts) -> + {prefer_nl,[$,],lexprs(Es, Opts)}. -lexpr(E, Hook) -> - lexpr(E, 0, Hook). +lexpr(E, Opts) -> + lexpr(E, 0, Opts). lexpr({var,_,V}, _, _) when is_integer(V) -> %Special hack for Robert leaf(format("_~w", [V])); -lexpr({var,_,V}, _, _) -> leaf(format("~s", [V])); -lexpr({char,_,C}, _, _) -> leaf(write_char(C)); +lexpr({var,_,V}, _, _) -> leaf(format("~ts", [V])); +lexpr({char,_,C}, _, _) -> {char,C}; lexpr({integer,_,N}, _, _) -> leaf(write(N)); lexpr({float,_,F}, _, _) -> leaf(write(F)); lexpr({atom,_,A}, _, _) -> leaf(write(A)); lexpr({string,_,S}, _, _) -> {string,S}; lexpr({nil,_}, _, _) -> '[]'; -lexpr({cons,_,H,T}, _, Hook) -> - list(T, [H], Hook); -lexpr({lc,_,E,Qs}, _Prec, Hook) -> - Lcl = {list,[{step,[lexpr(E, Hook),leaf(" ||")],lc_quals(Qs, Hook)}]}, +lexpr({cons,_,H,T}, _, Opts) -> + list(T, [H], Opts); +lexpr({lc,_,E,Qs}, _Prec, Opts) -> + Lcl = {list,[{step,[lexpr(E, Opts),leaf(" ||")],lc_quals(Qs, Opts)}]}, {list,[{seq,$[,[],[[]],[{force_nl,leaf(" "),[Lcl]}]},$]]}; %% {list,[{step,$[,Lcl},$]]}; -lexpr({bc,_,E,Qs}, _Prec, Hook) -> - Lcl = {list,[{step,[lexpr(E, Hook),leaf(" ||")],lc_quals(Qs, Hook)}]}, +lexpr({bc,_,E,Qs}, _Prec, Opts) -> + Lcl = {list,[{step,[lexpr(E, Opts),leaf(" ||")],lc_quals(Qs, Opts)}]}, {list,[{seq,'<<',[],[[]],[{force_nl,leaf(" "),[Lcl]}]},'>>']}; %% {list,[{step,'<<',Lcl},'>>']}; -lexpr({tuple,_,Elts}, _, Hook) -> - tuple(Elts, Hook); -%%lexpr({struct,_,Tag,Elts}, _, Hook) -> -%% {first,format("~w", [Tag]),tuple(Elts, Hook)}; -lexpr({record_index, _, Name, F}, Prec, Hook) -> +lexpr({tuple,_,Elts}, _, Opts) -> + tuple(Elts, Opts); +%%lexpr({struct,_,Tag,Elts}, _, Opts) -> +%% {first,format("~w", [Tag]),tuple(Elts, Opts)}; +lexpr({record_index, _, Name, F}, Prec, Opts) -> {P,R} = preop_prec('#'), Nl = record_name(Name), - El = [Nl,$.,lexpr(F, R, Hook)], + El = [Nl,$.,lexpr(F, R, Opts)], maybe_paren(P, Prec, El); -lexpr({record, _, Name, Fs}, Prec, Hook) -> +lexpr({record, _, Name, Fs}, Prec, Opts) -> {P,_R} = preop_prec('#'), Nl = record_name(Name), - El = {first,Nl,record_fields(Fs, Hook)}, + El = {first,Nl,record_fields(Fs, Opts)}, maybe_paren(P, Prec, El); -lexpr({record_field, _, Rec, Name, F}, Prec, Hook) -> +lexpr({record_field, _, Rec, Name, F}, Prec, Opts) -> {L,P,R} = inop_prec('#'), - Rl = lexpr(Rec, L, Hook), + Rl = lexpr(Rec, L, Opts), Nl = leaf(format("#~w.", [Name])), - El = [Rl,Nl,lexpr(F, R, Hook)], + El = [Rl,Nl,lexpr(F, R, Opts)], maybe_paren(P, Prec, El); -lexpr({record, _, Rec, Name, Fs}, Prec, Hook) -> +lexpr({record, _, Rec, Name, Fs}, Prec, Opts) -> {L,P,_R} = inop_prec('#'), - Rl = lexpr(Rec, L, Hook), + Rl = lexpr(Rec, L, Opts), Nl = record_name(Name), - El = {first,[Rl,Nl],record_fields(Fs, Hook)}, + El = {first,[Rl,Nl],record_fields(Fs, Opts)}, maybe_paren(P, Prec, El); -lexpr({record_field, _, {atom,_,''}, F}, Prec, Hook) -> +lexpr({record_field, _, {atom,_,''}, F}, Prec, Opts) -> {_L,P,R} = inop_prec('.'), - El = [$.,lexpr(F, R, Hook)], + El = [$.,lexpr(F, R, Opts)], maybe_paren(P, Prec, El); -lexpr({record_field, _, Rec, F}, Prec, Hook) -> +lexpr({record_field, _, Rec, F}, Prec, Opts) -> {L,P,R} = inop_prec('.'), - El = [lexpr(Rec, L, Hook),$.,lexpr(F, R, Hook)], + El = [lexpr(Rec, L, Opts),$.,lexpr(F, R, Opts)], maybe_paren(P, Prec, El); -lexpr({block,_,Es}, _, Hook) -> - {list,[{step,'begin',body(Es, Hook)},'end']}; -lexpr({'if',_,Cs}, _, Hook) -> - {list,[{step,'if',if_clauses(Cs, Hook)},'end']}; -lexpr({'case',_,Expr,Cs}, _, Hook) -> - {list,[{step,{list,[{step,'case',lexpr(Expr, Hook)},'of']}, - cr_clauses(Cs, Hook)}, +lexpr({block,_,Es}, _, Opts) -> + {list,[{step,'begin',body(Es, Opts)},'end']}; +lexpr({'if',_,Cs}, _, Opts) -> + {list,[{step,'if',if_clauses(Cs, Opts)},'end']}; +lexpr({'case',_,Expr,Cs}, _, Opts) -> + {list,[{step,{list,[{step,'case',lexpr(Expr, Opts)},'of']}, + cr_clauses(Cs, Opts)}, 'end']}; -lexpr({'cond',_,Cs}, _, Hook) -> - {list,[{step,leaf("cond"),cond_clauses(Cs, Hook)},'end']}; -lexpr({'receive',_,Cs}, _, Hook) -> - {list,[{step,'receive',cr_clauses(Cs, Hook)},'end']}; -lexpr({'receive',_,Cs,To,ToOpt}, _, Hook) -> - Al = {list,[{step,[lexpr(To, Hook),' ->'],body(ToOpt, Hook)}]}, - {list,[{step,'receive',cr_clauses(Cs, Hook)}, +lexpr({'cond',_,Cs}, _, Opts) -> + {list,[{step,leaf("cond"),cond_clauses(Cs, Opts)},'end']}; +lexpr({'receive',_,Cs}, _, Opts) -> + {list,[{step,'receive',cr_clauses(Cs, Opts)},'end']}; +lexpr({'receive',_,Cs,To,ToOpt}, _, Opts) -> + Al = {list,[{step,[lexpr(To, Opts),' ->'],body(ToOpt, Opts)}]}, + {list,[{step,'receive',cr_clauses(Cs, Opts)}, {step,'after',Al}, 'end']}; -lexpr({'fun',_,{function,F,A}}, _Prec, _Hook) -> +lexpr({'fun',_,{function,F,A}}, _Prec, _Opts) -> leaf(format("fun ~w/~w", [F,A])); -lexpr({'fun',_,{function,F,A},Extra}, _Prec, _Hook) -> +lexpr({'fun',_,{function,F,A},Extra}, _Prec, _Opts) -> {force_nl,fun_info(Extra),leaf(format("fun ~w/~w", [F,A]))}; -lexpr({'fun',_,{function,M,F,A}}, _Prec, _Hook) +lexpr({'fun',_,{function,M,F,A}}, _Prec, _Opts) when is_atom(M), is_atom(F), is_integer(A) -> %% For backward compatibility with pre-R15 abstract format. leaf(format("fun ~w:~w/~w", [M,F,A])); -lexpr({'fun',_,{function,M,F,A}}, _Prec, Hook) -> +lexpr({'fun',_,{function,M,F,A}}, _Prec, Opts) -> %% New format in R15. - NameItem = lexpr(M, Hook), - CallItem = lexpr(F, Hook), - ArityItem = lexpr(A, Hook), + NameItem = lexpr(M, Opts), + CallItem = lexpr(F, Opts), + ArityItem = lexpr(A, Opts), ["fun ",NameItem,$:,CallItem,$/,ArityItem]; -lexpr({'fun',_,{clauses,Cs}}, _Prec, Hook) -> - {list,[{first,'fun',fun_clauses(Cs, Hook)},'end']}; -lexpr({'fun',_,{clauses,Cs},Extra}, _Prec, Hook) -> +lexpr({'fun',_,{clauses,Cs}}, _Prec, Opts) -> + {list,[{first,'fun',fun_clauses(Cs, Opts)},'end']}; +lexpr({'fun',_,{clauses,Cs},Extra}, _Prec, Opts) -> {force_nl,fun_info(Extra), - {list,[{first,'fun',fun_clauses(Cs, Hook)},'end']}}; -lexpr({'query',_,Lc}, _Prec, Hook) -> - {list,[{step,leaf("query"),lexpr(Lc, 0, Hook)},'end']}; -lexpr({call,_,{remote,_,{atom,_,M},{atom,_,F}=N}=Name,Args}, Prec, Hook) -> + {list,[{first,'fun',fun_clauses(Cs, Opts)},'end']}}; +lexpr({call,_,{remote,_,{atom,_,M},{atom,_,F}=N}=Name,Args}, Prec, Opts) -> case erl_internal:bif(M, F, length(Args)) of true -> - call(N, Args, Prec, Hook); + call(N, Args, Prec, Opts); false -> - call(Name, Args, Prec, Hook) + call(Name, Args, Prec, Opts) end; -lexpr({call,_,Name,Args}, Prec, Hook) -> - call(Name, Args, Prec, Hook); -lexpr({'try',_,Es,Scs,Ccs,As}, _, Hook) -> +lexpr({call,_,Name,Args}, Prec, Opts) -> + call(Name, Args, Prec, Opts); +lexpr({'try',_,Es,Scs,Ccs,As}, _, Opts) -> {list,[if Scs =:= [] -> - {step,'try',body(Es, Hook)}; + {step,'try',body(Es, Opts)}; true -> - {step,{list,[{step,'try',body(Es, Hook)},'of']}, - cr_clauses(Scs, Hook)} + {step,{list,[{step,'try',body(Es, Opts)},'of']}, + cr_clauses(Scs, Opts)} end, if Ccs =:= [] -> []; true -> - {step,'catch',try_clauses(Ccs, Hook)} + {step,'catch',try_clauses(Ccs, Opts)} end, if As =:= [] -> []; true -> - {step,'after',body(As, Hook)} + {step,'after',body(As, Opts)} end, 'end']}; -lexpr({'catch',_,Expr}, Prec, Hook) -> +lexpr({'catch',_,Expr}, Prec, Opts) -> {P,R} = preop_prec('catch'), - El = {list,[{step,'catch',lexpr(Expr, R, Hook)}]}, + El = {list,[{step,'catch',lexpr(Expr, R, Opts)}]}, maybe_paren(P, Prec, El); -lexpr({match,_,Lhs,Rhs}, Prec, Hook) -> +lexpr({match,_,Lhs,Rhs}, Prec, Opts) -> {L,P,R} = inop_prec('='), - Pl = lexpr(Lhs, L, Hook), - Rl = lexpr(Rhs, R, Hook), + Pl = lexpr(Lhs, L, Opts), + Rl = lexpr(Rhs, R, Opts), El = {list,[{cstep,[Pl,' ='],Rl}]}, maybe_paren(P, Prec, El); -lexpr({op,_,Op,Arg}, Prec, Hook) -> +lexpr({op,_,Op,Arg}, Prec, Opts) -> {P,R} = preop_prec(Op), Ol = leaf(format("~s ", [Op])), - El = [Ol,lexpr(Arg, R, Hook)], + El = [Ol,lexpr(Arg, R, Opts)], maybe_paren(P, Prec, El); -lexpr({op,_,Op,Larg,Rarg}, Prec, Hook) when Op =:= 'orelse'; +lexpr({op,_,Op,Larg,Rarg}, Prec, Opts) when Op =:= 'orelse'; Op =:= 'andalso' -> %% Breaks lines since R12B. {L,P,R} = inop_prec(Op), - Ll = lexpr(Larg, L, Hook), + Ll = lexpr(Larg, L, Opts), Ol = leaf(format("~s", [Op])), - Lr = lexpr(Rarg, R, Hook), + Lr = lexpr(Rarg, R, Opts), El = {prefer_nl,[[]],[Ll,Ol,Lr]}, maybe_paren(P, Prec, El); -lexpr({op,_,Op,Larg,Rarg}, Prec, Hook) -> +lexpr({op,_,Op,Larg,Rarg}, Prec, Opts) -> {L,P,R} = inop_prec(Op), - Ll = lexpr(Larg, L, Hook), + Ll = lexpr(Larg, L, Opts), Ol = leaf(format("~s", [Op])), - Lr = lexpr(Rarg, R, Hook), + Lr = lexpr(Rarg, R, Opts), El = {list,[Ll,Ol,Lr]}, maybe_paren(P, Prec, El); %% Special expressions which are not really legal everywhere. -lexpr({remote,_,M,F}, Prec, Hook) -> +lexpr({remote,_,M,F}, Prec, Opts) -> {L,P,R} = inop_prec(':'), - NameItem = lexpr(M, L, Hook), - CallItem = lexpr(F, R, Hook), + NameItem = lexpr(M, L, Opts), + CallItem = lexpr(F, R, Opts), maybe_paren(P, Prec, [NameItem,$:,CallItem]); %% BIT SYNTAX: -lexpr({bin,_,Fs}, _, Hook) -> - bit_grp(Fs, Hook); +lexpr({bin,_,Fs}, _, Opts) -> + bit_grp(Fs, Opts); %% Special case for straight values. lexpr({value,_,Val}, _,_) -> leaf(write(Val)); %% Now do the hook. -lexpr(Other, _Precedence, none) -> +lexpr(Other, _Precedence, #options{hook = none}) -> leaf(format("INVALID-FORM:~w:",[Other])); -lexpr(HookExpr, Precedence, {Mod,Func,Eas}) when Mod =/= 'fun' -> +lexpr(HookExpr, Precedence, #options{hook = {Mod,Func,Eas}}) + when Mod =/= 'fun' -> {ehook,HookExpr,Precedence,{Mod,Func,Eas}}; -lexpr(HookExpr, Precedence, Func) -> - {hook,HookExpr,Precedence,Func}. +lexpr(HookExpr, Precedence, #options{hook = Func, opts = Options}) -> + {hook,HookExpr,Precedence,Func,Options}. -call(Name, Args, Prec, Hook) -> +call(Name, Args, Prec, Opts) -> {F,P} = func_prec(), - Item = {first,lexpr(Name, F, Hook),args(Args, Hook)}, + Item = {first,lexpr(Name, F, Opts),args(Args, Opts)}, maybe_paren(P, Prec, Item). fun_info(Extra) -> @@ -565,32 +604,18 @@ fun_info(Extra) -> %% BITS: -bit_grp(Fs, Hook) -> - append([['<<'], - [try - true = Fs =/= [], - S = bin_string(Fs), - true = io_lib:printable_list(S), - {string,S} - catch _:_ -> - bit_elems(Fs, Hook) - end], - ['>>']]). - -bin_string([]) -> - []; -bin_string([{bin_element,_,{char,_,C},_,_}|Bin]) -> - [C | bin_string(Bin)]. +bit_grp(Fs, Opts) -> + append([['<<'], [bit_elems(Fs, Opts)], ['>>']]). -bit_elems(Es, Hook) -> - expr_list(Es, $,, fun bit_elem/2, Hook). +bit_elems(Es, Opts) -> + expr_list(Es, $,, fun bit_elem/2, Opts). -bit_elem({bin_element,_,Expr,Sz,Types}, Hook) -> +bit_elem({bin_element,_,Expr,Sz,Types}, Opts) -> P = max_prec(), - VChars = lexpr(Expr, P, Hook), + VChars = lexpr(Expr, P, Opts), SChars = if Sz =/= default -> - [VChars,$:,lexpr(Sz, P, Hook)]; + [VChars,$:,lexpr(Sz, P, Opts)]; true -> VChars end, @@ -618,157 +643,157 @@ bit_elem_type(T) -> record_name(Name) -> leaf(format("#~w", [Name])). -record_fields(Fs, Hook) -> - tuple(Fs, fun record_field/2, Hook). +record_fields(Fs, Opts) -> + tuple(Fs, fun record_field/2, Opts). -record_field({record_field,_,F,Val}, Hook) -> +record_field({record_field,_,F,Val}, Opts) -> {L,_P,R} = inop_prec('='), - Fl = lexpr(F, L, Hook), - Vl = lexpr(Val, R, Hook), + Fl = lexpr(F, L, Opts), + Vl = lexpr(Val, R, Opts), {list,[{cstep,[Fl,' ='],Vl}]}; -record_field({typed_record_field,{record_field,_,F,Val},Type}, Hook) -> +record_field({typed_record_field,{record_field,_,F,Val},Type}, Opts) -> {L,_P,R} = inop_prec('='), - Fl = lexpr(F, L, Hook), - Vl = typed(lexpr(Val, R, Hook), Type), + Fl = lexpr(F, L, Opts), + Vl = typed(lexpr(Val, R, Opts), Type), {list,[{cstep,[Fl,' ='],Vl}]}; -record_field({typed_record_field,Field,Type}, Hook) -> - typed(record_field(Field, Hook), Type); -record_field({record_field,_,F}, Hook) -> - lexpr(F, 0, Hook). - -list({cons,_,H,T}, Es, Hook) -> - list(T, [H|Es], Hook); -list({nil,_}, Es, Hook) -> - proper_list(reverse(Es), Hook); -list(Other, Es, Hook) -> - improper_list(reverse(Es, [Other]), Hook). - -%% if_clauses(Clauses, Hook) -> [Char]. +record_field({typed_record_field,Field,Type}, Opts) -> + typed(record_field(Field, Opts), Type); +record_field({record_field,_,F}, Opts) -> + lexpr(F, 0, Opts). + +list({cons,_,H,T}, Es, Opts) -> + list(T, [H|Es], Opts); +list({nil,_}, Es, Opts) -> + proper_list(reverse(Es), Opts); +list(Other, Es, Opts) -> + improper_list(reverse(Es, [Other]), Opts). + +%% if_clauses(Clauses, Opts) -> [Char]. %% Print 'if' clauses. -if_clauses(Cs, Hook) -> - clauses(fun if_clause/2, Hook, Cs). +if_clauses(Cs, Opts) -> + clauses(fun if_clause/2, Opts, Cs). -if_clause({clause,_,[],G,B}, Hook) -> - Gl = [guard_no_when(G, Hook),' ->'], - {step,Gl,body(B, Hook)}. +if_clause({clause,_,[],G,B}, Opts) -> + Gl = [guard_no_when(G, Opts),' ->'], + {step,Gl,body(B, Opts)}. -guard_no_when([E|Es], Hook) when is_list(E) -> - expr_list([E|Es], $;, fun guard0/2, Hook); -guard_no_when([E|Es], Hook) -> % before R6 - guard_no_when([[E|Es]], Hook); +guard_no_when([E|Es], Opts) when is_list(E) -> + expr_list([E|Es], $;, fun guard0/2, Opts); +guard_no_when([E|Es], Opts) -> % before R6 + guard_no_when([[E|Es]], Opts); guard_no_when([], _) -> % cannot happen leaf("true"). -%% cr_clauses(Clauses, Hook) -> [Char]. +%% cr_clauses(Clauses, Opts) -> [Char]. %% Print 'case'/'receive' clauses. -cr_clauses(Cs, Hook) -> - clauses(fun cr_clause/2, Hook, Cs). +cr_clauses(Cs, Opts) -> + clauses(fun cr_clause/2, Opts, Cs). -cr_clause({clause,_,[T],G,B}, Hook) -> - El = lexpr(T, 0, Hook), - Gl = guard_when(El, G, Hook), - Bl = body(B, Hook), +cr_clause({clause,_,[T],G,B}, Opts) -> + El = lexpr(T, 0, Opts), + Gl = guard_when(El, G, Opts), + Bl = body(B, Opts), {step,Gl,Bl}. -%% try_clauses(Clauses, Hook) -> [Char]. +%% try_clauses(Clauses, Opts) -> [Char]. %% Print 'try' clauses. -try_clauses(Cs, Hook) -> - clauses(fun try_clause/2, Hook, Cs). +try_clauses(Cs, Opts) -> + clauses(fun try_clause/2, Opts, Cs). -try_clause({clause,_,[{tuple,_,[{atom,_,throw},V,S]}],G,B}, Hook) -> - El = lexpr(V, 0, Hook), - Sl = stack_backtrace(S, [El], Hook), - Gl = guard_when(Sl, G, Hook), - Bl = body(B, Hook), +try_clause({clause,_,[{tuple,_,[{atom,_,throw},V,S]}],G,B}, Opts) -> + El = lexpr(V, 0, Opts), + Sl = stack_backtrace(S, [El], Opts), + Gl = guard_when(Sl, G, Opts), + Bl = body(B, Opts), {step,Gl,Bl}; -try_clause({clause,_,[{tuple,_,[C,V,S]}],G,B}, Hook) -> - Cs = lexpr(C, 0, Hook), - El = lexpr(V, 0, Hook), +try_clause({clause,_,[{tuple,_,[C,V,S]}],G,B}, Opts) -> + Cs = lexpr(C, 0, Opts), + El = lexpr(V, 0, Opts), CsEl = [Cs,$:,El], - Sl = stack_backtrace(S, CsEl, Hook), - Gl = guard_when(Sl, G, Hook), - Bl = body(B, Hook), + Sl = stack_backtrace(S, CsEl, Opts), + Gl = guard_when(Sl, G, Opts), + Bl = body(B, Opts), {step,Gl,Bl}. -stack_backtrace({var,_,'_'}, El, _Hook) -> +stack_backtrace({var,_,'_'}, El, _Opts) -> El; -stack_backtrace(S, El, Hook) -> - El++[$:,lexpr(S, 0, Hook)]. +stack_backtrace(S, El, Opts) -> + El++[$:,lexpr(S, 0, Opts)]. -%% fun_clauses(Clauses, Hook) -> [Char]. +%% fun_clauses(Clauses, Opts) -> [Char]. %% Print 'fun' clauses. -fun_clauses(Cs, Hook) -> - nl_clauses(fun fun_clause/2, [$;], Hook, Cs). +fun_clauses(Cs, Opts) -> + nl_clauses(fun fun_clause/2, [$;], Opts, Cs). -fun_clause({clause,_,A,G,B}, Hook) -> - El = args(A, Hook), - Gl = guard_when(El, G, Hook), - Bl = body(B, Hook), +fun_clause({clause,_,A,G,B}, Opts) -> + El = args(A, Opts), + Gl = guard_when(El, G, Opts), + Bl = body(B, Opts), {step,Gl,Bl}. -%% cond_clauses(Clauses, Hook) -> [Char]. +%% cond_clauses(Clauses, Opts) -> [Char]. %% Print 'cond' clauses. -cond_clauses(Cs, Hook) -> - clauses(fun cond_clause/2, Hook, Cs). +cond_clauses(Cs, Opts) -> + clauses(fun cond_clause/2, Opts, Cs). -cond_clause({clause,_,[],[[E]],B}, Hook) -> - {step,[lexpr(E, Hook),' ->'],body(B, Hook)}. +cond_clause({clause,_,[],[[E]],B}, Opts) -> + {step,[lexpr(E, Opts),' ->'],body(B, Opts)}. -%% nl_clauses(Type, Hook, Clauses) -> [Char]. +%% nl_clauses(Type, Opts, Clauses) -> [Char]. %% Generic clause printing function (always breaks lines). -nl_clauses(Type, Sep, Hook, Cs) -> - {prefer_nl,Sep,lexprs(Cs, Type, Hook)}. +nl_clauses(Type, Sep, Opts, Cs) -> + {prefer_nl,Sep,lexprs(Cs, Type, Opts)}. -%% clauses(Type, Hook, Clauses) -> [Char]. +%% clauses(Type, Opts, Clauses) -> [Char]. %% Generic clause printing function (breaks lines since R12B). -clauses(Type, Hook, Cs) -> - {prefer_nl,[$;],lexprs(Cs, Type, Hook)}. +clauses(Type, Opts, Cs) -> + {prefer_nl,[$;],lexprs(Cs, Type, Opts)}. -%% lc_quals(Qualifiers, After, Hook) +%% lc_quals(Qualifiers, After, Opts) %% List comprehension qualifiers (breaks lines since R12B). -lc_quals(Qs, Hook) -> - {prefer_nl,[$,],lexprs(Qs, fun lc_qual/2, Hook)}. +lc_quals(Qs, Opts) -> + {prefer_nl,[$,],lexprs(Qs, fun lc_qual/2, Opts)}. -lc_qual({b_generate,_,Pat,E}, Hook) -> - Pl = lexpr(Pat, 0, Hook), - {list,[{step,[Pl,leaf(" <=")],lexpr(E, 0, Hook)}]}; -lc_qual({generate,_,Pat,E}, Hook) -> - Pl = lexpr(Pat, 0, Hook), - {list,[{step,[Pl,leaf(" <-")],lexpr(E, 0, Hook)}]}; -lc_qual(Q, Hook) -> - lexpr(Q, 0, Hook). +lc_qual({b_generate,_,Pat,E}, Opts) -> + Pl = lexpr(Pat, 0, Opts), + {list,[{step,[Pl,leaf(" <=")],lexpr(E, 0, Opts)}]}; +lc_qual({generate,_,Pat,E}, Opts) -> + Pl = lexpr(Pat, 0, Opts), + {list,[{step,[Pl,leaf(" <-")],lexpr(E, 0, Opts)}]}; +lc_qual(Q, Opts) -> + lexpr(Q, 0, Opts). -proper_list(Es, Hook) -> - {seq,$[,$],$,,lexprs(Es, Hook)}. +proper_list(Es, Opts) -> + {seq,$[,$],$,,lexprs(Es, Opts)}. -improper_list(Es, Hook) -> - {seq,$[,$],{$,,$|},lexprs(Es, Hook)}. +improper_list(Es, Opts) -> + {seq,$[,$],{$,,$|},lexprs(Es, Opts)}. -tuple(L, Hook) -> - tuple(L, fun lexpr/2, Hook). +tuple(L, Opts) -> + tuple(L, fun lexpr/2, Opts). -tuple(Es, F, Hook) -> - {seq,${,$},$,,lexprs(Es, F, Hook)}. +tuple(Es, F, Opts) -> + {seq,${,$},$,,lexprs(Es, F, Opts)}. -args(As, Hook) -> - {seq,$(,$),[$,],lexprs(As, Hook)}. +args(As, Opts) -> + {seq,$(,$),[$,],lexprs(As, Opts)}. -expr_list(Es, Sep, F, Hook) -> - {seq,[],[],Sep,lexprs(Es, F, Hook)}. +expr_list(Es, Sep, F, Opts) -> + {seq,[],[],Sep,lexprs(Es, F, Opts)}. -lexprs(Es, Hook) -> - lexprs(Es, fun lexpr/2, Hook). +lexprs(Es, Opts) -> + lexprs(Es, fun lexpr/2, Opts). -lexprs(Es, F, Hook) -> - [F(E, Hook) || E <- Es]. +lexprs(Es, F, Opts) -> + [F(E, Opts) || E <- Es]. maybe_paren(P, Prec, Expr) when P < Prec -> [$(,Expr,$)]; @@ -776,18 +801,18 @@ maybe_paren(_P, _Prec, Expr) -> Expr. leaf(S) -> - {leaf,iolist_size(S),S}. + {leaf,chars_size(S),S}. %%% Do the formatting. Currently nothing fancy. Could probably have %%% done it in one single pass. -frmt(Item) -> - frmt(Item, 0). +frmt(Item, PP) -> + frmt(Item, 0, PP). -frmt(Item, I) -> +frmt(Item, I, PP) -> ST = spacetab(), WT = wordtable(), - {Chars,_Length} = f(Item, I, ST, WT), + {Chars,_Length} = f(Item, I, ST, WT, PP), [Chars]. %%% What the tags mean: @@ -803,6 +828,7 @@ frmt(Item, I) -> %%% - {force_nl,ExtraInfo,I}: fun-info (a comment) forces linebreak before I. %%% - {prefer_nl,Sep,IPs}: forces linebreak between Is unlesss negative %%% indentation. +%%% - {char,C}: a character %%% - {string,S}: a string. %%% - {hook,...}, {ehook,...}: hook expressions. %%% @@ -812,22 +838,22 @@ frmt(Item, I) -> %%% cstep works similarly, but no linebreak if the width of I1 is less %%% than the indentation (this is for "A = <expression over several lines>). -f([]=Nil, _I0, _ST, _WT) -> +f([]=Nil, _I0, _ST, _WT, _PP) -> {Nil,0}; -f(C, _I0, _ST, _WT) when is_integer(C) -> +f(C, _I0, _ST, _WT, _PP) when is_integer(C) -> {C,1}; -f({leaf,Length,Chars}, _I0, _ST, _WT) -> +f({leaf,Length,Chars}, _I0, _ST, _WT, _PP) -> {Chars,Length}; -f([Item|Items], I0, ST, WT) -> - consecutive(Items, f(Item, I0, ST, WT), I0, ST, WT); -f({list,Items}, I0, ST, WT) -> - f({seq,[],[],[[]],Items}, I0, ST, WT); -f({first,E,Item}, I0, ST, WT) -> - f({seq,E,[],[[]],[Item]}, I0, ST, WT); -f({seq,Before,After,Sep,LItems}, I0, ST, WT) -> - BCharsSize = f(Before, I0, ST, WT), +f([Item|Items], I0, ST, WT, PP) -> + consecutive(Items, f(Item, I0, ST, WT, PP), I0, ST, WT, PP); +f({list,Items}, I0, ST, WT, PP) -> + f({seq,[],[],[[]],Items}, I0, ST, WT, PP); +f({first,E,Item}, I0, ST, WT, PP) -> + f({seq,E,[],[[]],[Item]}, I0, ST, WT, PP); +f({seq,Before,After,Sep,LItems}, I0, ST, WT, PP) -> + BCharsSize = f(Before, I0, ST, WT, PP), I = indent(BCharsSize, I0), - CharsSizeL = fl(LItems, Sep, I, After, ST, WT), + CharsSizeL = fl(LItems, Sep, I, After, ST, WT, PP), {CharsL,SizeL} = unz(CharsSizeL), {BCharsL,BSizeL} = unz1([BCharsSize]), Sizes = BSizeL ++ SizeL, @@ -848,15 +874,15 @@ f({seq,Before,After,Sep,LItems}, I0, ST, WT) -> {BCharsL++insert_newlines(CharsSizeL, I, ST), nsz(lists:last(Sizes), I0)} end; -f({force_nl,_ExtraInfoItem,Item}, I, ST, WT) when I < 0 -> +f({force_nl,_ExtraInfoItem,Item}, I, ST, WT, PP) when I < 0 -> %% Extra info is a comment; cannot have that on the same line - f(Item, I, ST, WT); -f({force_nl,ExtraInfoItem,Item}, I, ST, WT) -> - f({prefer_nl,[],[ExtraInfoItem,Item]}, I, ST, WT); -f({prefer_nl,Sep,LItems}, I, ST, WT) when I < 0 -> - f({seq,[],[],Sep,LItems}, I, ST, WT); -f({prefer_nl,Sep,LItems}, I0, ST, WT) -> - CharsSize2L = fl(LItems, Sep, I0, [], ST, WT), + f(Item, I, ST, WT, PP); +f({force_nl,ExtraInfoItem,Item}, I, ST, WT, PP) -> + f({prefer_nl,[],[ExtraInfoItem,Item]}, I, ST, WT, PP); +f({prefer_nl,Sep,LItems}, I, ST, WT, PP) when I < 0 -> + f({seq,[],[],Sep,LItems}, I, ST, WT, PP); +f({prefer_nl,Sep,LItems}, I0, ST, WT, PP) -> + CharsSize2L = fl(LItems, Sep, I0, [], ST, WT, PP), {_CharsL,Sizes} = unz(CharsSize2L), if Sizes =:= [] -> @@ -864,37 +890,40 @@ f({prefer_nl,Sep,LItems}, I0, ST, WT) -> true -> {insert_newlines(CharsSize2L, I0, ST),nsz(lists:last(Sizes), I0)} end; -f({string,S}, I, ST, WT) -> - f(write_a_string(S, I), I, ST, WT); -f({hook,HookExpr,Precedence,Func}, I, _ST, _WT) -> - Chars = Func(HookExpr, I, Precedence, Func), +f({char,C}, I, ST, WT, PP) -> + f(write_a_char(C, PP), I, ST, WT, PP); +f({string,S}, I, ST, WT, PP) -> + f(write_a_string(S, I, PP), I, ST, WT, PP); +f({hook,HookExpr,Precedence,Func,Options}, I, _ST, _WT, _PP) -> + Chars = Func(HookExpr, I, Precedence, Options), {Chars,indentation(Chars, I)}; -f({ehook,HookExpr,Precedence,{Mod,Func,Eas}=ModFuncEas}, I, _ST, _WT) -> +f({ehook,HookExpr,Precedence,{Mod,Func,Eas}=ModFuncEas}, I, _ST, _WT, _PP) -> Chars = apply(Mod, Func, [HookExpr,I,Precedence,ModFuncEas|Eas]), {Chars,indentation(Chars, I)}; -f(WordName, _I, _ST, WT) -> % when is_atom(WordName) +f(WordName, _I, _ST, WT, _PP) -> % when is_atom(WordName) word(WordName, WT). -define(IND, 4). %% fl(ListItems, I0, ST, WT) -> [[CharsSize1,CharsSize2]] %% ListItems = [{Item,Items}|Item] -fl([], _Sep, I0, After, ST, WT) -> - [[f(After, I0, ST, WT),{[],0}]]; -fl(CItems, Sep0, I0, After, ST, WT) -> +fl([], _Sep, I0, After, ST, WT, PP) -> + [[f(After, I0, ST, WT, PP),{[],0}]]; +fl(CItems, Sep0, I0, After, ST, WT, PP) -> F = fun({step,Item1,Item2}, S) -> - [f(Item1, I0, ST, WT),f([Item2,S], incr(I0, ?IND), ST, WT)]; + [f(Item1, I0, ST, WT, PP), + f([Item2,S], incr(I0, ?IND), ST, WT, PP)]; ({cstep,Item1,Item2}, S) -> - {_,Sz1} = CharSize1 = f(Item1, I0, ST, WT), + {_,Sz1} = CharSize1 = f(Item1, I0, ST, WT, PP), if is_integer(Sz1), Sz1 < ?IND -> Item2p = [leaf("\s"),Item2,S], - [consecutive(Item2p, CharSize1, I0, ST, WT),{[],0}]; + [consecutive(Item2p, CharSize1, I0, ST, WT, PP),{[],0}]; true -> - [CharSize1,f([Item2,S], incr(I0, ?IND), ST, WT)] + [CharSize1,f([Item2,S], incr(I0, ?IND), ST, WT, PP)] end; (Item, S) -> - [f([Item,S], I0, ST, WT),{[],0}] + [f([Item,S], I0, ST, WT, PP),{[],0}] end, {Sep,LastSep} = case Sep0 of {_,_} -> Sep0; _ -> {Sep0,Sep0} end, fl1(CItems, F, Sep, LastSep, After). @@ -906,10 +935,10 @@ fl1([CItem1,CItem2], F, _Sep, LastSep, After) -> fl1([CItem|CItems], F, Sep, LastSep, After) -> [F(CItem, Sep)|fl1(CItems, F, Sep, LastSep, After)]. -consecutive(Items, CharSize1, I0, ST, WT) -> +consecutive(Items, CharSize1, I0, ST, WT, PP) -> {CharsSizes,_Length} = mapfoldl(fun(Item, Len) -> - CharsSize = f(Item, Len, ST, WT), + CharsSize = f(Item, Len, ST, WT, PP), {CharsSize,indent(CharsSize, Len)} end, indent(CharSize1, I0), Items), {CharsL,SizeL} = unz1([CharSize1|CharsSizes]), @@ -982,7 +1011,7 @@ incr(I, Incr) -> I+Incr. indentation(E, I) when I < 0 -> - iolist_size(E); + chars_size(E); indentation(E, I0) -> I = io_lib_format:indentation(E, I0), case has_nl(E) of @@ -999,30 +1028,53 @@ has_nl([C|Cs]) -> has_nl([]) -> false. +write_a_char(C, PP) -> + flat_leaf(write_char(C, PP)). + -define(MIN_SUBSTRING, 5). -write_a_string(S, I) when I < 0; S =:= [] -> - leaf(write_string(S)); -write_a_string(S, I) -> +write_a_string(S, I, PP) when I < 0; S =:= [] -> + flat_leaf(write_string(S, PP)); +write_a_string(S, I, PP) -> Len = erlang:max(?MAXLINE-I, ?MIN_SUBSTRING), - {list,write_a_string(S, Len, Len)}. + {list,write_a_string(S, Len, Len, PP)}. -write_a_string([], _N, _Len) -> +write_a_string([], _N, _Len, _PP) -> []; -write_a_string(S, N, Len) -> +write_a_string(S, N, Len, PP) -> SS = string:sub_string(S, 1, N), - Sl = write_string(SS), - case (iolist_size(Sl) > Len) and (N > ?MIN_SUBSTRING) of + Sl = write_string(SS, PP), + case (length(Sl) > Len) and (N > ?MIN_SUBSTRING) of true -> - write_a_string(S, N-1, Len); + write_a_string(S, N-1, Len, PP); false -> - [leaf(Sl)|write_a_string(lists:nthtail(length(SS), S), Len, Len)] + [flat_leaf(Sl) | + write_a_string(lists:nthtail(length(SS), S), Len, Len, PP)] end. +flat_leaf(S) -> + L = lists:flatten(S), + {leaf,length(L),L}. + +write_string(S, PP) -> + lists:flatten((PP#pp.string_fun)(S)). + +write_char(C, PP) -> + lists:flatten((PP#pp.char_fun)(C)). + %% %% Utilities %% +chars_size([C | Es]) when is_integer(C) -> + 1 + chars_size(Es); +chars_size([E | Es]) -> + chars_size(E) + chars_size(Es); +chars_size([]) -> + 0; +chars_size(B) when is_binary(B) -> + byte_size(B). + -define(N_SPACES, 30). spacetab() -> diff --git a/lib/stdlib/src/erl_scan.erl b/lib/stdlib/src/erl_scan.erl index 8e59e01f48..3651f608bc 100644 --- a/lib/stdlib/src/erl_scan.erl +++ b/lib/stdlib/src/erl_scan.erl @@ -1,7 +1,8 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2012. All Rights Reserved. +%% Copyright Ericsson AB 1996-2013. 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,19 +33,19 @@ %% 173 - 176 { - ~ punctuation %% 177 DEL control %% 200 - 237 control -%% 240 - 277 NBSP - � punctuation -%% 300 - 326 � - � uppercase -%% 327 � punctuation -%% 330 - 336 � - � uppercase -%% 337 - 366 � - � lowercase -%% 367 � punctuation -%% 370 - 377 � - � lowercase +%% 240 - 277 NBSP - ¿ punctuation +%% 300 - 326 À - Ö uppercase +%% 327 × punctuation +%% 330 - 336 Ø - Þ uppercase +%% 337 - 366 ß - ö lowercase +%% 367 ÷ punctuation +%% 370 - 377 ø - ÿ lowercase %% %% Many punctuation characters have special meaning: %% $\s, $_, $", $$, $%, $', $. %% DEL is a punctuation. %% -%% Must watch using � \327, very close to x \170. +%% Must watch using × \327, very close to x \170. -module(erl_scan). @@ -55,7 +56,16 @@ token_info/1,token_info/2, attributes_info/1,attributes_info/2,set_attribute/3]). --export_type([error_info/0, line/0, tokens_result/0]). +%%% Private +-export([continuation_location/1]). + +-export_type([error_info/0, + line/0, + location/0, + options/0, + return_cont/0, + token/0, + tokens_result/0]). %%% %%% Defines and type definitions @@ -104,7 +114,7 @@ format_error({string,Quote,Head}) -> lists:flatten(["unterminated " ++ string_thing(Quote) ++ " starting with " ++ - io_lib:write_unicode_string(Head, Quote)]); + io_lib:write_string(Head, Quote)]); format_error({illegal,Type}) -> lists:flatten(io_lib:fwrite("illegal ~w", [Type])); format_error(char) -> "unterminated character"; @@ -183,6 +193,11 @@ tokens({erl_scan_continuation,Cs,Col,Toks,Line,St,Any,Fun}, CharSpec, _Loc, _Opts) -> tokens1(Cs++CharSpec, St, Line, Col, Toks, Fun, Any). +continuation_location({erl_scan_continuation,_,no_col,_,Line,_,_,_}) -> + Line; +continuation_location({erl_scan_continuation,_,Col,_,Line,_,_,_}) -> + {Line,Col}. + -type attribute_item() :: 'column' | 'length' | 'line' | 'location' | 'text'. -type info_location() :: location() | term(). @@ -201,15 +216,14 @@ token_info(Token) -> Items = [category,column,length,line,symbol,text], % undefined order token_info(Token, Items). --spec token_info(Token, TokenItem) -> TokenInfo | 'undefined' when - Token :: token(), - TokenItem :: token_item(), - TokenInfo :: TokenInfoTuple :: token_info(); - (Token, TokenItems) -> [TokenInfo] when - Token :: token(), - TokenItems :: [TokenItem], - TokenItem :: token_item(), - TokenInfo :: [TokenInfoTuple :: token_info()]. +-spec token_info(Token, TokenItem) -> TokenInfoTuple | 'undefined' when + Token :: token(), + TokenItem :: token_item(), + TokenInfoTuple :: token_info(); + (Token, TokenItems) -> TokenInfo when + Token :: token(), + TokenItems :: [TokenItem :: token_item()], + TokenInfo :: [TokenInfoTuple :: token_info()]. token_info(_Token, []) -> []; token_info(Token, [Item|Items]) when is_atom(Item) -> @@ -239,16 +253,15 @@ attributes_info(Attributes) -> Items = [column,length,line,text], % undefined order attributes_info(Attributes, Items). --spec attributes_info(Attributes, AttributeItem) -> - AttributeInfo | 'undefined' when - Attributes :: attributes(), - AttributeItem :: attribute_item(), - AttributeInfo :: AttributeInfoTuple :: attribute_info(); - (Attributes, AttributeItems) -> [AttributeInfo] when - Attributes :: attributes(), - AttributeItems :: [AttributeItem], - AttributeItem :: attribute_item(), - AttributeInfo :: [AttributeInfoTuple :: attribute_info()]. +-spec attributes_info + (Attributes, AttributeItem) -> AttributeInfoTuple | 'undefined' when + Attributes :: attributes(), + AttributeItem :: attribute_item(), + AttributeInfoTuple :: attribute_info(); + (Attributes, AttributeItems) -> AttributeInfo when + Attributes :: attributes(), + AttributeItems :: [AttributeItem :: attribute_item()], + AttributeInfo :: [AttributeInfoTuple :: attribute_info()]. attributes_info(_Attrs, []) -> []; attributes_info(Attrs, [A|As]) when is_atom(A) -> @@ -324,13 +337,12 @@ string_thing(_) -> "string". (C >= $\000 andalso C =< $\s orelse C >= $\200 andalso C =< $\240)). -define(DIGIT(C), C >= $0, C =< $9). -define(CHAR(C), is_integer(C), C >= 0). +-define(UNICODE(C), + (C >= 0 andalso C < 16#D800 orelse + C > 16#DFFF andalso C < 16#FFFE orelse + C > 16#FFFF andalso C =< 16#10FFFF)). -%% A workaround: Unicode strings are not returned as strings, but as -%% lists of integers. For instance, "b\x{aaa}c" => [98,2730,99]. This -%% is to protect the system from character codes greater than 255. To -%% be removed. Search for UNI to find workaround code. --define(NO_UNICODE, 0). --define(UNI255(C), (C) =< 16#ff). +-define(UNI255(C), C >= 0, C =< 16#ff). options(Opts0) when is_list(Opts0) -> Opts = lists:foldr(fun expand_opt/2, [], Opts0), @@ -504,18 +516,18 @@ scan1("."=Cs, _St, Line, Col, Toks) -> scan1([$.=C|Cs], St, Line, Col, Toks) -> scan_dot(Cs, St, Line, Col, Toks, [C]); scan1([$"|Cs], St, Line, Col, Toks) -> %" Emacs - State0 = {[],[],Line,Col,?NO_UNICODE}, + State0 = {[],[],Line,Col}, scan_string(Cs, St, Line, incr_column(Col, 1), Toks, State0); scan1([$'|Cs], St, Line, Col, Toks) -> %' Emacs - State0 = {[],[],Line,Col,?NO_UNICODE}, + State0 = {[],[],Line,Col}, scan_qatom(Cs, St, Line, incr_column(Col, 1), Toks, State0); scan1([$$|Cs], St, Line, Col, Toks) -> scan_char(Cs, St, Line, Col, Toks); scan1([$\r|Cs], St, Line, Col, Toks) when St#erl_scan.ws -> white_space_end(Cs, St, Line, Col, Toks, 1, "\r"); -scan1([C|Cs], St, Line, Col, Toks) when C >= $�, C =< $�, C =/= $� -> +scan1([C|Cs], St, Line, Col, Toks) when C >= $ß, C =< $ÿ, C =/= $÷ -> scan_atom(Cs, St, Line, Col, Toks, [C]); -scan1([C|Cs], St, Line, Col, Toks) when C >= $�, C =< $�, C /= $� -> +scan1([C|Cs], St, Line, Col, Toks) when C >= $À, C =< $Þ, C /= $× -> scan_variable(Cs, St, Line, Col, Toks, [C]); scan1([$\t|Cs], St, Line, Col, Toks) when St#erl_scan.ws -> scan_tabs(Cs, St, Line, Col, Toks, 1); @@ -628,15 +640,12 @@ scan1([$~|Cs], St, Line, Col, Toks) -> scan1([$&|Cs], St, Line, Col, Toks) -> tok2(Cs, St, Line, Col, Toks, "&", '&', 1); %% End of optimization. -scan1([C|Cs], St, Line, Col, Toks) when ?CHAR(C) -> +scan1([C|Cs], St, Line, Col, Toks) when ?UNI255(C) -> Str = [C], - case catch list_to_atom(Str) of - Sym when is_atom(Sym) -> - tok2(Cs, St, Line, Col, Toks, Str, Sym, 1); - _ -> - Ncol = incr_column(Col, 1), - scan_error({illegal,character}, Line, Col, Line, Ncol, Cs) - end; + tok2(Cs, St, Line, Col, Toks, Str, list_to_atom(Str), 1); +scan1([C|Cs], _St, Line, Col, _Toks) when ?CHAR(C) -> + Ncol = incr_column(Col, 1), + scan_error({illegal,character}, Line, Col, Line, Ncol, Cs); scan1([]=Cs, _St, Line, Col, Toks) -> {more,{Cs,Col,Toks,Line,[],fun scan/6}}; scan1(eof=Cs, _St, Line, Col, Toks) -> @@ -685,23 +694,25 @@ scan_name([C|Cs], Ncs) when ?DIGIT(C) -> scan_name(Cs, [C|Ncs]); scan_name([$@=C|Cs], Ncs) -> scan_name(Cs, [C|Ncs]); -scan_name([C|Cs], Ncs) when C >= $�, C =< $�, C =/= $� -> +scan_name([C|Cs], Ncs) when C >= $ß, C =< $ÿ, C =/= $÷ -> scan_name(Cs, [C|Ncs]); -scan_name([C|Cs], Ncs) when C >= $�, C =< $�, C =/= $� -> +scan_name([C|Cs], Ncs) when C >= $À, C =< $Þ, C =/= $× -> scan_name(Cs, [C|Ncs]); scan_name([], Ncs) -> {more,Ncs}; scan_name(Cs, Ncs) -> {lists:reverse(Ncs),Cs}. +-define(STR(St, S), if St#erl_scan.text -> S; true -> [] end). + scan_dot([$%|_]=Cs, St, Line, Col, Toks, Ncs) -> Attrs = attributes(Line, Col, St, Ncs), {ok,[{dot,Attrs}|Toks],Cs,Line,incr_column(Col, 1)}; scan_dot([$\n=C|Cs], St, Line, Col, Toks, Ncs) -> - Attrs = attributes(Line, Col, St, Ncs++[C]), + Attrs = attributes(Line, Col, St, ?STR(St, Ncs++[C])), {ok,[{dot,Attrs}|Toks],Cs,Line+1,new_column(Col, 1)}; scan_dot([C|Cs], St, Line, Col, Toks, Ncs) when ?WHITE_SPACE(C) -> - Attrs = attributes(Line, Col, St, Ncs++[C]), + Attrs = attributes(Line, Col, St, ?STR(St, Ncs++[C])), {ok,[{dot,Attrs}|Toks],Cs,Line,incr_column(Col, 2)}; scan_dot(eof=Cs, St, Line, Col, Toks, Ncs) -> Attrs = attributes(Line, Col, St, Ncs), @@ -834,102 +845,53 @@ scan_char([$\\|Cs]=Cs0, St, Line, Col, Toks) -> {eof,Ncol} -> scan_error(char, Line, Col, Line, Ncol, eof); {nl,Val,Str,Ncs,Ncol} -> - Attrs = attributes(Line, Col, St, "$\\"++Str), + Attrs = attributes(Line, Col, St, ?STR(St, "$\\"++Str)), %" Ntoks = [{char,Attrs,Val}|Toks], scan1(Ncs, St, Line+1, Ncol, Ntoks); - {unicode,Val,Str,Ncs,Ncol} -> - Attrs = attributes(Line, Col, St, "$\\"++Str), - Ntoks = [{integer,Attrs,Val}|Toks], % UNI - scan1(Ncs, St, Line, Ncol, Ntoks); {Val,Str,Ncs,Ncol} -> - Attrs = attributes(Line, Col, St, "$\\"++Str), + Attrs = attributes(Line, Col, St, ?STR(St, "$\\"++Str)), %" Ntoks = [{char,Attrs,Val}|Toks], scan1(Ncs, St, Line, Ncol, Ntoks) end; scan_char([$\n=C|Cs], St, Line, Col, Toks) -> - Attrs = attributes(Line, Col, St, [$$,C]), + Attrs = attributes(Line, Col, St, ?STR(St, [$$,C])), scan1(Cs, St, Line+1, new_column(Col, 1), [{char,Attrs,C}|Toks]); -scan_char([C|Cs], St, Line, Col, Toks) when ?CHAR(C) -> - Tag = if ?UNI255(C) -> char; true -> integer end, % UNI - Attrs = attributes(Line, Col, St, [$$,C]), - scan1(Cs, St, Line, incr_column(Col, 2), [{Tag,Attrs,C}|Toks]); +scan_char([C|Cs], St, Line, Col, Toks) when ?UNICODE(C) -> + Attrs = attributes(Line, Col, St, ?STR(St, [$$,C])), + scan1(Cs, St, Line, incr_column(Col, 2), [{char,Attrs,C}|Toks]); +scan_char([C|_Cs], _St, Line, Col, _Toks) when ?CHAR(C) -> + scan_error({illegal,character}, Line, Col, Line, incr_column(Col, 1), eof); scan_char([], _St, Line, Col, Toks) -> {more,{[$$],Col,Toks,Line,[],fun scan/6}}; scan_char(eof, _St, Line, Col, _Toks) -> scan_error(char, Line, Col, Line, incr_column(Col, 1), eof). -scan_string(Cs, St, Line, Col, Toks, {Wcs,Str,Line0,Col0,Uni0}) -> - case scan_string0(Cs, St, Line, Col, $\", Str, Wcs, Uni0) of - {more,Ncs,Nline,Ncol,Nstr,Nwcs,Uni} -> - State = {Nwcs,Nstr,Line0,Col0,Uni}, +scan_string(Cs, St, Line, Col, Toks, {Wcs,Str,Line0,Col0}) -> + case scan_string0(Cs, St, Line, Col, $\", Str, Wcs) of %" + {more,Ncs,Nline,Ncol,Nstr,Nwcs} -> + State = {Nwcs,Nstr,Line0,Col0}, {more,{Ncs,Ncol,Toks,Nline,State,fun scan_string/6}}; {char_error,Ncs,Error,Nline,Ncol,EndCol} -> scan_error(Error, Nline, Ncol, Nline, EndCol, Ncs); {error,Nline,Ncol,Nwcs,Ncs} -> Estr = string:substr(Nwcs, 1, 16), % Expanded escape chars. - scan_error({string,$\",Estr}, Line0, Col0, Nline, Ncol, Ncs); - {Ncs,Nline,Ncol,Nstr,Nwcs,?NO_UNICODE} -> + scan_error({string,$\",Estr}, Line0, Col0, Nline, Ncol, Ncs); %" + {Ncs,Nline,Ncol,Nstr,Nwcs} -> Attrs = attributes(Line0, Col0, St, Nstr), - scan1(Ncs, St, Nline, Ncol, [{string,Attrs,Nwcs}|Toks]); - {Ncs,Nline,Ncol,Nstr,_Nwcs,_Uni} -> - Ntoks = unicode_string_to_list(Line0, Col0, St, Nstr, Toks), - scan1(Ncs, St, Nline, Ncol, Ntoks) + scan1(Ncs, St, Nline, Ncol, [{string,Attrs,Nwcs}|Toks]) end. -%% UNI -unicode_string_to_list(Line, Col, St, [$"=C|Nstr], Toks) -> %" Emacs - Paren = {'[',attributes(Line, Col, St, [C])}, - u2l(Nstr, Line, incr_column(Col, 1), St, [Paren|Toks]). - -u2l([$"]=Cs, Line, Col, St, Toks) -> %" Emacs - [{']',attributes(Line, Col, St, Cs)}|Toks]; -u2l([$\n=C|Cs], Line, Col, St, Toks) -> - Ntoks = unicode_nl_tokens(Line, Col, [C], C, St, Toks, Cs), - u2l(Cs, Line+1, new_column(Col, 1), St, Ntoks); -u2l([$\\|Cs], Line, Col, St, Toks) -> - case scan_escape(Cs, Col) of - {nl,Val,ValStr,Ncs,Ncol} -> - Nstr = [$\\|ValStr], - Ntoks = unicode_nl_tokens(Line, Col, Nstr, Val, St, Toks, Ncs), - u2l(Ncs, Line+1, Ncol, St, Ntoks); - {unicode,Val,ValStr,Ncs,Ncol} -> - Nstr = [$\\|ValStr], - Ntoks = unicode_tokens(Line, Col, Nstr, Val, St, Toks, Ncs), - u2l(Ncs, Line, incr_column(Ncol, 1), St, Ntoks); - {Val,ValStr,Ncs,Ncol} -> - Nstr = [$\\|ValStr], - Ntoks = unicode_tokens(Line, Col, Nstr, Val, St, Toks, Ncs), - u2l(Ncs, Line, incr_column(Ncol, 1), St, Ntoks) - end; -u2l([C|Cs], Line, Col, St, Toks) -> - Ntoks = unicode_tokens(Line, Col, [C], C, St, Toks, Cs), - u2l(Cs, Line, incr_column(Col, 1), St, Ntoks). - -unicode_nl_tokens(Line, Col, Str, Val, St, Toks, Cs) -> - Ccol = new_column(Col, 1), - unicode_tokens(Line, Col, Str, Val, St, Toks, Cs, Line+1, Ccol). - -unicode_tokens(Line, Col, Str, Val, St, Toks, Cs) -> - Ccol = incr_column(Col, length(Str)), - unicode_tokens(Line, Col, Str, Val, St, Toks, Cs, Line, Ccol). - -unicode_tokens(Line, Col, Str, Val, St, Toks, Cs, Cline, Ccol) -> - Attrs = attributes(Line, Col, St, Str), - Tag = if ?UNI255(Val) -> char; true -> integer end, - Token = {Tag,Attrs,Val}, - [{',',attributes(Cline, Ccol, St, "")} || Cs =/= "\""] ++ [Token|Toks]. - -scan_qatom(Cs, St, Line, Col, Toks, {Wcs,Str,Line0,Col0,Uni0}) -> - case scan_string0(Cs, St, Line, Col, $\', Str, Wcs, Uni0) of - {more,Ncs,Nline,Ncol,Nstr,Nwcs,Uni} -> - State = {Nwcs,Nstr,Line0,Col0,Uni}, +scan_qatom(Cs, St, Line, Col, Toks, {Wcs,Str,Line0,Col0}) -> + case scan_string0(Cs, St, Line, Col, $\', Str, Wcs) of %' + {more,Ncs,Nline,Ncol,Nstr,Nwcs} -> + State = {Nwcs,Nstr,Line0,Col0}, {more,{Ncs,Ncol,Toks,Nline,State,fun scan_qatom/6}}; {char_error,Ncs,Error,Nline,Ncol,EndCol} -> scan_error(Error, Nline, Ncol, Nline, EndCol, Ncs); {error,Nline,Ncol,Nwcs,Ncs} -> Estr = string:substr(Nwcs, 1, 16), % Expanded escape chars. - scan_error({string,$\',Estr}, Line0, Col0, Nline, Ncol, Ncs); - {Ncs,Nline,Ncol,Nstr,Nwcs,?NO_UNICODE} -> + scan_error({string,$\',Estr}, Line0, Col0, Nline, Ncol, Ncs); %' + {Ncs,Nline,Ncol,Nstr,Nwcs} -> case catch list_to_atom(Nwcs) of A when is_atom(A) -> Attrs = attributes(Line0, Col0, St, Nstr), @@ -939,91 +901,75 @@ scan_qatom(Cs, St, Line, Col, Toks, {Wcs,Str,Line0,Col0,Uni0}) -> end end. -scan_string0(Cs, #erl_scan{text=false}, Line, no_col=Col, Q, [], Wcs, Uni) -> - scan_string_no_col(Cs, Line, Col, Q, Wcs, Uni); -scan_string0(Cs, #erl_scan{text=true}, Line, no_col=Col, Q, Str, Wcs, Uni) -> - scan_string1(Cs, Line, Col, Q, Str, Wcs, Uni); -scan_string0(Cs, _St, Line, Col, Q, [], Wcs, Uni) -> - scan_string_col(Cs, Line, Col, Q, Wcs, Uni); -scan_string0(Cs, _St, Line, Col, Q, Str, Wcs, Uni) -> - scan_string1(Cs, Line, Col, Q, Str, Wcs, Uni). +scan_string0(Cs, #erl_scan{text=false}, Line, no_col=Col, Q, [], Wcs) -> + scan_string_no_col(Cs, Line, Col, Q, Wcs); +scan_string0(Cs, #erl_scan{text=true}, Line, no_col=Col, Q, Str, Wcs) -> + scan_string1(Cs, Line, Col, Q, Str, Wcs); +scan_string0(Cs, St, Line, Col, Q, [], Wcs) -> + scan_string_col(Cs, St, Line, Col, Q, Wcs); +scan_string0(Cs, _St, Line, Col, Q, Str, Wcs) -> + scan_string1(Cs, Line, Col, Q, Str, Wcs). %% Optimization. Col =:= no_col. -scan_string_no_col([Q|Cs], Line, Col, Q, Wcs, Uni) -> - {Cs,Line,Col,_DontCare=[],lists:reverse(Wcs),Uni}; -scan_string_no_col([$\n=C|Cs], Line, Col, Q, Wcs, Uni) -> - scan_string_no_col(Cs, Line+1, Col, Q, [C|Wcs], Uni); -scan_string_no_col([C|Cs], Line, Col, Q, Wcs, Uni) when C =/= $\\, - ?CHAR(C), ?UNI255(C) -> - scan_string_no_col(Cs, Line, Col, Q, [C|Wcs], Uni); -scan_string_no_col(Cs, Line, Col, Q, Wcs, Uni) -> - scan_string1(Cs, Line, Col, Q, Wcs, Wcs, Uni). +scan_string_no_col([Q|Cs], Line, Col, Q, Wcs) -> + {Cs,Line,Col,_DontCare=[],lists:reverse(Wcs)}; +scan_string_no_col([$\n=C|Cs], Line, Col, Q, Wcs) -> + scan_string_no_col(Cs, Line+1, Col, Q, [C|Wcs]); +scan_string_no_col([C|Cs], Line, Col, Q, Wcs) when C =/= $\\, ?UNICODE(C) -> + scan_string_no_col(Cs, Line, Col, Q, [C|Wcs]); +scan_string_no_col(Cs, Line, Col, Q, Wcs) -> + scan_string1(Cs, Line, Col, Q, Wcs, Wcs). %% Optimization. Col =/= no_col. -scan_string_col([Q|Cs], Line, Col, Q, Wcs0, Uni) -> +scan_string_col([Q|Cs], St, Line, Col, Q, Wcs0) -> Wcs = lists:reverse(Wcs0), - Str = [Q|Wcs++[Q]], - {Cs,Line,Col+1,Str,Wcs,Uni}; -scan_string_col([$\n=C|Cs], Line, _xCol, Q, Wcs, Uni) -> - scan_string_col(Cs, Line+1, 1, Q, [C|Wcs], Uni); -scan_string_col([C|Cs], Line, Col, Q, Wcs, Uni) when C =/= $\\, - ?CHAR(C), ?UNI255(C) -> - scan_string_col(Cs, Line, Col+1, Q, [C|Wcs], Uni); -scan_string_col(Cs, Line, Col, Q, Wcs, Uni) -> - scan_string1(Cs, Line, Col, Q, Wcs, Wcs, Uni). - -%% UNI_STR is to be replaced by STR when the Unicode-string-to-list -%% workaround is eventually removed. --define(UNI_STR(Col, S), S). + Str = ?STR(St, [Q|Wcs++[Q]]), + {Cs,Line,Col+1,Str,Wcs}; +scan_string_col([$\n=C|Cs], St, Line, _xCol, Q, Wcs) -> + scan_string_col(Cs, St, Line+1, 1, Q, [C|Wcs]); +scan_string_col([C|Cs], St, Line, Col, Q, Wcs) when C =/= $\\, ?UNICODE(C) -> + scan_string_col(Cs, St, Line, Col+1, Q, [C|Wcs]); +scan_string_col(Cs, _St, Line, Col, Q, Wcs) -> + scan_string1(Cs, Line, Col, Q, Wcs, Wcs). %% Note: in those cases when a 'char_error' tuple is returned below it %% is tempting to skip over characters up to the first Q character, %% but then the end location of the error tuple would not correspond %% to the start location of the returned Rest string. (Maybe the end %% location could be modified, but that too is ugly.) -scan_string1([Q|Cs], Line, Col, Q, Str0, Wcs0, Uni) -> +scan_string1([Q|Cs], Line, Col, Q, Str0, Wcs0) -> Wcs = lists:reverse(Wcs0), - Str = ?UNI_STR(Col, [Q|lists:reverse(Str0, [Q])]), - {Cs,Line,incr_column(Col, 1),Str,Wcs,Uni}; -scan_string1([$\n=C|Cs], Line, Col, Q, Str, Wcs, Uni) -> + Str = [Q|lists:reverse(Str0, [Q])], + {Cs,Line,incr_column(Col, 1),Str,Wcs}; +scan_string1([$\n=C|Cs], Line, Col, Q, Str, Wcs) -> Ncol = new_column(Col, 1), - scan_string1(Cs, Line+1, Ncol, Q, ?UNI_STR(Col, [C|Str]), [C|Wcs], Uni); -scan_string1([$\\|Cs]=Cs0, Line, Col, Q, Str, Wcs, Uni) -> + scan_string1(Cs, Line+1, Ncol, Q, [C|Str], [C|Wcs]); +scan_string1([$\\|Cs]=Cs0, Line, Col, Q, Str, Wcs) -> case scan_escape(Cs, Col) of more -> - {more,Cs0,Line,Col,Str,Wcs,Uni}; + {more,Cs0,Line,Col,Str,Wcs}; {error,Ncs,Error,Ncol} -> {char_error,Ncs,Error,Line,Col,incr_column(Ncol, 1)}; {eof,Ncol} -> {error,Line,incr_column(Ncol, 1),lists:reverse(Wcs),eof}; {nl,Val,ValStr,Ncs,Ncol} -> - Nstr = ?UNI_STR(Ncol, lists:reverse(ValStr, [$\\|Str])), + Nstr = lists:reverse(ValStr, [$\\|Str]), Nwcs = [Val|Wcs], - scan_string1(Ncs, Line+1, Ncol, Q, Nstr, Nwcs, Uni); - {unicode,_Val,_ValStr,Ncs,Ncol} when Q =:= $' -> %' Emacs - {char_error,Ncs,{illegal,character},Line,Col,incr_column(Ncol, 1)}; - {unicode,Val,ValStr,Ncs,Ncol} -> % UNI. Uni is set to Val. - Nstr = ?UNI_STR(Ncol, lists:reverse(ValStr, [$\\|Str])), - Nwcs = [Val|Wcs], % not used - scan_string1(Ncs, Line, incr_column(Ncol, 1), Q, Nstr, Nwcs, Val); + scan_string1(Ncs, Line+1, Ncol, Q, Nstr, Nwcs); {Val,ValStr,Ncs,Ncol} -> - Nstr = ?UNI_STR(Ncol, lists:reverse(ValStr, [$\\|Str])), + Nstr = lists:reverse(ValStr, [$\\|Str]), Nwcs = [Val|Wcs], - scan_string1(Ncs, Line, incr_column(Ncol, 1), Q, Nstr, Nwcs, Uni) + scan_string1(Ncs, Line, incr_column(Ncol, 1), Q, Nstr, Nwcs) end; -scan_string1([C|Cs], Line, no_col=Col, Q, Str, Wcs, Uni) when ?CHAR(C), - ?UNI255(C) -> - %% scan_string1(Cs, Line, Col, Q, Str, [C|Wcs], Uni); - scan_string1(Cs, Line, Col, Q, [C|Str], [C|Wcs], Uni); % UNI -scan_string1([C|Cs], Line, Col, Q, Str, Wcs, Uni) when ?CHAR(C), ?UNI255(C) -> - scan_string1(Cs, Line, Col+1, Q, [C|Str], [C|Wcs], Uni); -scan_string1([C|Cs], Line, Col, $', _Str, _Wcs, _Uni) when ?CHAR(C) -> %' UNI +scan_string1([C|Cs], Line, no_col=Col, Q, Str, Wcs) when ?UNICODE(C) -> + scan_string1(Cs, Line, Col, Q, [C|Str], [C|Wcs]); +scan_string1([C|Cs], Line, Col, Q, Str, Wcs) when ?UNICODE(C) -> + scan_string1(Cs, Line, Col+1, Q, [C|Str], [C|Wcs]); +scan_string1([C|Cs], Line, Col, _Q, _Str, _Wcs) when ?CHAR(C) -> {char_error,Cs,{illegal,character},Line,Col,incr_column(Col, 1)}; -scan_string1([C|Cs], Line, Col, Q, Str, Wcs, _Uni) when ?CHAR(C) -> % UNI - scan_string1(Cs, Line, incr_column(Col, 1), Q, [C|Str], [C|Wcs], C); -scan_string1([]=Cs, Line, Col, _Q, Str, Wcs, Uni) -> - {more,Cs,Line,Col,Str,Wcs,Uni}; -scan_string1(eof, Line, Col, _Q, _Str, Wcs, _Uni) -> +scan_string1([]=Cs, Line, Col, _Q, Str, Wcs) -> + {more,Cs,Line,Col,Str,Wcs}; +scan_string1(eof, Line, Col, _Q, _Str, Wcs) -> {error,Line,Col,lists:reverse(Wcs),eof}. -define(OCT(C), C >= $0, C =< $7). @@ -1034,16 +980,16 @@ scan_string1(eof, Line, Col, _Q, _Str, Wcs, _Uni) -> %% \<1-3> octal digits scan_escape([O1,O2,O3|Cs], Col) when ?OCT(O1), ?OCT(O2), ?OCT(O3) -> Val = (O1*8 + O2)*8 + O3 - 73*$0, - {Val,?UNI_STR(Col, [O1,O2,O3]),Cs,incr_column(Col, 3)}; + {Val,[O1,O2,O3],Cs,incr_column(Col, 3)}; scan_escape([O1,O2], _Col) when ?OCT(O1), ?OCT(O2) -> more; scan_escape([O1,O2|Cs], Col) when ?OCT(O1), ?OCT(O2) -> Val = (O1*8 + O2) - 9*$0, - {Val,?UNI_STR(Col, [O1,O2]),Cs,incr_column(Col, 2)}; + {Val,[O1,O2],Cs,incr_column(Col, 2)}; scan_escape([O1], _Col) when ?OCT(O1) -> more; scan_escape([O1|Cs], Col) when ?OCT(O1) -> - {O1 - $0,?UNI_STR(Col, [O1]),Cs,incr_column(Col, 1)}; + {O1 - $0,[O1],Cs,incr_column(Col, 1)}; %% \x{<hex digits>} scan_escape([$x,${|Cs], Col) -> scan_hex(Cs, incr_column(Col, 2), []); @@ -1054,28 +1000,28 @@ scan_escape([$x|eof], Col) -> %% \x<2> hexadecimal digits scan_escape([$x,H1,H2|Cs], Col) when ?HEX(H1), ?HEX(H2) -> Val = erlang:list_to_integer([H1,H2], 16), - {Val,?UNI_STR(Col, [$x,H1,H2]),Cs,incr_column(Col, 3)}; + {Val,[$x,H1,H2],Cs,incr_column(Col, 3)}; scan_escape([$x,H1], _Col) when ?HEX(H1) -> more; scan_escape([$x|Cs], Col) -> {error,Cs,{illegal,character},incr_column(Col, 1)}; %% \^X -> CTL-X scan_escape([$^=C0,$\n=C|Cs], Col) -> - {nl,C,?UNI_STR(Col, [C0,C]),Cs,new_column(Col, 1)}; + {nl,C,[C0,C],Cs,new_column(Col, 1)}; scan_escape([$^=C0,C|Cs], Col) when ?CHAR(C) -> Val = C band 31, - {Val,?UNI_STR(Col, [C0,C]),Cs,incr_column(Col, 2)}; + {Val,[C0,C],Cs,incr_column(Col, 2)}; scan_escape([$^], _Col) -> more; scan_escape([$^|eof], Col) -> {eof,incr_column(Col, 1)}; scan_escape([$\n=C|Cs], Col) -> - {nl,C,?UNI_STR(Col, [C]),Cs,new_column(Col, 1)}; -scan_escape([C0|Cs], Col) when ?CHAR(C0), ?UNI255(C0) -> + {nl,C,[C],Cs,new_column(Col, 1)}; +scan_escape([C0|Cs], Col) when ?UNICODE(C0) -> C = escape_char(C0), - {C,?UNI_STR(Col, [C0]),Cs,incr_column(Col, 1)}; -scan_escape([C|Cs], Col) when ?CHAR(C) -> % UNI - {unicode,C,?UNI_STR(Col, [C]),Cs,incr_column(Col, 1)}; + {C,[C0],Cs,incr_column(Col, 1)}; +scan_escape([C|Cs], Col) when ?CHAR(C) -> + {error,Cs,{illegal,character},incr_column(Col, 1)}; scan_escape([], _Col) -> more; scan_escape(eof, Col) -> @@ -1091,10 +1037,8 @@ scan_hex(Cs, Col, Wcs) -> scan_esc_end([$}|Cs], Col, Wcs0, B, Str0) -> Wcs = lists:reverse(Wcs0), case catch erlang:list_to_integer(Wcs, B) of - Val when Val =< 16#FF -> - {Val,?UNI_STR(Col, Str0++Wcs++[$}]),Cs,incr_column(Col, 1)}; - Val when Val =< 16#10FFFF -> - {unicode,Val,?UNI_STR(Col, Str0++Wcs++[$}]),Cs,incr_column(Col,1)}; + Val when ?UNICODE(Val) -> + {Val,Str0++Wcs++[$}],Cs,incr_column(Col, 1)}; _ -> {error,Cs,{illegal,character},incr_column(Col, 1)} end; @@ -1126,7 +1070,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 = Ncs++[$#], + Bcs = ?STR(St, Ncs++[$#]), scan_based_int(Cs, St, Line, Col, Toks, {B,[],Bcs}); B -> Len = length(Ncs), @@ -1159,7 +1103,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, Bcs++Ncs, N); + tok3(Cs, St, Line, Col, Toks, integer, ?STR(St, Bcs++Ncs), N); _ -> Len = length(Bcs)+length(Ncs), Ncol = incr_column(Col, Len), @@ -1200,14 +1144,26 @@ float_end(Cs, St, Line, Col, Toks, Ncs0) -> end. skip_comment([C|Cs], St, Line, Col, Toks, N) when C =/= $\n, ?CHAR(C) -> - skip_comment(Cs, St, Line, Col, Toks, N+1); + case ?UNICODE(C) of + true -> + skip_comment(Cs, St, Line, Col, Toks, N+1); + false -> + Ncol = incr_column(Col, N+1), + scan_error({illegal,character}, Line, Col, Line, Ncol, Cs) + end; skip_comment([]=Cs, _St, Line, Col, Toks, N) -> {more,{Cs,Col,Toks,Line,N,fun skip_comment/6}}; skip_comment(Cs, St, Line, Col, Toks, N) -> scan1(Cs, St, Line, incr_column(Col, N), Toks). scan_comment([C|Cs], St, Line, Col, Toks, Ncs) when C =/= $\n, ?CHAR(C) -> - scan_comment(Cs, St, Line, Col, Toks, [C|Ncs]); + case ?UNICODE(C) of + true -> + scan_comment(Cs, St, Line, Col, Toks, [C|Ncs]); + false -> + Ncol = incr_column(Col, length(Ncs)+1), + scan_error({illegal,character}, Line, Col, Line, Ncol, Cs) + end; scan_comment([]=Cs, _St, Line, Col, Toks, Ncs) -> {more,{Cs,Col,Toks,Line,Ncs,fun scan_comment/6}}; scan_comment(Cs, St, Line, Col, Toks, Ncs0) -> @@ -1345,7 +1301,6 @@ reserved_word('fun') -> true; reserved_word('if') -> true; reserved_word('let') -> true; reserved_word('of') -> true; -reserved_word('query') -> true; reserved_word('receive') -> true; reserved_word('when') -> true; reserved_word('bnot') -> true; diff --git a/lib/stdlib/src/erl_tar.erl b/lib/stdlib/src/erl_tar.erl index 306834e845..4b654833ed 100644 --- a/lib/stdlib/src/erl_tar.erl +++ b/lib/stdlib/src/erl_tar.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2011. All Rights Reserved. +%% Copyright Ericsson AB 1997-2013. 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 @@ -154,7 +154,7 @@ table(Name, Opts) -> t(Name) -> case table(Name) of {ok, List} -> - lists:foreach(fun(N) -> ok = io:format("~s\n", [N]) end, List); + lists:foreach(fun(N) -> ok = io:format("~ts\n", [N]) end, List); Error -> Error end. @@ -216,11 +216,11 @@ format_error(bad_header) -> "Bad directory header"; format_error(eof) -> "Unexpected end of file"; format_error(symbolic_link_too_long) -> "Symbolic link too long"; format_error({Name,Reason}) -> - lists:flatten(io_lib:format("~s: ~s", [Name,format_error(Reason)])); + lists:flatten(io_lib:format("~ts: ~ts", [Name,format_error(Reason)])); format_error(Atom) when is_atom(Atom) -> file:format_error(Atom); format_error(Term) -> - lists:flatten(io_lib:format("~p", [Term])). + lists:flatten(io_lib:format("~tp", [Term])). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -325,13 +325,13 @@ add1(TarFile, Name, NameInArchive, Opts) -> end. add1(Tar, Name, Header, Bin, Options) -> - add_verbose(Options, "a ~s~n", [Name]), + add_verbose(Options, "a ~ts~n", [Name]), file:write(Tar, [Header, Bin, padding(byte_size(Bin), ?record_size)]). add_directory(TarFile, DirName, NameInArchive, Info, Options) -> case file:list_dir(DirName) of {ok, []} -> - add_verbose(Options, "a ~s~n", [DirName]), + add_verbose(Options, "a ~ts~n", [DirName]), Header = create_header(NameInArchive, Info), file:write(TarFile, Header); {ok, Files} -> @@ -731,7 +731,7 @@ write_extracted_element(Header, Bin, Opts) -> symlink -> create_symlink(Name, Header, Opts); Other -> % Ignore. - read_verbose(Opts, "x ~s - unsupported type ~p~n", + read_verbose(Opts, "x ~ts - unsupported type ~p~n", [Name, Other]), not_written end, @@ -757,7 +757,7 @@ create_symlink(Name, #tar_header{linkname=Linkname}=Header, Opts) -> create_symlink(Name, Header, Opts); {error,eexist} -> not_written; {error,enotsup} -> - read_verbose(Opts, "x ~s - symbolic links not supported~n", [Name]), + read_verbose(Opts, "x ~ts - symbolic links not supported~n", [Name]), not_written; {error,Reason} -> throw({error, Reason}) end. @@ -774,10 +774,10 @@ write_extracted_file(Name, Bin, Opts) -> end, case Write of true -> - read_verbose(Opts, "x ~s~n", [Name]), + read_verbose(Opts, "x ~ts~n", [Name]), write_file(Name, Bin); false -> - read_verbose(Opts, "x ~s - exists, not created~n", [Name]), + read_verbose(Opts, "x ~ts - exists, not created~n", [Name]), not_written end. diff --git a/lib/stdlib/src/escript.erl b/lib/stdlib/src/escript.erl index 498d850df3..32742e419b 100644 --- a/lib/stdlib/src/escript.erl +++ b/lib/stdlib/src/escript.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2012. All Rights Reserved. +%% Copyright Ericsson AB 2007-2013. 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 @@ -624,7 +624,7 @@ parse_source(S, File, Fd, StartLine, HeaderSz, CheckOnly) -> ok = file:close(Fd), check_source(S3, CheckOnly); {error, Reason} -> - io:format("escript: ~p\n", [Reason]), + io:format("escript: ~tp\n", [Reason]), fatal("Preprocessor error") end. @@ -694,7 +694,7 @@ epp_parse_file2(Epp, S, Forms, Parsed) -> epp_parse_file(Epp, S2, [Form | Forms]); true -> Args = lists:flatten(io_lib:format("illegal mode attribute: ~p", [NewMode])), - io:format("~s:~w ~s\n", [S#state.file,Ln,Args]), + io:format("~ts:~w ~s\n", [S#state.file,Ln,Args]), Error = {error,{Ln,erl_parse,Args}}, Nerrs= S#state.n_errors + 1, epp_parse_file(Epp, S2#state{n_errors = Nerrs}, [Error | Forms]) @@ -710,7 +710,7 @@ epp_parse_file2(Epp, S, Forms, Parsed) -> epp_parse_file(Epp, S, [Form | Forms]) end; {error,{Ln,Mod,Args}} = Form -> - io:format("~s:~w: ~s\n", + io:format("~ts:~w: ~ts\n", [S#state.file,Ln,Mod:format_error(Args)]), epp_parse_file(Epp, S#state{n_errors = S#state.n_errors + 1}, [Form | Forms]); {eof, _LastLine} = Eof -> @@ -780,10 +780,10 @@ report_errors(Errors) -> Errors). list_errors(F, [{Line,Mod,E}|Es]) -> - io:fwrite("~s:~w: ~s\n", [F,Line,Mod:format_error(E)]), + io:fwrite("~ts:~w: ~ts\n", [F,Line,Mod:format_error(E)]), list_errors(F, Es); list_errors(F, [{Mod,E}|Es]) -> - io:fwrite("~s: ~s\n", [F,Mod:format_error(E)]), + io:fwrite("~ts: ~ts\n", [F,Mod:format_error(E)]), list_errors(F, Es); list_errors(_F, []) -> ok. @@ -795,10 +795,10 @@ report_warnings(Ws0) -> lists:foreach(fun({_,Str}) -> io:put_chars(Str) end, Ws). format_message(F, [{Line,Mod,E}|Es]) -> - M = {{F,Line},io_lib:format("~s:~w: Warning: ~s\n", [F,Line,Mod:format_error(E)])}, + M = {{F,Line},io_lib:format("~ts:~w: Warning: ~ts\n", [F,Line,Mod:format_error(E)])}, [M|format_message(F, Es)]; format_message(F, [{Mod,E}|Es]) -> - M = {none,io_lib:format("~s: Warning: ~s\n", [F,Mod:format_error(E)])}, + M = {none,io_lib:format("~ts: Warning: ~ts\n", [F,Mod:format_error(E)])}, [M|format_message(F, Es)]; format_message(_, []) -> []. @@ -851,12 +851,27 @@ eval_exprs([E|Es], Bs0, Lf, Ef, RBs) -> eval_exprs(Es, Bs, Lf, Ef, RBs). format_exception(Class, Reason) -> + Enc = encoding(), + P = case Enc of + latin1 -> "P"; + _ -> "tP" + end, PF = fun(Term, I) -> - io_lib:format("~." ++ integer_to_list(I) ++ "P", [Term, 50]) + io_lib:format("~." ++ integer_to_list(I) ++ P, [Term, 50]) end, StackTrace = erlang:get_stacktrace(), StackFun = fun(M, _F, _A) -> (M =:= erl_eval) or (M =:= ?MODULE) end, - lib:format_exception(1, Class, Reason, StackTrace, StackFun, PF). + lib:format_exception(1, Class, Reason, StackTrace, StackFun, PF, Enc). + +encoding() -> + [{encoding, Encoding}] = enc(), + Encoding. + +enc() -> + case lists:keyfind(encoding, 1, io:getopts()) of + false -> [{encoding,latin1}]; % should never happen + Enc -> [Enc] + end. fatal(Str) -> throw(Str). diff --git a/lib/stdlib/src/ets.erl b/lib/stdlib/src/ets.erl index afa914a456..77c8029f59 100644 --- a/lib/stdlib/src/ets.erl +++ b/lib/stdlib/src/ets.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2013. 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 @@ -42,11 +42,16 @@ -export([i/0, i/1, i/2, i/3]). --export_type([tab/0, tid/0, match_spec/0]). +-export_type([tab/0, tid/0, match_spec/0, comp_match_spec/0, match_pattern/0]). %%----------------------------------------------------------------------------- +-type access() :: public | protected | private. -type tab() :: atom() | tid(). +-type type() :: set | ordered_set | bag | duplicate_bag. +-type continuation() :: '$end_of_table' + | {tab(),integer(),integer(),binary(),list(),integer()} + | {tab(),_,_,integer(),binary(),list(),integer(),integer()}. %% a similar definition is also in erl_types -opaque tid() :: integer(). @@ -57,59 +62,398 @@ %%----------------------------------------------------------------------------- -%% The following functions used to be found in this module, but -%% are now BIFs (i.e. implemented in C). -%% -%% all/0 -%% new/2 -%% delete/1 -%% delete/2 -%% first/1 -%% info/1 -%% info/2 -%% safe_fixtable/2 -%% lookup/2 -%% lookup_element/3 -%% insert/2 -%% is_compiled_ms/1 -%% last/1 -%% member/2 -%% next/2 -%% prev/2 -%% rename/2 -%% slot/2 -%% match/1 -%% match/2 -%% match/3 -%% match_object/1 -%% match_object/2 -%% match_object/3 -%% match_spec_compile/1 -%% match_spec_run_r/3 -%% select/1 -%% select/2 -%% select/3 -%% select_count/2 -%% select_reverse/1 -%% select_reverse/2 -%% select_reverse/3 -%% select_delete/2 -%% setopts/2 -%% update_counter/3 -%% update_element/3 -%% +%%% BIFs + +-export([all/0, delete/1, delete/2, delete_all_objects/1, + delete_object/2, first/1, give_away/3, info/1, info/2, + insert/2, insert_new/2, is_compiled_ms/1, last/1, lookup/2, + lookup_element/3, match/1, match/2, match/3, match_object/1, + match_object/2, match_object/3, match_spec_compile/1, + match_spec_run_r/3, member/2, new/2, next/2, prev/2, + rename/2, safe_fixtable/2, select/1, select/2, select/3, + select_count/2, select_delete/2, select_reverse/1, + select_reverse/2, select_reverse/3, setopts/2, slot/2, + update_counter/3, update_element/3]). + +-spec all() -> [Tab] when + Tab :: tab(). + +all() -> + erlang:nif_error(undef). + +-spec delete(Tab) -> true when + Tab :: tab(). + +delete(_) -> + erlang:nif_error(undef). + +-spec delete(Tab, Key) -> true when + Tab :: tab(), + Key :: term(). + +delete(_, _) -> + erlang:nif_error(undef). + +-spec delete_all_objects(Tab) -> true when + Tab :: tab(). + +delete_all_objects(_) -> + erlang:nif_error(undef). + +-spec delete_object(Tab, Object) -> true when + Tab :: tab(), + Object :: tuple(). + +delete_object(_, _) -> + erlang:nif_error(undef). + +-spec first(Tab) -> Key | '$end_of_table' when + Tab :: tab(), + Key :: term(). + +first(_) -> + erlang:nif_error(undef). + +-spec give_away(Tab, Pid, GiftData) -> true when + Tab :: tab(), + Pid :: pid(), + GiftData :: term(). + +give_away(_, _, _) -> + erlang:nif_error(undef). + +-spec info(Tab) -> InfoList | undefined when + Tab :: tab(), + InfoList :: [InfoTuple], + InfoTuple :: {compressed, boolean()} + | {heir, pid() | none} + | {keypos, pos_integer()} + | {memory, non_neg_integer()} + | {name, atom()} + | {named_table, boolean()} + | {node, node()} + | {owner, pid()} + | {protection, access()} + | {size, non_neg_integer()} + | {type, type()}. + +info(_) -> + erlang:nif_error(undef). + +-spec info(Tab, Item) -> Value | undefined when + Tab :: tab(), + Item :: compressed | fixed | heir | keypos | memory + | name | named_table | node | owner | protection + | safe_fixed | size | stats | type, + Value :: term(). + +info(_, _) -> + erlang:nif_error(undef). + +-spec insert(Tab, ObjectOrObjects) -> true when + Tab :: tab(), + ObjectOrObjects :: tuple() | [tuple()]. + +insert(_, _) -> + erlang:nif_error(undef). + +-spec insert_new(Tab, ObjectOrObjects) -> boolean() when + Tab :: tab(), + ObjectOrObjects :: tuple() | [tuple()]. + +insert_new(_, _) -> + erlang:nif_error(undef). + +-spec is_compiled_ms(Term) -> boolean() when + Term :: term(). + +is_compiled_ms(_) -> + erlang:nif_error(undef). + +-spec last(Tab) -> Key | '$end_of_table' when + Tab :: tab(), + Key :: term(). + +last(_) -> + erlang:nif_error(undef). + +-spec lookup(Tab, Key) -> [Object] when + Tab :: tab(), + Key :: term(), + Object :: tuple(). + +lookup(_, _) -> + erlang:nif_error(undef). + +-spec lookup_element(Tab, Key, Pos) -> Elem when + Tab :: tab(), + Key :: term(), + Pos :: pos_integer(), + Elem :: term() | [term()]. + +lookup_element(_, _, _) -> + erlang:nif_error(undef). + +-spec match(Tab, Pattern) -> [Match] when + Tab :: tab(), + Pattern :: match_pattern(), + Match :: [term()]. + +match(_, _) -> + erlang:nif_error(undef). + +-spec match(Tab, Pattern, Limit) -> {[Match], Continuation} | + '$end_of_table' when + Tab :: tab(), + Pattern :: match_pattern(), + Limit :: pos_integer(), + Match :: [term()], + Continuation :: continuation(). + +match(_, _, _) -> + erlang:nif_error(undef). + +-spec match(Continuation) -> {[Match], Continuation} | + '$end_of_table' when + Match :: [term()], + Continuation :: continuation(). + +match(_) -> + erlang:nif_error(undef). + +-spec match_object(Tab, Pattern) -> [Object] when + Tab :: tab(), + Pattern :: match_pattern(), + Object :: tuple(). + +match_object(_, _) -> + erlang:nif_error(undef). + +-spec match_object(Tab, Pattern, Limit) -> {[Match], Continuation} | + '$end_of_table' when + Tab :: tab(), + Pattern :: match_pattern(), + Limit :: pos_integer(), + Match :: [term()], + Continuation :: continuation(). + +match_object(_, _, _) -> + erlang:nif_error(undef). + +-spec match_object(Continuation) -> {[Match], Continuation} | + '$end_of_table' when + Match :: [term()], + Continuation :: continuation(). + +match_object(_) -> + erlang:nif_error(undef). + +-spec match_spec_compile(MatchSpec) -> CompiledMatchSpec when + MatchSpec :: match_spec(), + CompiledMatchSpec :: comp_match_spec(). --opaque comp_match_spec() :: any(). %% this one is REALLY opaque +match_spec_compile(_) -> + erlang:nif_error(undef). --spec match_spec_run([tuple()], comp_match_spec()) -> [term()]. +-spec match_spec_run_r(List, CompiledMatchSpec, list()) -> list() when + List :: [tuple()], + CompiledMatchSpec :: comp_match_spec(). + +match_spec_run_r(_, _, _) -> + erlang:nif_error(undef). + +-spec member(Tab, Key) -> boolean() when + Tab :: tab(), + Key :: term(). + +member(_, _) -> + erlang:nif_error(undef). + +-spec new(Name, Options) -> tid() | atom() when + Name :: atom(), + Options :: [Option], + Option :: Type | Access | named_table | {keypos,Pos} + | {heir, Pid :: pid(), HeirData} | {heir, none} | Tweaks, + Type :: type(), + Access :: access(), + Tweaks :: {write_concurrency, boolean()} + | {read_concurrency, boolean()} + | compressed, + Pos :: pos_integer(), + HeirData :: term(). + +new(_, _) -> + erlang:nif_error(undef). + +-spec next(Tab, Key1) -> Key2 | '$end_of_table' when + Tab :: tab(), + Key1 :: term(), + Key2 :: term(). + +next(_, _) -> + erlang:nif_error(undef). + +-spec prev(Tab, Key1) -> Key2 | '$end_of_table' when + Tab :: tab(), + Key1 :: term(), + Key2 :: term(). + +prev(_, _) -> + erlang:nif_error(undef). + +%% Shadowed by erl_bif_types: ets:rename/2 +-spec rename(Tab, Name) -> Name when + Tab :: tab(), + Name :: atom(). + +rename(_, _) -> + erlang:nif_error(undef). + +-spec safe_fixtable(Tab, Fix) -> true when + Tab :: tab(), + Fix :: boolean(). + +safe_fixtable(_, _) -> + erlang:nif_error(undef). + +-spec select(Tab, MatchSpec) -> [Match] when + Tab :: tab(), + MatchSpec :: match_spec(), + Match :: term(). + +select(_, _) -> + erlang:nif_error(undef). + +-spec select(Tab, MatchSpec, Limit) -> {[Match],Continuation} | + '$end_of_table' when + Tab :: tab(), + MatchSpec :: match_spec(), + Limit :: pos_integer(), + Match :: term(), + Continuation :: continuation(). + +select(_, _, _) -> + erlang:nif_error(undef). + +-spec select(Continuation) -> {[Match],Continuation} | '$end_of_table' when + Match :: term(), + Continuation :: continuation(). + +select(_) -> + erlang:nif_error(undef). + +-spec select_count(Tab, MatchSpec) -> NumMatched when + Tab :: tab(), + MatchSpec :: match_spec(), + NumMatched :: non_neg_integer(). + +select_count(_, _) -> + erlang:nif_error(undef). + +-spec select_delete(Tab, MatchSpec) -> NumDeleted when + Tab :: tab(), + MatchSpec :: match_spec(), + NumDeleted :: non_neg_integer(). + +select_delete(_, _) -> + erlang:nif_error(undef). + +-spec select_reverse(Tab, MatchSpec) -> [Match] when + Tab :: tab(), + MatchSpec :: match_spec(), + Match :: term(). + +select_reverse(_, _) -> + erlang:nif_error(undef). + +-spec select_reverse(Tab, MatchSpec, Limit) -> {[Match],Continuation} | + '$end_of_table' when + Tab :: tab(), + MatchSpec :: match_spec(), + Limit :: pos_integer(), + Match :: term(), + Continuation :: continuation(). + +select_reverse(_, _, _) -> + erlang:nif_error(undef). + +-spec select_reverse(Continuation) -> {[Match],Continuation} | + '$end_of_table' when + Continuation :: continuation(), + Match :: term(). + +select_reverse(_) -> + erlang:nif_error(undef). + +-spec setopts(Tab, Opts) -> true when + Tab :: tab(), + Opts :: Opt | [Opt], + Opt :: {heir, pid(), HeirData} | {heir,none}, + HeirData :: term(). + +setopts(_, _) -> + erlang:nif_error(undef). + +-spec slot(Tab, I) -> [Object] | '$end_of_table' when + Tab :: tab(), + I :: non_neg_integer(), + Object :: tuple(). + +slot(_, _) -> + erlang:nif_error(undef). + +-spec update_counter(Tab, Key, UpdateOp) -> Result when + Tab :: tab(), + Key :: term(), + UpdateOp :: {Pos, Incr} | {Pos, Incr, Threshold, SetValue}, + Pos :: integer(), + Incr :: integer(), + Threshold :: integer(), + SetValue :: integer(), + Result :: integer(); + (Tab, Key, [UpdateOp]) -> [Result] when + Tab :: tab(), + Key :: term(), + UpdateOp :: {Pos, Incr} | {Pos, Incr, Threshold, SetValue}, + Pos :: integer(), + Incr :: integer(), + Threshold :: integer(), + SetValue :: integer(), + Result :: integer(); + (Tab, Key, Incr) -> Result when + Tab :: tab(), + Key :: term(), + Incr :: integer(), + Result :: integer(). + +update_counter(_, _, _) -> + erlang:nif_error(undef). + +-spec update_element(Tab, Key, ElementSpec :: {Pos, Value}) -> boolean() when + Tab :: tab(), + Key :: term(), + Pos :: pos_integer(), + Value :: term(); + (Tab, Key, ElementSpec :: [{Pos, Value}]) -> boolean() when + Tab :: tab(), + Key :: term(), + Pos :: pos_integer(), + Value :: term(). + +update_element(_, _, _) -> + erlang:nif_error(undef). + +%%% End of BIFs + +-opaque comp_match_spec() :: binary(). %% this one is REALLY opaque + +-spec match_spec_run(List, CompiledMatchSpec) -> list() when + List :: [tuple()], + CompiledMatchSpec :: comp_match_spec(). match_spec_run(List, CompiledMS) -> lists:reverse(ets:match_spec_run_r(List, CompiledMS, [])). --type continuation() :: '$end_of_table' - | {tab(),integer(),integer(),binary(),list(),integer()} - | {tab(),_,_,integer(),binary(),list(),integer(),integer()}. - -spec repair_continuation(Continuation, MatchSpec) -> Continuation when Continuation :: continuation(), MatchSpec :: match_spec(). @@ -157,7 +501,7 @@ fun2ms(ShellFun) when is_function(ShellFun) -> case ms_transform:transform_from_shell( ?MODULE,Clauses,ImportList) of {error,[{_,[{_,_,Code}|_]}|_],_} -> - io:format("Error: ~s~n", + io:format("Error: ~ts~n", [ms_transform:format_error(Code)]), {error,transform_error}; Else -> @@ -1242,7 +1586,7 @@ choice(Height, Width, P, Mode, Tab, Key, Turn, Opos) -> {ok,Re} -> re_search(Height, Width, Tab, ets:first(Tab), Re, 1, 1); {error,{ErrorString,_Pos}} -> - io:format("~s\n", [ErrorString]), + io:format("~ts\n", [ErrorString]), choice(Height, Width, P, Mode, Tab, Key, Turn, Opos) end; _ -> diff --git a/lib/stdlib/src/eval_bits.erl b/lib/stdlib/src/eval_bits.erl index f40904df1c..e49cbc1fd1 100644 --- a/lib/stdlib/src/eval_bits.erl +++ b/lib/stdlib/src/eval_bits.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2011. All Rights Reserved. +%% Copyright Ericsson AB 1999-2013. 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 @@ -67,7 +67,8 @@ expr_grp([], Bs0, _Lf, Acc) -> {value,Acc,Bs0}. eval_field({bin_element, _, {string, _, S}, default, default}, Bs0, _Fun) -> - {list_to_binary(S),Bs0}; + Latin1 = [C band 16#FF || C <- S], + {list_to_binary(Latin1),Bs0}; eval_field({bin_element, Line, {string, _, S}, Size0, Options0}, Bs, _Fun) -> {_Size,[Type,_Unit,_Sign,Endian]} = make_bit_type(Line, Size0, Options0), @@ -162,8 +163,10 @@ bin_gen([], Bin, _Bs0, _BBs0, _Mfun, _Efun, false) -> bin_gen_field({bin_element,_,{string,_,S},default,default}, Bin, Bs, BBs, _Mfun, _Efun) -> - Bits = list_to_binary(S), - Size = byte_size(Bits), + Bits = try list_to_binary(S) + catch _:_ -> <<>> + end, + Size = length(S), case Bin of <<Bits:Size/binary,Rest/bitstring>> -> {match,Bs,BBs,Rest}; @@ -172,16 +175,42 @@ bin_gen_field({bin_element,_,{string,_,S},default,default}, _ -> done end; +bin_gen_field({bin_element,Line,{string,SLine,S},Size0,Options0}, + Bin0, Bs0, BBs0, Mfun, Efun) -> + {Size1, [Type,{unit,Unit},Sign,Endian]} = + make_bit_type(Line, Size0, Options0), + match_check_size(Mfun, Size1, BBs0), + {value, Size, _BBs} = Efun(Size1, BBs0), + F = fun(C, Bin, Bs, BBs) -> + bin_gen_field1(Bin, Type, Size, Unit, Sign, Endian, + {integer,SLine,C}, Bs, BBs, Mfun) + end, + bin_gen_field_string(S, Bin0, Bs0, BBs0, F); bin_gen_field({bin_element,Line,VE,Size0,Options0}, Bin, Bs0, BBs0, Mfun, Efun) -> {Size1, [Type,{unit,Unit},Sign,Endian]} = make_bit_type(Line, Size0, Options0), V = erl_eval:partial_eval(VE), + NewV = coerce_to_float(V, Type), match_check_size(Mfun, Size1, BBs0), {value, Size, _BBs} = Efun(Size1, BBs0), + bin_gen_field1(Bin, Type, Size, Unit, Sign, Endian, NewV, Bs0, BBs0, Mfun). + +bin_gen_field_string([], Rest, Bs, BBs, _F) -> + {match,Bs,BBs,Rest}; +bin_gen_field_string([C|Cs], Bin0, Bs0, BBs0, Fun) -> + case Fun(C, Bin0, Bs0, BBs0) of + {match,Bs,BBs,Rest} -> + bin_gen_field_string(Cs, Rest, Bs, BBs, Fun); + {nomatch,Rest} -> + {nomatch,Rest}; + done -> + done + end. + +bin_gen_field1(Bin, Type, Size, Unit, Sign, Endian, NewV, Bs0, BBs0, Mfun) -> case catch get_value(Bin, Type, Size, Unit, Sign, Endian) of {Val,<<_/bitstring>>=Rest} -> - NewV = coerce_to_float(V, Type), case catch Mfun(match, {NewV,Val,Bs0}) of {match,Bs} -> BBs = add_bin_binding(Mfun, NewV, Bs, BBs0), @@ -223,20 +252,41 @@ match_bits_1([F|Fs], Bits0, Bs0, BBs0, Mfun, Efun) -> match_field_1({bin_element,_,{string,_,S},default,default}, Bin, Bs, BBs, _Mfun, _Efun) -> - Bits = list_to_binary(S), + Bits = list_to_binary(S), % fails if there are characters > 255 Size = byte_size(Bits), <<Bits:Size/binary,Rest/binary-unit:1>> = Bin, {Bs,BBs,Rest}; +match_field_1({bin_element,Line,{string,SLine,S},Size0,Options0}, + Bin0, Bs0, BBs0, Mfun, Efun) -> + {Size1, [Type,{unit,Unit},Sign,Endian]} = + make_bit_type(Line, Size0, Options0), + Size2 = erl_eval:partial_eval(Size1), + match_check_size(Mfun, Size2, BBs0), + {value, Size, _BBs} = Efun(Size2, BBs0), + F = fun(C, Bin, Bs, BBs) -> + match_field(Bin, Type, Size, Unit, Sign, Endian, + {integer,SLine,C}, Bs, BBs, Mfun) + end, + match_field_string(S, Bin0, Bs0, BBs0, F); match_field_1({bin_element,Line,VE,Size0,Options0}, Bin, Bs0, BBs0, Mfun, Efun) -> {Size1, [Type,{unit,Unit},Sign,Endian]} = make_bit_type(Line, Size0, Options0), V = erl_eval:partial_eval(VE), + NewV = coerce_to_float(V, Type), Size2 = erl_eval:partial_eval(Size1), match_check_size(Mfun, Size2, BBs0), {value, Size, _BBs} = Efun(Size2, BBs0), + match_field(Bin, Type, Size, Unit, Sign, Endian, NewV, Bs0, BBs0, Mfun). + +match_field_string([], Rest, Bs, BBs, _Fun) -> + {Bs,BBs,Rest}; +match_field_string([C|Cs], Bin0, Bs0, BBs0, Fun) -> + {Bs,BBs,Bin} = Fun(C, Bin0, Bs0, BBs0), + match_field_string(Cs, Bin, Bs, BBs, Fun). + +match_field(Bin, Type, Size, Unit, Sign, Endian, NewV, Bs0, BBs0, Mfun) -> {Val,Rest} = get_value(Bin, Type, Size, Unit, Sign, Endian), - NewV = coerce_to_float(V, Type), {match,Bs} = Mfun(match, {NewV,Val,Bs0}), BBs = add_bin_binding(Mfun, NewV, Bs, BBs0), {Bs,BBs,Rest}. diff --git a/lib/stdlib/src/file_sorter.erl b/lib/stdlib/src/file_sorter.erl index 3f31852afc..2bf88959b7 100644 --- a/lib/stdlib/src/file_sorter.erl +++ b/lib/stdlib/src/file_sorter.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2011. All Rights Reserved. +%% Copyright Ericsson AB 2001-2013. 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 @@ -633,7 +633,7 @@ last_merge(R, W) when length(R) =< W#w.no_files -> case W#w.out of Fun when is_function(Fun) -> {Fs, W1} = init_merge(lists:reverse(R), 1, [], W), - ?DEBUG("merging ~p~n", [lists:reverse(R)]), + ?DEBUG("merging ~tp~n", [lists:reverse(R)]), W2 = merge_files(Fs, [], 0, nolast, W1), NW = close_input(W2), outfun(close, NW); @@ -659,7 +659,7 @@ merge_runs([R, R1 | Rs], NRs0, W) -> merge_files(R, W) -> {W1, Temp} = next_temp(W), - ?DEBUG("merging ~p~nto ~p~n", [lists:reverse(R), Temp]), + ?DEBUG("merging ~tp~nto ~tp~n", [lists:reverse(R), Temp]), {Temp, merge_files(R, W1, Temp)}. merge_files(R, W, FileName) -> @@ -1501,7 +1501,7 @@ close_out(_) -> close_file(Fd, W) -> {Fd, FileName} = lists:keyfind(Fd, 1, W#w.temp), - ?DEBUG("closing ~p~n", [FileName]), + ?DEBUG("closing ~tp~n", [FileName]), file:close(Fd), W#w{temp = [FileName | lists:keydelete(Fd, 1, W#w.temp)]}. diff --git a/lib/stdlib/src/filelib.erl b/lib/stdlib/src/filelib.erl index fa4f92617c..42ef3679a2 100644 --- a/lib/stdlib/src/filelib.erl +++ b/lib/stdlib/src/filelib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2012. All Rights Reserved. +%% Copyright Ericsson AB 1997-2013. 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 @@ -19,16 +19,14 @@ -module(filelib). %% File utilities. - -%% Avoid warning for local function error/1 clashing with autoimported BIF. --compile({no_auto_import,[error/1]}). --export([wildcard/1, wildcard/2, is_dir/1, is_file/1, is_regular/1, - compile_wildcard/1]). +-export([wildcard/1, wildcard/2, is_dir/1, is_file/1, is_regular/1]). -export([fold_files/5, last_modified/1, file_size/1, ensure_dir/1]). - -export([wildcard/3, is_dir/2, is_file/2, is_regular/2]). -export([fold_files/6, last_modified/2, file_size/2]). +%% For debugging/testing. +-export([compile_wildcard/1]). + -include_lib("kernel/include/file.hrl"). -define(HANDLE_ERROR(Expr), @@ -37,7 +35,7 @@ catch error:{badpattern,_}=UnUsUalVaRiAbLeNaMe -> %% Get the stack backtrace correct. - erlang:error(UnUsUalVaRiAbLeNaMe) + error(UnUsUalVaRiAbLeNaMe) end). -type filename() :: file:name(). @@ -48,19 +46,19 @@ -spec wildcard(Wildcard) -> [file:filename()] when Wildcard :: filename() | dirname(). wildcard(Pattern) when is_list(Pattern) -> - ?HANDLE_ERROR(do_wildcard(Pattern, file)). + ?HANDLE_ERROR(do_wildcard(Pattern, ".", file)). -spec wildcard(Wildcard, Cwd) -> [file:filename()] when Wildcard :: filename() | dirname(), Cwd :: dirname(). -wildcard(Pattern, Cwd) when is_list(Pattern), (is_list(Cwd) or is_binary(Cwd)) -> +wildcard(Pattern, Cwd) when is_list(Pattern), is_list(Cwd) -> ?HANDLE_ERROR(do_wildcard(Pattern, Cwd, file)); wildcard(Pattern, Mod) when is_list(Pattern), is_atom(Mod) -> - ?HANDLE_ERROR(do_wildcard(Pattern, Mod)). + ?HANDLE_ERROR(do_wildcard(Pattern, ".", Mod)). -spec wildcard(file:name(), file:name(), atom()) -> [file:filename()]. wildcard(Pattern, Cwd, Mod) - when is_list(Pattern), (is_list(Cwd) or is_binary(Cwd)), is_atom(Mod) -> + when is_list(Pattern), is_list(Cwd), is_atom(Mod) -> ?HANDLE_ERROR(do_wildcard(Pattern, Cwd, Mod)). -spec is_dir(Name) -> boolean() when @@ -124,41 +122,6 @@ file_size(File, Mod) when is_atom(Mod) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -do_wildcard(Pattern, Mod) when is_list(Pattern) -> - do_wildcard_comp(do_compile_wildcard(Pattern), Mod). - -do_wildcard_comp({compiled_wildcard,{exists,File}}, Mod) -> - case eval_read_file_info(File, Mod) of - {ok,_} -> [File]; - _ -> [] - end; -do_wildcard_comp({compiled_wildcard,[Base|Rest]}, Mod) -> - do_wildcard_1([Base], Rest, Mod). - -do_wildcard(Pattern, Cwd, Mod) when is_list(Pattern), (is_list(Cwd) or is_binary(Cwd)) -> - do_wildcard_comp(do_compile_wildcard(Pattern), Cwd, Mod). - -do_wildcard_comp({compiled_wildcard,{exists,File}}, Cwd, Mod) -> - case eval_read_file_info(filename:absname(File, Cwd), Mod) of - {ok,_} -> [File]; - _ -> [] - end; -do_wildcard_comp({compiled_wildcard,[current|Rest]}, Cwd0, Mod) -> - {Cwd,PrefixLen} = case filename:join([Cwd0]) of - Bin when is_binary(Bin) -> {Bin,byte_size(Bin)+1}; - Other -> {Other,length(Other)+1} - end, %Slash away redundant slashes. - [ - if - is_binary(N) -> - <<_:PrefixLen/binary,Res/binary>> = N, - Res; - true -> - lists:nthtail(PrefixLen, N) - end || N <- do_wildcard_1([Cwd], Rest, Mod)]; -do_wildcard_comp({compiled_wildcard,[Base|Rest]}, _Cwd, Mod) -> - do_wildcard_1([Base], Rest, Mod). - do_is_dir(Dir, Mod) -> case eval_read_file_info(Dir, Mod) of {ok, #file_info{type=directory}} -> @@ -287,19 +250,37 @@ ensure_dir(F) -> %%% Pattern matching using a compiled wildcard. %%% -do_wildcard_1(Files, Pattern, Mod) -> - do_wildcard_2(Files, Pattern, [], Mod). +do_wildcard(Pattern, Cwd, Mod) -> + {Compiled,PrefixLen} = compile_wildcard(Pattern, Cwd), + Files0 = do_wildcard_1(Compiled, Mod), + Files = if + PrefixLen =:= 0 -> + Files0; + true -> + [lists:nthtail(PrefixLen, File) || File <- Files0] + end, + lists:sort(Files). + +do_wildcard_1({exists,File}, Mod) -> + case eval_read_file_info(File, Mod) of + {ok,_} -> [File]; + _ -> [] + end; +do_wildcard_1([Base|Rest], Mod) -> + do_wildcard_2([Base], Rest, [], Mod). do_wildcard_2([File|Rest], Pattern, Result, Mod) -> do_wildcard_2(Rest, Pattern, do_wildcard_3(File, Pattern, Result, Mod), Mod); do_wildcard_2([], _, Result, _Mod) -> Result. -do_wildcard_3(Base, [Pattern|Rest], Result, Mod) -> - case do_list_dir(Base, Mod) of - {ok, Files0} -> - Files = lists:sort(Files0), - Matches = wildcard_4(Pattern, Files, Base, []), +do_wildcard_3(Base, [[double_star]|Rest], Result, Mod) -> + do_double_star(".", [Base], Rest, Result, Mod, true); +do_wildcard_3(Base0, [Pattern|Rest], Result, Mod) -> + case do_list_dir(Base0, Mod) of + {ok, Files} -> + Base = prepare_base(Base0), + Matches = do_wildcard_4(Pattern, Base, Files), do_wildcard_2(Matches, Rest, Result, Mod); _ -> Result @@ -307,100 +288,121 @@ do_wildcard_3(Base, [Pattern|Rest], Result, Mod) -> do_wildcard_3(Base, [], Result, _Mod) -> [Base|Result]. -wildcard_4(Pattern, [File|Rest], Base, Result) when is_binary(File) -> - case wildcard_5(Pattern, binary_to_list(File)) of - true -> - wildcard_4(Pattern, Rest, Base, [join(Base, File)|Result]); +do_wildcard_4(Pattern, Base, Files) -> + case will_always_match(Pattern) of false -> - wildcard_4(Pattern, Rest, Base, Result) - end; -wildcard_4(Pattern, [File|Rest], Base, Result) -> - case wildcard_5(Pattern, File) of + [Base++F || F <- Files, match_part(Pattern, F)]; true -> - wildcard_4(Pattern, Rest, Base, [join(Base, File)|Result]); - false -> - wildcard_4(Pattern, Rest, Base, Result) - end; -wildcard_4(_Patt, [], _Base, Result) -> - Result. + [Base++F || F <- Files] + end. -wildcard_5([question|Rest1], [_|Rest2]) -> - wildcard_5(Rest1, Rest2); -wildcard_5([accept], _) -> +match_part([question|Rest1], [_|Rest2]) -> + match_part(Rest1, Rest2); +match_part([accept], _) -> + true; +match_part([double_star], _) -> true; -wildcard_5([star|Rest], File) -> +match_part([star|Rest], File) -> do_star(Rest, File); -wildcard_5([{one_of, Ordset}|Rest], [C|File]) -> - case ordsets:is_element(C, Ordset) of - true -> wildcard_5(Rest, File); - false -> false - end; -wildcard_5([{alt, Alts}], File) -> +match_part([{one_of, Ordset}|Rest], [C|File]) -> + gb_sets:is_element(C, Ordset) andalso match_part(Rest, File); +match_part([{alt, Alts}], File) -> do_alt(Alts, File); -wildcard_5([C|Rest1], [C|Rest2]) when is_integer(C) -> - wildcard_5(Rest1, Rest2); -wildcard_5([X|_], [Y|_]) when is_integer(X), is_integer(Y) -> +match_part([C|Rest1], [C|Rest2]) when is_integer(C) -> + match_part(Rest1, Rest2); +match_part([X|_], [Y|_]) when is_integer(X), is_integer(Y) -> false; -wildcard_5([], []) -> +match_part([], []) -> true; -wildcard_5([], [_|_]) -> +match_part([], [_|_]) -> false; -wildcard_5([_|_], []) -> +match_part([_|_], []) -> false. -do_star(Pattern, [X|Rest]) -> - case wildcard_5(Pattern, [X|Rest]) of - true -> true; - false -> do_star(Pattern, Rest) - end; +will_always_match([accept]) -> true; +will_always_match(_) -> false. + +prepare_base(Base0) -> + Base1 = filename:join(Base0, "x"), + "x"++Base2 = lists:reverse(Base1), + lists:reverse(Base2). + +do_double_star(Base, [H|T], Rest, Result, Mod, Root) -> + Full = case Root of + false -> filename:join(Base, H); + true -> H + end, + Result1 = case do_list_dir(Full, Mod) of + {ok, Files} -> + do_double_star(Full, Files, Rest, Result, Mod, false); + _ -> Result + end, + Result2 = case Root andalso Rest == [] of + true -> Result1; + false -> do_wildcard_3(Full, Rest, Result1, Mod) + end, + do_double_star(Base, T, Rest, Result2, Mod, Root); +do_double_star(_Base, [], _Rest, Result, _Mod, _Root) -> + Result. + +do_star(Pattern, [_|Rest]=File) -> + match_part(Pattern, File) orelse do_star(Pattern, Rest); do_star(Pattern, []) -> - wildcard_5(Pattern, []). + match_part(Pattern, []). do_alt([Alt|Rest], File) -> - case wildcard_5(Alt, File) of - true -> true; - false -> do_alt(Rest, File) - end; + match_part(Alt, File) orelse do_alt(Rest, File); do_alt([], _File) -> false. -do_list_dir(current, Mod) -> eval_list_dir(".", Mod); do_list_dir(Dir, Mod) -> eval_list_dir(Dir, Mod). -join(current, File) -> File; -join(Base, File) -> filename:join(Base, File). - %%% Compiling a wildcard. -compile_wildcard(Pattern) -> - ?HANDLE_ERROR(do_compile_wildcard(Pattern)). - -do_compile_wildcard(Pattern) -> - {compiled_wildcard,compile_wildcard_1(Pattern)}. +%% Only for debugging. +compile_wildcard(Pattern) when is_list(Pattern) -> + {compiled_wildcard,?HANDLE_ERROR(compile_wildcard(Pattern, "."))}. -compile_wildcard_1(Pattern) -> +compile_wildcard(Pattern, Cwd0) -> [Root|Rest] = filename:split(Pattern), case filename:pathtype(Root) of relative -> - compile_wildcard_2([Root|Rest], current); + Cwd = filename:join([Cwd0]), + compile_wildcard_2([Root|Rest], {cwd,Cwd}); _ -> - compile_wildcard_2(Rest, [Root]) + compile_wildcard_2(Rest, {root,0,Root}) end. compile_wildcard_2([Part|Rest], Root) -> case compile_part(Part) of Part -> - compile_wildcard_2(Rest, join(Root, Part)); + compile_wildcard_2(Rest, compile_join(Root, Part)); Pattern -> compile_wildcard_3(Rest, [Pattern,Root]) end; -compile_wildcard_2([], Root) -> {exists,Root}. +compile_wildcard_2([], {root,PrefixLen,Root}) -> + {{exists,Root},PrefixLen}. compile_wildcard_3([Part|Rest], Result) -> compile_wildcard_3(Rest, [compile_part(Part)|Result]); compile_wildcard_3([], Result) -> - lists:reverse(Result). + case lists:reverse(Result) of + [{root,PrefixLen,Root}|Compiled] -> + {[Root|Compiled],PrefixLen}; + [{cwd,Root}|Compiled] -> + {[Root|Compiled],length(filename:join(Root, "x"))-1} + end. + +compile_join({cwd,"."}, File) -> + {root,0,File}; +compile_join({cwd,Cwd}, File0) -> + File = filename:join([File0]), + Root = filename:join(Cwd, File), + PrefixLen = length(Root) - length(File), + {root,PrefixLen,Root}; +compile_join({root,PrefixLen,Root}, File) -> + {root,PrefixLen,filename:join(Root, File)}. compile_part(Part) -> compile_part(Part, false, []). @@ -409,13 +411,17 @@ compile_part_to_sep(Part) -> compile_part(Part, true, []). compile_part([], true, _) -> - error(missing_delimiter); + badpattern(missing_delimiter); compile_part([$,|Rest], true, Result) -> {ok, $,, lists:reverse(Result), Rest}; compile_part([$}|Rest], true, Result) -> {ok, $}, lists:reverse(Result), Rest}; compile_part([$?|Rest], Upto, Result) -> compile_part(Rest, Upto, [question|Result]); +compile_part([$*,$*], Upto, Result) -> + compile_part([], Upto, [double_star|Result]); +compile_part([$*,$*|Rest], Upto, Result) -> + compile_part(Rest, Upto, [star|Result]); compile_part([$*], Upto, Result) -> compile_part([], Upto, [accept|Result]); compile_part([$*|Rest], Upto, Result) -> @@ -441,8 +447,6 @@ compile_part([], _Upto, Result) -> compile_charset([$]|Rest], Ordset) -> compile_charset1(Rest, ordsets:add_element($], Ordset)); -compile_charset([$-|Rest], Ordset) -> - compile_charset1(Rest, ordsets:add_element($-, Ordset)); compile_charset([], _Ordset) -> error; compile_charset(List, Ordset) -> @@ -451,7 +455,7 @@ compile_charset(List, Ordset) -> compile_charset1([Lower, $-, Upper|Rest], Ordset) when Lower =< Upper -> compile_charset1(Rest, compile_range(Lower, Upper, Ordset)); compile_charset1([$]|Rest], Ordset) -> - {ok, {one_of, Ordset}, Rest}; + {ok, {one_of, gb_sets:from_ordset(Ordset)}, Rest}; compile_charset1([X|Rest], Ordset) -> compile_charset1(Rest, ordsets:add_element(X, Ordset)); compile_charset1([], _Ordset) -> @@ -477,8 +481,8 @@ compile_alt(Pattern, Result) -> error end. -error(Reason) -> - erlang:error({badpattern,Reason}). +badpattern(Reason) -> + error({badpattern,Reason}). eval_read_file_info(File, file) -> file:read_file_info(File); diff --git a/lib/stdlib/src/filename.erl b/lib/stdlib/src/filename.erl index a6b42cc68c..66e54ef221 100644 --- a/lib/stdlib/src/filename.erl +++ b/lib/stdlib/src/filename.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2012. All Rights Reserved. +%% Copyright Ericsson AB 1997-2013. 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 @@ -61,15 +61,15 @@ %% (for WIN32): absname("/") -> "D:/" --spec absname(Filename) -> file:filename() when - Filename :: file:name(). +-spec absname(Filename) -> file:filename_all() when + Filename :: file:name_all(). absname(Name) -> {ok, Cwd} = file:get_cwd(), absname(Name, Cwd). --spec absname(Filename, Dir) -> file:filename() when - Filename :: file:name(), - Dir :: file:filename(). +-spec absname(Filename, Dir) -> file:filename_all() when + Filename :: file:name_all(), + Dir :: file:name_all(). absname(Name, AbsBase) when is_binary(Name), is_list(AbsBase) -> absname(Name,filename_string_to_binary(AbsBase)); absname(Name, AbsBase) when is_list(Name), is_binary(AbsBase) -> @@ -122,9 +122,9 @@ absname_vr([[X, $:]|Name], _, _AbsBase) -> %% This is just a join/2, but assumes that %% AbsBase must be absolute and Name must be relative. --spec absname_join(Dir, Filename) -> file:filename() when - Dir :: file:filename(), - Filename :: file:name(). +-spec absname_join(Dir, Filename) -> file:filename_all() when + Dir :: file:name_all(), + Filename :: file:name_all(). absname_join(AbsBase, Name) -> join(AbsBase, flatten(Name)). @@ -136,8 +136,8 @@ absname_join(AbsBase, Name) -> %% basename("/usr/foo/") -> "foo" (trailing slashes ignored) %% basename("/") -> [] --spec basename(Filename) -> file:filename() when - Filename :: file:name(). +-spec basename(Filename) -> file:filename_all() when + Filename :: file:name_all(). basename(Name) when is_binary(Name) -> case os:type() of {win32,_} -> @@ -201,9 +201,9 @@ skip_prefix(Name, _) -> %% rootname(basename("xxx.jam")) -> "xxx" %% rootname(basename("xxx.erl")) -> "xxx" --spec basename(Filename, Ext) -> file:filename() when - Filename :: file:name(), - Ext :: file:name(). +-spec basename(Filename, Ext) -> file:filename_all() when + Filename :: file:name_all(), + Ext :: file:name_all(). basename(Name, Ext) when is_binary(Name), is_list(Ext) -> basename(Name,filename_string_to_binary(Ext)); basename(Name, Ext) when is_list(Name), is_binary(Ext) -> @@ -251,8 +251,8 @@ basename([], _Ext, Tail, _DrvSep2) -> %% Example: dirname("/usr/src/kalle.erl") -> "/usr/src", %% dirname("kalle.erl") -> "." --spec dirname(Filename) -> file:filename() when - Filename :: file:name(). +-spec dirname(Filename) -> file:filename_all() when + Filename :: file:name_all(). dirname(Name) when is_binary(Name) -> {Dsep,Drivesep} = separators(), SList = case Dsep of @@ -344,8 +344,8 @@ dirjoin1([H|T],Acc,Sep) -> %% %% On Windows: fn:dirname("\\usr\\src/kalle.erl") -> "/usr/src" --spec extension(Filename) -> file:filename() when - Filename :: file:name(). +-spec extension(Filename) -> file:filename_all() when + Filename :: file:name_all(). extension(Name) when is_binary(Name) -> {Dsep,_} = separators(), SList = case Dsep of @@ -387,8 +387,8 @@ extension([], Result, _OsType) -> %% Joins a list of filenames with directory separators. --spec join(Components) -> file:filename() when - Components :: [file:filename()]. +-spec join(Components) -> file:filename_all() when + Components :: [file:name_all()]. join([Name1, Name2|Rest]) -> join([join(Name1, Name2)|Rest]); join([Name]) when is_list(Name) -> @@ -400,9 +400,9 @@ join([Name]) when is_atom(Name) -> %% Joins two filenames with directory separators. --spec join(Name1, Name2) -> file:filename() when - Name1 :: file:filename(), - Name2 :: file:filename(). +-spec join(Name1, Name2) -> file:filename_all() when + Name1 :: file:name_all(), + Name2 :: file:name_all(). join(Name1, Name2) when is_list(Name1), is_list(Name2) -> OsType = major_os_type(), case pathtype(Name2) of @@ -488,7 +488,7 @@ maybe_remove_dirsep(Name, _) -> %% a given base directory, which is is assumed to be normalised %% by a previous call to join/{1,2}. --spec append(file:filename(), file:name()) -> file:filename(). +-spec append(file:filename_all(), file:name_all()) -> file:filename_all(). append(Dir, Name) when is_binary(Dir), is_binary(Name) -> <<Dir/binary,$/:8,Name/binary>>; append(Dir, Name) when is_binary(Dir) -> @@ -511,7 +511,7 @@ append(Dir, Name) -> %% Example: a:bar.erl, /temp/foo.erl -spec pathtype(Path) -> 'absolute' | 'relative' | 'volumerelative' when - Path :: file:name(). + Path :: file:name_all(). pathtype(Atom) when is_atom(Atom) -> pathtype(atom_to_list(Atom)); pathtype(Name) when is_list(Name) or is_binary(Name) -> @@ -564,8 +564,8 @@ win32_pathtype(_) -> relative. %% Examples: rootname("/jam.src/kalle") -> "/jam.src/kalle" %% rootname("/jam.src/foo.erl") -> "/jam.src/foo" --spec rootname(Filename) -> file:filename() when - Filename :: file:name(). +-spec rootname(Filename) -> file:filename_all() when + Filename :: file:name_all(). rootname(Name) when is_binary(Name) -> list_to_binary(rootname(binary_to_list(Name))); % No need to handle unicode, . is < 128 rootname(Name0) -> @@ -594,9 +594,9 @@ rootname([], Root, _Ext, _OsType) -> %% Examples: rootname("/jam.src/kalle.jam", ".erl") -> "/jam.src/kalle.jam" %% rootname("/jam.src/foo.erl", ".erl") -> "/jam.src/foo" --spec rootname(Filename, Ext) -> file:filename() when - Filename :: file:name(), - Ext :: file:name(). +-spec rootname(Filename, Ext) -> file:filename_all() when + Filename :: file:name_all(), + Ext :: file:name_all(). rootname(Name, Ext) when is_binary(Name), is_binary(Ext) -> list_to_binary(rootname(binary_to_list(Name),binary_to_list(Ext))); rootname(Name, Ext) when is_binary(Name) -> @@ -623,8 +623,8 @@ rootname2([Char|Rest], Ext, Result) when is_integer(Char) -> %% split("a:\\msdev\\include") -> ["a:/", "msdev", "include"] -spec split(Filename) -> Components when - Filename :: file:name(), - Components :: [file:filename()]. + Filename :: file:name_all(), + Components :: [file:name_all()]. split(Name) when is_binary(Name) -> case os:type() of {win32, _} -> win32_splitb(Name); @@ -717,8 +717,8 @@ split([], Comp, Components, OsType) -> %% will be converted to backslashes. On all platforms, the %% name will be normalized as done by join/1. --spec nativename(Path) -> file:filename() when - Path :: file:filename(). +-spec nativename(Path) -> file:filename_all() when + Path :: file:name_all(). nativename(Name0) -> Name = join([Name0]), %Normalize. case os:type() of @@ -878,7 +878,7 @@ filter_options(_Base, [], Result) -> %% Gets the source file given path of object code and module name. get_source_file(Obj, Mod, Rules) -> - source_by_rules(dirname(Obj), packages:last(Mod), Rules). + source_by_rules(dirname(Obj), atom_to_list(Mod), Rules). source_by_rules(Dir, Base, [{From, To}|Rest]) -> case try_rule(Dir, Base, From, To) of @@ -915,16 +915,14 @@ make_abs_path(BasePath, Path) -> join(BasePath, Path). major_os_type() -> - case os:type() of - {OsT, _} -> OsT; - OsT -> OsT - end. + {OsT, _} = os:type(), + OsT. %% flatten(List) %% Flatten a list, also accepting atoms. --spec flatten(Filename) -> file:filename() when - Filename :: file:name(). +-spec flatten(Filename) -> file:filename_all() when + Filename :: file:name_all(). flatten(Bin) when is_binary(Bin) -> Bin; flatten(List) -> diff --git a/lib/stdlib/src/gb_sets.erl b/lib/stdlib/src/gb_sets.erl index 91d21d869c..ba35a7170a 100644 --- a/lib/stdlib/src/gb_sets.erl +++ b/lib/stdlib/src/gb_sets.erl @@ -1,7 +1,8 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2011. All Rights Reserved. +%% Copyright Ericsson AB 2001-2012. 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 @@ -165,7 +166,7 @@ -export([new/0, is_element/2, add_element/2, del_element/2, subtract/2]). -%% GB-trees adapted from Sven-Olof Nystr�m's implementation for +%% GB-trees adapted from Sven-Olof Nyström's implementation for %% representation of sets. %% %% Data structures: @@ -196,6 +197,8 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Some types. +-export_type([iter/0]). + -type gb_set_node() :: 'nil' | {term(), _, _}. -opaque iter() :: [gb_set_node()]. diff --git a/lib/stdlib/src/gb_trees.erl b/lib/stdlib/src/gb_trees.erl index 6ad861ff5b..de0c239e26 100644 --- a/lib/stdlib/src/gb_trees.erl +++ b/lib/stdlib/src/gb_trees.erl @@ -1,7 +1,8 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2011. All Rights Reserved. +%% Copyright Ericsson AB 2001-2012. 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 @@ -19,7 +20,7 @@ %% ===================================================================== %% General Balanced Trees - highly efficient dictionaries. %% -%% Copyright (C) 1999-2001 Sven-Olof Nystr�m, Richard Carlsson +%% Copyright (C) 1999-2001 Sven-Olof Nyström, Richard Carlsson %% %% An efficient implementation of Prof. Arne Andersson's General %% Balanced Trees. These have no storage overhead compared to plain @@ -152,6 +153,8 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Some types. +-export_type([iter/0]). + -type gb_tree_node() :: 'nil' | {_, _, _, _}. -opaque iter() :: [gb_tree_node()]. diff --git a/lib/stdlib/src/io.erl b/lib/stdlib/src/io.erl index 9f65bbfa3a..c92e9e3ade 100644 --- a/lib/stdlib/src/io.erl +++ b/lib/stdlib/src/io.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2013. 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 @@ -22,28 +22,30 @@ get_chars/2,get_chars/3,get_line/1,get_line/2, get_password/0, get_password/1, setopts/1, setopts/2, getopts/0, getopts/1]). --export([write/1,write/2,read/1,read/2,read/3]). +-export([write/1,write/2,read/1,read/2,read/3,read/4]). -export([columns/0,columns/1,rows/0,rows/1]). -export([fwrite/1,fwrite/2,fwrite/3,fread/2,fread/3, format/1,format/2,format/3]). --export([scan_erl_exprs/1,scan_erl_exprs/2,scan_erl_exprs/3, - scan_erl_form/1,scan_erl_form/2,scan_erl_form/3, +-export([scan_erl_exprs/1,scan_erl_exprs/2,scan_erl_exprs/3,scan_erl_exprs/4, + scan_erl_form/1,scan_erl_form/2,scan_erl_form/3,scan_erl_form/4, parse_erl_exprs/1,parse_erl_exprs/2,parse_erl_exprs/3, - parse_erl_form/1,parse_erl_form/2,parse_erl_form/3]). + parse_erl_exprs/4,parse_erl_form/1,parse_erl_form/2, + parse_erl_form/3,parse_erl_form/4]). -export([request/1,request/2,requests/1,requests/2]). +%% Implemented in native code +-export([printable_range/0]). --export_type([device/0, format/0]). +-export_type([device/0, format/0, server_no_data/0]). %%------------------------------------------------------------------------- -type device() :: atom() | pid(). -type prompt() :: atom() | string(). --type error_description() :: term(). % Whatever the io-server sends. --type request_error() :: {'error',error_description()}. +%% ErrorDescription is whatever the I/O-server sends. +-type server_no_data() :: {'error', ErrorDescription :: term()} | 'eof'. -%% XXX: Some uses of line() in this file may need to read erl_scan:location() --type line() :: pos_integer(). +-type location() :: erl_scan:location(). %%------------------------------------------------------------------------- @@ -66,6 +68,11 @@ o_request(Io, Request, Func) -> Other end. +%% Request what the user considers printable characters +-spec printable_range() -> 'unicode' | 'latin1'. +printable_range() -> + erlang:nif_error(undefined). + %% Put chars takes mixed *unicode* list from R13 onwards. -spec put_chars(CharData) -> 'ok' when CharData :: unicode:chardata(). @@ -73,9 +80,9 @@ o_request(Io, Request, Func) -> put_chars(Chars) -> put_chars(default_output(), Chars). --spec put_chars(IoDevice, IoData) -> 'ok' when +-spec put_chars(IoDevice, CharData) -> 'ok' when IoDevice :: device(), - IoData :: unicode:chardata(). + CharData :: unicode:chardata(). put_chars(Io, Chars) -> o_request(Io, {put_chars,unicode,Chars}, put_chars). @@ -124,36 +131,34 @@ rows(Io) -> {error,enotsup} end. --spec get_chars(Prompt, Count) -> Data | 'eof' when +-spec get_chars(Prompt, Count) -> Data | server_no_data() when Prompt :: prompt(), Count :: non_neg_integer(), - Data :: [unicode:unicode_char()] | unicode:unicode_binary(). + Data :: string() | unicode:unicode_binary(). get_chars(Prompt, N) -> get_chars(default_input(), Prompt, N). --spec get_chars(IoDevice, Prompt, Count) -> Data | 'eof' | {error, Reason} when +-spec get_chars(IoDevice, Prompt, Count) -> Data | server_no_data() when IoDevice :: device(), Prompt :: prompt(), Count :: non_neg_integer(), - Reason :: term(), - Data :: [unicode:unicode_char()] | unicode:unicode_binary(). + Data :: string() | unicode:unicode_binary(). get_chars(Io, Prompt, N) when is_integer(N), N >= 0 -> request(Io, {get_chars,unicode,Prompt,N}). --spec get_line(Prompt) -> Data | 'eof' | {'error', Reason} when +-spec get_line(Prompt) -> Data | server_no_data() when Prompt :: prompt(), - Reason :: term(), - Data :: [unicode:unicode_char()] | unicode:unicode_binary(). + Data :: string() | unicode:unicode_binary(). get_line(Prompt) -> get_line(default_input(), Prompt). --spec get_line(IoDevice, Prompt) -> Data | 'eof' | {'error', term()} when +-spec get_line(IoDevice, Prompt) -> Data | server_no_data() when IoDevice :: device(), Prompt :: prompt(), - Data :: [unicode:unicode_char()] | unicode:unicode_binary(). + Data :: string() | unicode:unicode_binary(). get_line(Io, Prompt) -> request(Io, {get_line,unicode,Prompt}). @@ -219,11 +224,10 @@ write(Io, Term) -> -spec read(Prompt) -> Result when Prompt :: prompt(), Result :: {'ok', Term :: term()} - | 'eof' - | {'error', ErrorInfo :: erl_scan:error_info()}. + | server_no_data() + | {'error', ErrorInfo}, + ErrorInfo :: erl_scan:error_info() | erl_parse:error_info(). -% Read does not use get_until as erl_scan does not work with unicode -% XXX:PaN fixme? read(Prompt) -> read(default_input(), Prompt). @@ -231,8 +235,9 @@ read(Prompt) -> IoDevice :: device(), Prompt :: prompt(), Result :: {'ok', Term :: term()} - | 'eof' - | {'error', ErrorInfo :: erl_scan:error_info()}. + | server_no_data() + | {'error', ErrorInfo}, + ErrorInfo :: erl_scan:error_info() | erl_parse:error_info(). read(Io, Prompt) -> case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[1]}) of @@ -248,24 +253,41 @@ read(Io, Prompt) -> Other end. --spec read(IoDevice, Prompt, StartLine) -> Result when +-spec read(IoDevice, Prompt, StartLocation) -> Result when IoDevice :: device(), Prompt :: prompt(), - StartLine :: line(), - Result :: {'ok', Term :: term(), EndLine :: line()} - | {'eof', EndLine :: line()} - | {'error', ErrorInfo :: erl_scan:error_info(), ErrorLine :: line()}. - -read(Io, Prompt, StartLine) when is_integer(StartLine) -> - case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[StartLine]}) of - {ok,Toks,EndLine} -> + StartLocation :: location(), + Result :: {'ok', Term :: term(), EndLocation :: location()} + | {'eof', EndLocation :: location()} + | server_no_data() + | {'error', ErrorInfo, ErrorLocation :: location()}, + ErrorInfo :: erl_scan:error_info() | erl_parse:error_info(). + +read(Io, Prompt, Pos0) -> + read(Io, Prompt, Pos0, []). + +-spec read(IoDevice, Prompt, StartLocation, Options) -> Result when + IoDevice :: device(), + Prompt :: prompt(), + StartLocation :: location(), + Options :: erl_scan:options(), + Result :: {'ok', Term :: term(), EndLocation :: location()} + | {'eof', EndLocation :: location()} + | server_no_data() + | {'error', ErrorInfo, ErrorLocation :: location()}, + ErrorInfo :: erl_scan:error_info() | erl_parse:error_info(). + +read(Io, Prompt, Pos0, Options) -> + Args = [Pos0,Options], + case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,Args}) of + {ok,Toks,EndLocation} -> case erl_parse:parse_term(Toks) of - {ok,Term} -> {ok,Term,EndLine}; - {error,ErrorInfo} -> {error,ErrorInfo,EndLine} + {ok,Term} -> {ok,Term,EndLocation}; + {error,ErrorInfo} -> {error,ErrorInfo,EndLocation} end; - {error,_E,_EndLine} = Error -> + {error,_E,_EndLocation} = Error -> Error; - {eof,_EndLine} = Eof -> + {eof,_EndLocation} = Eof -> Eof; Other -> Other @@ -313,7 +335,9 @@ fread(Prompt, Format) -> IoDevice :: device(), Prompt :: prompt(), Format :: format(), - Result :: {'ok', Terms :: [term()]} | 'eof' | {'error', What :: term()}. + Result :: {'ok', Terms :: [term()]} + | {'error', {'fread', FreadError :: io_lib:fread_error()}} + | server_no_data(). fread(Io, Prompt, Format) -> case request(Io, {fread,Prompt,Format}) of @@ -348,7 +372,7 @@ format(Io, Format, Args) -> -spec scan_erl_exprs(Prompt) -> Result when Prompt :: prompt(), - Result :: erl_scan:tokens_result() | request_error(). + Result :: erl_scan:tokens_result() | server_no_data(). scan_erl_exprs(Prompt) -> scan_erl_exprs(default_input(), Prompt, 1). @@ -356,23 +380,33 @@ scan_erl_exprs(Prompt) -> -spec scan_erl_exprs(Device, Prompt) -> Result when Device :: device(), Prompt :: prompt(), - Result :: erl_scan:tokens_result() | request_error(). + Result :: erl_scan:tokens_result() | server_no_data(). scan_erl_exprs(Io, Prompt) -> scan_erl_exprs(Io, Prompt, 1). --spec scan_erl_exprs(Device, Prompt, StartLine) -> Result when +-spec scan_erl_exprs(Device, Prompt, StartLocation) -> Result when Device :: device(), Prompt :: prompt(), - StartLine :: line(), - Result :: erl_scan:tokens_result() | request_error(). + StartLocation :: location(), + Result :: erl_scan:tokens_result() | server_no_data(). scan_erl_exprs(Io, Prompt, Pos0) -> - request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0]}). + scan_erl_exprs(Io, Prompt, Pos0, []). + +-spec scan_erl_exprs(Device, Prompt, StartLocation, Options) -> Result when + Device :: device(), + Prompt :: prompt(), + StartLocation :: location(), + Options :: erl_scan:options(), + Result :: erl_scan:tokens_result() | server_no_data(). + +scan_erl_exprs(Io, Prompt, Pos0, Options) -> + request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0,Options]}). -spec scan_erl_form(Prompt) -> Result when Prompt :: prompt(), - Result :: erl_scan:tokens_result() | request_error(). + Result :: erl_scan:tokens_result() | server_no_data(). scan_erl_form(Prompt) -> scan_erl_form(default_input(), Prompt, 1). @@ -380,26 +414,41 @@ scan_erl_form(Prompt) -> -spec scan_erl_form(IoDevice, Prompt) -> Result when IoDevice :: device(), Prompt :: prompt(), - Result :: erl_scan:tokens_result() | request_error(). + Result :: erl_scan:tokens_result() | server_no_data(). scan_erl_form(Io, Prompt) -> scan_erl_form(Io, Prompt, 1). --spec scan_erl_form(IoDevice, Prompt, StartLine) -> Result when +-spec scan_erl_form(IoDevice, Prompt, StartLocation) -> Result when IoDevice :: device(), Prompt :: prompt(), - StartLine :: line(), - Result :: erl_scan:tokens_result() | request_error(). + StartLocation :: location(), + Result :: erl_scan:tokens_result() | server_no_data(). scan_erl_form(Io, Prompt, Pos0) -> - request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0]}). + scan_erl_form(Io, Prompt, Pos0, []). + +-spec scan_erl_form(IoDevice, Prompt, StartLocation, Options) -> Result when + IoDevice :: device(), + Prompt :: prompt(), + StartLocation :: location(), + Options :: erl_scan:options(), + Result :: erl_scan:tokens_result() | server_no_data(). + +scan_erl_form(Io, Prompt, Pos0, Options) -> + request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0,Options]}). %% Parsing Erlang code. --type parse_ret() :: {'ok', ExprList :: erl_parse:abstract_expr(), EndLine :: line()} - | {'eof', EndLine :: line()} - | {'error', ErrorInfo :: erl_scan:error_info(), ErrorLine :: line()} - | request_error(). +-type parse_ret() :: {'ok', + ExprList :: erl_parse:abstract_expr(), + EndLocation :: location()} + | {'eof', EndLocation :: location()} + | {'error', + ErrorInfo :: erl_scan:error_info() + | erl_parse:error_info(), + ErrorLocation :: location()} + | server_no_data(). -spec parse_erl_exprs(Prompt) -> Result when Prompt :: prompt(), @@ -416,14 +465,24 @@ parse_erl_exprs(Prompt) -> parse_erl_exprs(Io, Prompt) -> parse_erl_exprs(Io, Prompt, 1). --spec parse_erl_exprs(IoDevice, Prompt, StartLine) -> Result when +-spec parse_erl_exprs(IoDevice, Prompt, StartLocation) -> Result when IoDevice :: device(), Prompt :: prompt(), - StartLine :: line(), + StartLocation :: location(), Result :: parse_ret(). parse_erl_exprs(Io, Prompt, Pos0) -> - case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0]}) of + parse_erl_exprs(Io, Prompt, Pos0, []). + +-spec parse_erl_exprs(IoDevice, Prompt, StartLocation, Options) -> Result when + IoDevice :: device(), + Prompt :: prompt(), + StartLocation :: location(), + Options :: erl_scan:options(), + Result :: parse_ret(). + +parse_erl_exprs(Io, Prompt, Pos0, Options) -> + case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0,Options]}) of {ok,Toks,EndPos} -> case erl_parse:parse_exprs(Toks) of {ok,Exprs} -> {ok,Exprs,EndPos}; @@ -433,10 +492,15 @@ parse_erl_exprs(Io, Prompt, Pos0) -> Other end. --type parse_form_ret() :: {'ok', AbsForm :: erl_parse:abstract_form(), EndLine :: line()} - | {'eof', EndLine :: line()} - | {'error', ErrorInfo :: erl_scan:error_info(), ErrorLine :: line()} - | request_error(). +-type parse_form_ret() :: {'ok', + AbsForm :: erl_parse:abstract_form(), + EndLocation :: location()} + | {'eof', EndLocation :: location()} + | {'error', + ErrorInfo :: erl_scan:error_info() + | erl_parse:error_info(), + ErrorLocation :: location()} + | server_no_data(). -spec parse_erl_form(Prompt) -> Result when Prompt :: prompt(), @@ -453,14 +517,25 @@ parse_erl_form(Prompt) -> parse_erl_form(Io, Prompt) -> parse_erl_form(Io, Prompt, 1). --spec parse_erl_form(IoDevice, Prompt, StartLine) -> Result when +-spec parse_erl_form(IoDevice, Prompt, StartLocation) -> Result when IoDevice :: device(), Prompt :: prompt(), - StartLine :: line(), + StartLocation :: location(), Result :: parse_form_ret(). parse_erl_form(Io, Prompt, Pos0) -> - case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0]}) of + parse_erl_form(Io, Prompt, Pos0, []). + +-spec parse_erl_form(IoDevice, Prompt, StartLocation, Options) -> Result when + IoDevice :: device(), + Prompt :: prompt(), + StartLocation :: location(), + Options :: erl_scan:options(), + Result :: parse_form_ret(). + +parse_erl_form(Io, Prompt, Pos0, Options) -> + Args = [Pos0, Options], + case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,Args}) of {ok,Toks,EndPos} -> case erl_parse:parse_form(Toks) of {ok,Exprs} -> {ok,Exprs,EndPos}; diff --git a/lib/stdlib/src/io_lib.erl b/lib/stdlib/src/io_lib.erl index 0252cdf742..a9b6d4131e 100644 --- a/lib/stdlib/src/io_lib.erl +++ b/lib/stdlib/src/io_lib.erl @@ -1,7 +1,8 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2013. 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,52 +47,71 @@ %% 173 - 176 { - ~ punctuation %% 177 DEL control %% 200 - 237 control -%% 240 - 277 NBSP - � punctuation -%% 300 - 326 � - � uppercase -%% 327 � punctuation -%% 330 - 336 � - � uppercase -%% 337 - 366 � - � lowercase -%% 367 � punctuation -%% 370 - 377 � - � lowercase +%% 240 - 277 NBSP - ¿ punctuation +%% 300 - 326 À - Ö uppercase +%% 327 × punctuation +%% 330 - 336 Ø - Þ uppercase +%% 337 - 366 ß - ö lowercase +%% 367 ÷ punctuation +%% 370 - 377 ø - ÿ lowercase %% %% Many punctuation characters region have special meaning. Must -%% watch using � \327, very close to x \170 +%% watch using × \327, very close to x \170 -module(io_lib). -export([fwrite/2,fread/2,fread/3,format/2]). -export([print/1,print/4,indentation/2]). --export([write/1,write/2,write/3,nl/0,format_prompt/1]). --export([write_atom/1,write_string/1,write_string/2,write_unicode_string/1, - write_unicode_string/2, write_char/1, write_unicode_char/1]). +-export([write/1,write/2,write/3,nl/0,format_prompt/1,format_prompt/2]). +-export([write_atom/1,write_string/1,write_string/2,write_latin1_string/1, + write_latin1_string/2, write_char/1, write_latin1_char/1]). --export([quote_atom/2, char_list/1, unicode_char_list/1, - deep_char_list/1, deep_unicode_char_list/1, - printable_list/1, printable_unicode_list/1]). +-export([write_string_as_latin1/1, write_string_as_latin1/2, + write_char_as_latin1/1]). + +-export([quote_atom/2, char_list/1, latin1_char_list/1, + deep_char_list/1, deep_latin1_char_list/1, + printable_list/1, printable_latin1_list/1, printable_unicode_list/1]). %% Utilities for collecting characters. -export([collect_chars/3, collect_chars/4, collect_line/2, collect_line/3, collect_line/4, get_until/3, get_until/4]). --export_type([chars/0, continuation/0]). +%% The following functions were used by Yecc's include-file. +-export([write_unicode_string/1, write_unicode_char/1, + deep_unicode_char_list/1]). + +-export_type([chars/0, latin1_string/0, continuation/0, fread_error/0]). %%---------------------------------------------------------------------- -type chars() :: [char() | chars()]. +-type latin1_string() :: [unicode:latin1_char()]. -type depth() :: -1 | non_neg_integer(). --opaque continuation() :: {_, _, _, _}. % XXX: refine +-opaque continuation() :: {Format :: string(), + Stack :: chars(), + Nchars :: non_neg_integer(), + Results :: [term()]}. + +-type fread_error() :: 'atom' + | 'based' + | 'character' + | 'float' + | 'format' + | 'input' + | 'integer' + | 'string' + | 'unsigned'. %%---------------------------------------------------------------------- %% Interface calls to sub-modules. --spec fwrite(Format, Data) -> chars() | UnicodeList when +-spec fwrite(Format, Data) -> chars() when Format :: io:format(), - Data :: [term()], - UnicodeList :: [unicode:unicode_char()], Data :: [term()]. fwrite(Format, Args) -> @@ -104,7 +124,7 @@ fwrite(Format, Args) -> | {'more', RestFormat :: string(), Nchars :: non_neg_integer(), InputStack :: chars()} - | {'error', What :: term()}. + | {'error', {'fread', What :: fread_error()}}. fread(Chars, Format) -> io_lib_fread:fread(Chars, Format). @@ -117,15 +137,14 @@ fread(Chars, Format) -> | {'done', Result, LeftOverChars :: string()}, Result :: {'ok', InputList :: [term()]} | 'eof' - | {'error', What :: term()}. + | {'error', {'fread', What :: fread_error()}}. fread(Cont, Chars, Format) -> io_lib_fread:fread(Cont, Chars, Format). --spec format(Format, Data) -> chars() | UnicodeList when +-spec format(Format, Data) -> chars() when Format :: io:format(), - Data :: [term()], - UnicodeList :: [unicode:unicode_char()]. + Data :: [term()]. format(Format, Args) -> case catch io_lib_format:fwrite(Format, Args) of @@ -159,27 +178,34 @@ indentation(Chars, Current) -> %% Format an IO-request prompt (handles formatting errors safely). -%% Atoms, binaries, and iolists can be used as-is, and will be -%% printed without any additional quotes. -%% Note that the output is a deep string, and not an iolist (i.e., -%% it may be deep, but never contains binaries, due to the "~s"). +%% Atoms, binaries, and iolists (or unicode:charlist()) can be used +%% as-is, and will be printed without any additional quotes. -spec format_prompt(term()) -> chars(). -format_prompt({format,Format,Args}) -> - format_prompt(Format,Args); -format_prompt(Prompt) - when is_list(Prompt); is_atom(Prompt); is_binary(Prompt) -> - format_prompt("~ts", [Prompt]); format_prompt(Prompt) -> - format_prompt("~tp", [Prompt]). + format_prompt(Prompt, latin1). + +-spec format_prompt(term(), atom()) -> chars(). + +format_prompt({format,Format,Args}, _Encoding) -> + do_format_prompt(Format, Args); +format_prompt(Prompt, Encoding) + when is_list(Prompt); is_atom(Prompt); is_binary(Prompt) -> + do_format_prompt(add_modifier(Encoding, "s"), [Prompt]); +format_prompt(Prompt, Encoding) -> + do_format_prompt(add_modifier(Encoding, "p"), [Prompt]). -format_prompt(Format, Args) -> +do_format_prompt(Format, Args) -> case catch io_lib:format(Format, Args) of {'EXIT',_} -> "???"; List -> List end. +add_modifier(latin1, C) -> + "~"++C; +add_modifier(_, C) -> + "~t"++C. %% write(Term) %% write(Term, Depth) @@ -250,10 +276,10 @@ write_ref(Ref) -> write_binary(B, D) when is_integer(D) -> [$<,$<,write_binary_body(B, D),$>,$>]. -write_binary_body(_B, 1) -> - "..."; write_binary_body(<<>>, _D) -> ""; +write_binary_body(_B, 1) -> + "..."; write_binary_body(<<X:8>>, _D) -> [integer_to_list(X)]; write_binary_body(<<X:8,Rest/bitstring>>, D) -> @@ -291,7 +317,7 @@ quote_atom(Atom, Cs0) -> case Cs0 of [C|Cs] when C >= $a, C =< $z -> not name_chars(Cs); - [C|Cs] when C >= $�, C =< $�, C =/= $� -> + [C|Cs] when C >= $ß, C =< $ÿ, C =/= $÷ -> not name_chars(Cs); _ -> true end @@ -305,14 +331,19 @@ name_chars([C|Cs]) -> name_chars([]) -> true. name_char(C) when C >= $a, C =< $z -> true; -name_char(C) when C >= $�, C =< $�, C =/= $� -> true; +name_char(C) when C >= $ß, C =< $ÿ, C =/= $÷ -> true; name_char(C) when C >= $A, C =< $Z -> true; -name_char(C) when C >= $�, C =< $�, C =/= $� -> true; +name_char(C) when C >= $À, C =< $Þ, C =/= $× -> true; name_char(C) when C >= $0, C =< $9 -> true; name_char($_) -> true; name_char($@) -> true; name_char(_) -> false. +%%% There are two functions to write Unicode strings: +%%% - they both escape control characters < 160; +%%% - write_string() never escapes characters >= 160; +%%% - write_string_as_latin1() also escapes characters >= 255. + %% write_string([Char]) -> [Char] %% Generate the list of characters needed to print a string. @@ -325,13 +356,33 @@ write_string(S) -> -spec write_string(string(), char()) -> chars(). write_string(S, Q) -> - [Q|write_string1(latin1, S, Q)]. + [Q|write_string1(unicode_as_unicode, S, Q)]. +%% Backwards compatibility. write_unicode_string(S) -> - write_unicode_string(S, $"). %" + write_string(S). + +-spec write_latin1_string(Latin1String) -> latin1_string() when + Latin1String :: latin1_string(). -write_unicode_string(S, Q) -> - [Q|write_string1(unicode, S, Q)]. +write_latin1_string(S) -> + write_latin1_string(S, $"). %" + +-spec write_latin1_string(latin1_string(), char()) -> latin1_string(). + +write_latin1_string(S, Q) -> + [Q|write_string1(latin1, S, Q)]. + +-spec write_string_as_latin1(String) -> latin1_string() when + String :: string(). + +write_string_as_latin1(S) -> + write_string_as_latin1(S, $"). %" + +-spec write_string_as_latin1(string(), char()) -> latin1_string(). + +write_string_as_latin1(S, Q) -> + [Q|write_string1(unicode_as_latin1, S, Q)]. write_string1(_,[], Q) -> [Q]; @@ -344,7 +395,11 @@ string_char(_,C, _, Tail) when C >= $\s, C =< $~ -> [C|Tail]; string_char(latin1,C, _, Tail) when C >= $\240, C =< $\377 -> [C|Tail]; -string_char(unicode,C, _, Tail) when C >= $\240 -> +string_char(unicode_as_unicode,C, _, Tail) when C >= $\240 -> + [C|Tail]; +string_char(unicode_as_latin1,C, _, Tail) when C >= $\240, C =< $\377 -> + [C|Tail]; +string_char(unicode_as_latin1,C, _, Tail) when C >= $\377 -> "\\x{"++erlang:integer_to_list(C, 16)++"}"++Tail; string_char(_,$\n, _, Tail) -> [$\\,$n|Tail]; %\n = LF string_char(_,$\r, _, Tail) -> [$\\,$r|Tail]; %\r = CR @@ -360,6 +415,11 @@ string_char(_,C, _, Tail) when C < $\240-> %Other control characters. C3 = (C band 7) + $0, [$\\,C1,C2,C3|Tail]. +%%% There are two functions to write a Unicode character: +%%% - they both escape control characters < 160; +%%% - write_char() never escapes characters >= 160; +%%% - write_char_as_latin1() also escapes characters >= 255. + %% write_char(Char) -> [char()]. %% Generate the list of characters needed to print a character constant. %% Must special case SPACE, $\s, here. @@ -368,35 +428,63 @@ string_char(_,C, _, Tail) when C < $\240-> %Other control characters. Char :: char(). write_char($\s) -> "$\\s"; %Must special case this. -write_char(C) when is_integer(C), C >= $\000, C =< $\377 -> - [$$|string_char(latin1,C, -1, [])]. +write_char(C) when is_integer(C), C >= $\000 -> + [$$|string_char(unicode_as_unicode, C, -1, [])]. -write_unicode_char(Ch) when Ch =< 255 -> - write_char(Ch); -write_unicode_char(Uni) -> - [$$|string_char(unicode,Uni, -1, [])]. +%% Backwards compatibility. +write_unicode_char(C) -> + write_char(C). -%% char_list(CharList) -%% deep_char_list(CharList) -%% Return true if CharList is a (possibly deep) list of characters, else -%% false. +-spec write_latin1_char(Latin1Char) -> latin1_string() when + Latin1Char :: unicode:latin1_char(). + +write_latin1_char(Lat1) when is_integer(Lat1), Lat1 >= $\000, Lat1 =< $\377 -> + [$$|string_char(latin1, Lat1, -1, [])]. + +-spec write_char_as_latin1(Char) -> latin1_string() when + Char :: char(). + +write_char_as_latin1(Uni) when is_integer(Uni), Uni >= $\000 -> + [$$|string_char(unicode_as_latin1,Uni, -1, [])]. + +%% latin1_char_list(CharList) +%% deep_latin1_char_list(CharList) +%% Return true if CharList is a (possibly deep) list of Latin-1 +%% characters, else false. + +-spec latin1_char_list(Term) -> boolean() when + Term :: term(). + +latin1_char_list([C|Cs]) when is_integer(C), C >= $\000, C =< $\377 -> + latin1_char_list(Cs); +latin1_char_list([]) -> true; +latin1_char_list(_) -> false. %Everything else is false -spec char_list(Term) -> boolean() when Term :: term(). -char_list([C|Cs]) when is_integer(C), C >= $\000, C =< $\377 -> +char_list([C|Cs]) when is_integer(C), C >= 0, C < 16#D800; + is_integer(C), C > 16#DFFF, C < 16#FFFE; + is_integer(C), C > 16#FFFF, C =< 16#10FFFF -> char_list(Cs); char_list([]) -> true; char_list(_) -> false. %Everything else is false --spec unicode_char_list(term()) -> boolean(). +-spec deep_latin1_char_list(Term) -> boolean() when + Term :: term(). -unicode_char_list([C|Cs]) when is_integer(C), C >= 0, C < 16#D800; - is_integer(C), C > 16#DFFF, C < 16#FFFE; - is_integer(C), C > 16#FFFF, C =< 16#10FFFF -> - unicode_char_list(Cs); -unicode_char_list([]) -> true; -unicode_char_list(_) -> false. %Everything else is false +deep_latin1_char_list(Cs) -> + deep_latin1_char_list(Cs, []). + +deep_latin1_char_list([C|Cs], More) when is_list(C) -> + deep_latin1_char_list(C, [Cs|More]); +deep_latin1_char_list([C|Cs], More) when is_integer(C), C >= $\000, C =< $\377 -> + deep_latin1_char_list(Cs, More); +deep_latin1_char_list([], [Cs|More]) -> + deep_latin1_char_list(Cs, More); +deep_latin1_char_list([], []) -> true; +deep_latin1_char_list(_, _More) -> %Everything else is false + false. -spec deep_char_list(Term) -> boolean() when Term :: term(). @@ -406,65 +494,72 @@ deep_char_list(Cs) -> deep_char_list([C|Cs], More) when is_list(C) -> deep_char_list(C, [Cs|More]); -deep_char_list([C|Cs], More) when is_integer(C), C >= $\000, C =< $\377 -> +deep_char_list([C|Cs], More) + when is_integer(C), C >= 0, C < 16#D800; + is_integer(C), C > 16#DFFF, C < 16#FFFE; + is_integer(C), C > 16#FFFF, C =< 16#10FFFF -> deep_char_list(Cs, More); deep_char_list([], [Cs|More]) -> deep_char_list(Cs, More); deep_char_list([], []) -> true; -deep_char_list(_, _More) -> %Everything else is false +deep_char_list(_, _More) -> %Everything else is false false. --spec deep_unicode_char_list(term()) -> boolean(). +deep_unicode_char_list(Term) -> + deep_char_list(Term). -deep_unicode_char_list(Cs) -> - deep_unicode_char_list(Cs, []). +%% printable_latin1_list([Char]) -> boolean() +%% Return true if CharList is a list of printable Latin1 characters, else +%% false. -deep_unicode_char_list([C|Cs], More) when is_list(C) -> - deep_unicode_char_list(C, [Cs|More]); -deep_unicode_char_list([C|Cs], More) - when is_integer(C), C >= 0, C < 16#D800; - is_integer(C), C > 16#DFFF, C < 16#FFFE; - is_integer(C), C > 16#FFFF, C =< 16#10FFFF -> - deep_unicode_char_list(Cs, More); -deep_unicode_char_list([], [Cs|More]) -> - deep_unicode_char_list(Cs, More); -deep_unicode_char_list([], []) -> true; -deep_unicode_char_list(_, _More) -> %Everything else is false - false. +-spec printable_latin1_list(Term) -> boolean() when + Term :: term(). + +printable_latin1_list([C|Cs]) when is_integer(C), C >= $\040, C =< $\176 -> + printable_latin1_list(Cs); +printable_latin1_list([C|Cs]) when is_integer(C), C >= $\240, C =< $\377 -> + printable_latin1_list(Cs); +printable_latin1_list([$\n|Cs]) -> printable_latin1_list(Cs); +printable_latin1_list([$\r|Cs]) -> printable_latin1_list(Cs); +printable_latin1_list([$\t|Cs]) -> printable_latin1_list(Cs); +printable_latin1_list([$\v|Cs]) -> printable_latin1_list(Cs); +printable_latin1_list([$\b|Cs]) -> printable_latin1_list(Cs); +printable_latin1_list([$\f|Cs]) -> printable_latin1_list(Cs); +printable_latin1_list([$\e|Cs]) -> printable_latin1_list(Cs); +printable_latin1_list([]) -> true; +printable_latin1_list(_) -> false. %Everything else is false %% printable_list([Char]) -> boolean() %% Return true if CharList is a list of printable characters, else -%% false. +%% false. The notion of printable in Unicode terms is somewhat floating. +%% Everything that is not a control character and not invalid unicode +%% will be considered printable. +%% What the user has noted as printable characters is what actually +%% specifies when this function will return true. If the VM is started +%% with +pc latin1, only the latin1 range will be deemed as printable +%% if on the other hand +pc unicode is given, all characters in the Unicode +%% character set are deemed printable. latin1 is default. -spec printable_list(Term) -> boolean() when Term :: term(). -printable_list([C|Cs]) when is_integer(C), C >= $\040, C =< $\176 -> - printable_list(Cs); -printable_list([C|Cs]) when is_integer(C), C >= $\240, C =< $\377 -> - printable_list(Cs); -printable_list([$\n|Cs]) -> printable_list(Cs); -printable_list([$\r|Cs]) -> printable_list(Cs); -printable_list([$\t|Cs]) -> printable_list(Cs); -printable_list([$\v|Cs]) -> printable_list(Cs); -printable_list([$\b|Cs]) -> printable_list(Cs); -printable_list([$\f|Cs]) -> printable_list(Cs); -printable_list([$\e|Cs]) -> printable_list(Cs); -printable_list([]) -> true; -printable_list(_) -> false. %Everything else is false - -%% printable_unicode_list([Char]) -> boolean() -%% Return true if CharList is a list of printable characters, else -%% false. The notion of printable in Unicode terms is somewhat floating. -%% Everything that is not a control character and not invalid unicode -%% will be considered printable. +printable_list(L) -> + %% There will be more alternatives returns from io:printable range + %% in the future. To not have a catch-all clause is deliberate. + case io:printable_range() of + latin1 -> + printable_latin1_list(L); + unicode -> + printable_unicode_list(L) + end. --spec printable_unicode_list(term()) -> boolean(). +-spec printable_unicode_list(Term) -> boolean() when + Term :: term(). printable_unicode_list([C|Cs]) when is_integer(C), C >= $\040, C =< $\176 -> printable_unicode_list(Cs); -printable_unicode_list([C|Cs]) - when is_integer(C), C >= 16#A0, C < 16#D800; +printable_unicode_list([C|Cs]) + when is_integer(C), C >= 16#A0, C < 16#D800; is_integer(C), C > 16#DFFF, C < 16#FFFE; is_integer(C), C > 16#FFFF, C =< 16#10FFFF -> printable_unicode_list(Cs); diff --git a/lib/stdlib/src/io_lib_format.erl b/lib/stdlib/src/io_lib_format.erl index 49a00a4ec7..56e15a17ec 100644 --- a/lib/stdlib/src/io_lib_format.erl +++ b/lib/stdlib/src/io_lib_format.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2013. 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 @@ -22,7 +22,7 @@ -export([fwrite/2,fwrite_g/1,indentation/2]). -%% fwrite(Format, ArgList) -> [Char]. +%% fwrite(Format, ArgList) -> string(). %% Format the arguments in ArgList after string Format. Just generate %% an error if there is an error in the arguments. %% @@ -58,14 +58,22 @@ collect_cseq(Fmt0, Args0) -> {P,Fmt2,Args2} = precision(Fmt1, Args1), {Pad,Fmt3,Args3} = pad_char(Fmt2, Args2), {Encoding,Fmt4,Args4} = encoding(Fmt3, Args3), - {C,As,Fmt5,Args5} = collect_cc(Fmt4, Args4), - {{C,As,F,Ad,P,Pad,Encoding},Fmt5,Args5}. + {Strings,Fmt5,Args5} = strings(Fmt4, Args4), + {C,As,Fmt6,Args6} = collect_cc(Fmt5, Args5), + {{C,As,F,Ad,P,Pad,Encoding,Strings},Fmt6,Args6}. encoding([$t|Fmt],Args) -> + true = hd(Fmt) =/= $l, {unicode,Fmt,Args}; encoding(Fmt,Args) -> {latin1,Fmt,Args}. +strings([$l|Fmt],Args) -> + true = hd(Fmt) =/= $t, + {false,Fmt,Args}; +strings(Fmt,Args) -> + {true,Fmt,Args}. + field_width([$-|Fmt0], Args0) -> {F,Fmt,Args} = field_value(Fmt0, Args0), field_width(-F, Fmt, Args); @@ -128,18 +136,18 @@ collect_cc([$i|Fmt], [A|Args]) -> {$i,[A],Fmt,Args}. pcount(Cs) -> pcount(Cs, 0). -pcount([{$p,_As,_F,_Ad,_P,_Pad,_Enc}|Cs], Acc) -> pcount(Cs, Acc+1); -pcount([{$P,_As,_F,_Ad,_P,_Pad,_Enc}|Cs], Acc) -> pcount(Cs, Acc+1); +pcount([{$p,_As,_F,_Ad,_P,_Pad,_Enc,_Str}|Cs], Acc) -> pcount(Cs, Acc+1); +pcount([{$P,_As,_F,_Ad,_P,_Pad,_Enc,_Str}|Cs], Acc) -> pcount(Cs, Acc+1); pcount([_|Cs], Acc) -> pcount(Cs, Acc); pcount([], Acc) -> Acc. -%% build([Control], Pc, Indentation) -> [Char]. +%% build([Control], Pc, Indentation) -> string(). %% Interpret the control structures. Count the number of print %% remaining and only calculate indentation when necessary. Must also %% be smart when calculating indentation for characters in format. -build([{C,As,F,Ad,P,Pad,Enc}|Cs], Pc0, I) -> - S = control(C, As, F, Ad, P, Pad, Enc, I), +build([{C,As,F,Ad,P,Pad,Enc,Str}|Cs], Pc0, I) -> + S = control(C, As, F, Ad, P, Pad, Enc, Str, I), Pc1 = decr_pc(C, Pc0), if Pc1 > 0 -> [S|build(Cs, Pc1, indentation(S, I))]; @@ -154,7 +162,7 @@ decr_pc($p, Pc) -> Pc - 1; decr_pc($P, Pc) -> Pc - 1; decr_pc(_, Pc) -> Pc. -%% indentation([Char], Indentation) -> Indentation. +%% indentation(String, Indentation) -> Indentation. %% Calculate the indentation of the end of a string given its start %% indentation. We assume tabs at 8 cols. @@ -167,64 +175,63 @@ indentation([C|Cs], I) -> indentation([], I) -> I. %% control(FormatChar, [Argument], FieldWidth, Adjust, Precision, PadChar, -%% Indentation) -> -%% [Char] +%% Encoding, Indentation) -> String %% This is the main dispatch function for the various formatting commands. %% Field widths and precisions have already been calculated. -control($w, [A], F, Adj, P, Pad, _Enc,_I) -> +control($w, [A], F, Adj, P, Pad, _Enc, _Str, _I) -> term(io_lib:write(A, -1), F, Adj, P, Pad); -control($p, [A], F, Adj, P, Pad, _Enc, I) -> - print(A, -1, F, Adj, P, Pad, I); -control($W, [A,Depth], F, Adj, P, Pad, _Enc, _I) when is_integer(Depth) -> +control($p, [A], F, Adj, P, Pad, Enc, Str, I) -> + print(A, -1, F, Adj, P, Pad, Enc, Str, I); +control($W, [A,Depth], F, Adj, P, Pad, _Enc, _Str, _I) when is_integer(Depth) -> term(io_lib:write(A, Depth), F, Adj, P, Pad); -control($P, [A,Depth], F, Adj, P, Pad, _Enc, I) when is_integer(Depth) -> - print(A, Depth, F, Adj, P, Pad, I); -control($s, [A], F, Adj, P, Pad, _Enc, _I) when is_atom(A) -> +control($P, [A,Depth], F, Adj, P, Pad, Enc, Str, I) when is_integer(Depth) -> + print(A, Depth, F, Adj, P, Pad, Enc, Str, I); +control($s, [A], F, Adj, P, Pad, _Enc, _Str, _I) when is_atom(A) -> string(atom_to_list(A), F, Adj, P, Pad); -control($s, [L0], F, Adj, P, Pad, latin1, _I) -> +control($s, [L0], F, Adj, P, Pad, latin1, _Str, _I) -> L = iolist_to_chars(L0), string(L, F, Adj, P, Pad); -control($s, [L0], F, Adj, P, Pad, unicode, _I) -> - L = unicode:characters_to_list(L0), +control($s, [L0], F, Adj, P, Pad, unicode, _Str, _I) -> + L = cdata_to_chars(L0), uniconv(string(L, F, Adj, P, Pad)); -control($e, [A], F, Adj, P, Pad, _Enc, _I) when is_float(A) -> +control($e, [A], F, Adj, P, Pad, _Enc, _Str, _I) when is_float(A) -> fwrite_e(A, F, Adj, P, Pad); -control($f, [A], F, Adj, P, Pad, _Enc, _I) when is_float(A) -> +control($f, [A], F, Adj, P, Pad, _Enc, _Str, _I) when is_float(A) -> fwrite_f(A, F, Adj, P, Pad); -control($g, [A], F, Adj, P, Pad, _Enc, _I) when is_float(A) -> +control($g, [A], F, Adj, P, Pad, _Enc, _Str, _I) when is_float(A) -> fwrite_g(A, F, Adj, P, Pad); -control($b, [A], F, Adj, P, Pad, _Enc, _I) when is_integer(A) -> +control($b, [A], F, Adj, P, Pad, _Enc, _Str, _I) when is_integer(A) -> unprefixed_integer(A, F, Adj, base(P), Pad, true); -control($B, [A], F, Adj, P, Pad, _Enc, _I) when is_integer(A) -> +control($B, [A], F, Adj, P, Pad, _Enc, _Str, _I) when is_integer(A) -> unprefixed_integer(A, F, Adj, base(P), Pad, false); -control($x, [A,Prefix], F, Adj, P, Pad, _Enc, _I) when is_integer(A), +control($x, [A,Prefix], F, Adj, P, Pad, _Enc, _Str, _I) when is_integer(A), is_atom(Prefix) -> prefixed_integer(A, F, Adj, base(P), Pad, atom_to_list(Prefix), true); -control($x, [A,Prefix], F, Adj, P, Pad, _Enc, _I) when is_integer(A) -> +control($x, [A,Prefix], F, Adj, P, Pad, _Enc, _Str, _I) when is_integer(A) -> true = io_lib:deep_char_list(Prefix), %Check if Prefix a character list prefixed_integer(A, F, Adj, base(P), Pad, Prefix, true); -control($X, [A,Prefix], F, Adj, P, Pad, _Enc, _I) when is_integer(A), +control($X, [A,Prefix], F, Adj, P, Pad, _Enc, _Str, _I) when is_integer(A), is_atom(Prefix) -> prefixed_integer(A, F, Adj, base(P), Pad, atom_to_list(Prefix), false); -control($X, [A,Prefix], F, Adj, P, Pad, _Enc, _I) when is_integer(A) -> +control($X, [A,Prefix], F, Adj, P, Pad, _Enc, _Str, _I) when is_integer(A) -> true = io_lib:deep_char_list(Prefix), %Check if Prefix a character list prefixed_integer(A, F, Adj, base(P), Pad, Prefix, false); -control($+, [A], F, Adj, P, Pad, _Enc, _I) when is_integer(A) -> +control($+, [A], F, Adj, P, Pad, _Enc, _Str, _I) when is_integer(A) -> Base = base(P), Prefix = [integer_to_list(Base), $#], prefixed_integer(A, F, Adj, Base, Pad, Prefix, true); -control($#, [A], F, Adj, P, Pad, _Enc, _I) when is_integer(A) -> +control($#, [A], F, Adj, P, Pad, _Enc, _Str, _I) when is_integer(A) -> Base = base(P), Prefix = [integer_to_list(Base), $#], prefixed_integer(A, F, Adj, Base, Pad, Prefix, false); -control($c, [A], F, Adj, P, Pad, unicode, _I) when is_integer(A) -> +control($c, [A], F, Adj, P, Pad, unicode, _Str, _I) when is_integer(A) -> char(A, F, Adj, P, Pad); -control($c, [A], F, Adj, P, Pad, _Enc, _I) when is_integer(A) -> +control($c, [A], F, Adj, P, Pad, _Enc, _Str, _I) when is_integer(A) -> char(A band 255, F, Adj, P, Pad); -control($~, [], F, Adj, P, Pad, _Enc, _I) -> char($~, F, Adj, P, Pad); -control($n, [], F, Adj, P, Pad, _Enc, _I) -> newline(F, Adj, P, Pad); -control($i, [_A], _F, _Adj, _P, _Pad, _Enc, _I) -> []. +control($~, [], F, Adj, P, Pad, _Enc, _Str, _I) -> char($~, F, Adj, P, Pad); +control($n, [], F, Adj, P, Pad, _Enc, _Str, _I) -> newline(F, Adj, P, Pad); +control($i, [_A], _F, _Adj, _P, _Pad, _Enc, _Str, _I) -> []. -ifdef(UNICODE_AS_BINARIES). uniconv(C) -> @@ -256,13 +263,21 @@ term(T, F, Adj, P0, Pad) -> adjust(T, chars(Pad, F-L), Adj) end. -%% print(Term, Depth, Field, Adjust, Precision, PadChar, Indentation) +%% print(Term, Depth, Field, Adjust, Precision, PadChar, Encoding, +%% Indentation) %% Print a term. -print(T, D, none, Adj, P, Pad, I) -> print(T, D, 80, Adj, P, Pad, I); -print(T, D, F, Adj, none, Pad, I) -> print(T, D, F, Adj, I+1, Pad, I); -print(T, D, F, right, P, _Pad, _I) -> - io_lib_pretty:print(T, P, F, D). +print(T, D, none, Adj, P, Pad, E, Str, I) -> + print(T, D, 80, Adj, P, Pad, E, Str, I); +print(T, D, F, Adj, none, Pad, E, Str, I) -> + print(T, D, F, Adj, I+1, Pad, E, Str, I); +print(T, D, F, right, P, _Pad, Enc, Str, _I) -> + Options = [{column, P}, + {line_length, F}, + {depth, D}, + {encoding, Enc}, + {strings, Str}], + io_lib_pretty:print(T, Options). %% fwrite_e(Float, Field, Adjust, Precision, PadChar) @@ -554,6 +569,25 @@ iolist_to_chars([]) -> iolist_to_chars(B) when is_binary(B) -> binary_to_list(B). +%% cdata() :: clist() | cbinary() +%% clist() :: maybe_improper_list(char() | cbinary() | clist(), +%% cbinary() | nil()) +%% cbinary() :: unicode:unicode_binary() | unicode:latin1_binary() + +%% cdata_to_chars(cdata()) -> io_lib:deep_char_list() + +cdata_to_chars([C|Cs]) when is_integer(C), C >= $\000 -> + [C | cdata_to_chars(Cs)]; +cdata_to_chars([I|Cs]) -> + [cdata_to_chars(I) | cdata_to_chars(Cs)]; +cdata_to_chars([]) -> + []; +cdata_to_chars(B) when is_binary(B) -> + case catch unicode:characters_to_list(B) of + L when is_list(L) -> L; + _ -> binary_to_list(B) + end. + %% string(String, Field, Adjust, Precision, PadChar) string(S, none, _Adj, none, _Pad) -> S; @@ -608,7 +642,7 @@ prefixed_integer(Int, F, Adj, Base, Pad, Prefix, Lowercase) term([Prefix|S], F, Adj, none, Pad) end. -%% char(Char, Field, Adjust, Precision, PadChar) -> [Char]. +%% char(Char, Field, Adjust, Precision, PadChar) -> string(). char(C, none, _Adj, none, _Pad) -> [C]; char(C, F, _Adj, none, _Pad) -> chars(C, F); diff --git a/lib/stdlib/src/io_lib_fread.erl b/lib/stdlib/src/io_lib_fread.erl index ded1346097..92a34995b8 100644 --- a/lib/stdlib/src/io_lib_fread.erl +++ b/lib/stdlib/src/io_lib_fread.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2013. 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 @@ -22,7 +22,7 @@ -export([fread/2,fread/3]). --import(lists, [reverse/1,reverse/2]). +-import(lists, [reverse/1]). -define(is_whitespace(C), ((C) =:= $\s orelse (C) =:= $\t @@ -43,7 +43,7 @@ | {'done', Result, LeftOverChars :: string()}, Result :: {'ok', InputList :: io_lib:chars()} | 'eof' - | {'error', What :: term()}. + | {'error', {'read', What :: io_lib:fread_error()}}. fread([], Chars, Format) -> %%io:format("FREAD: ~w `~s'~n", [Format,Chars]), diff --git a/lib/stdlib/src/io_lib_pretty.erl b/lib/stdlib/src/io_lib_pretty.erl index 169410796b..7637ad7a3d 100644 --- a/lib/stdlib/src/io_lib_pretty.erl +++ b/lib/stdlib/src/io_lib_pretty.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2013. 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 @@ -33,43 +33,76 @@ %% print(Term, Column, LineLength, Depth) -> [Chars] %% Depth = -1 gives unlimited print depth. Use io_lib:write for atomic terms. +-spec print(term()) -> io_lib:chars(). + print(Term) -> print(Term, 1, 80, -1). %% print(Term, RecDefFun) -> [Chars] %% print(Term, Depth, RecDefFun) -> [Chars] %% RecDefFun = fun(Tag, NoFields) -> [FieldTag] | no -%% Used by the shell for printing records. +%% Used by the shell for printing records and for Unicode. + +-type rec_print_fun() :: fun((Tag :: atom(), NFields :: non_neg_integer()) -> + no | [FieldName :: atom()]). +-type column() :: integer(). +-type line_length() :: pos_integer(). +-type depth() :: integer(). +-type max_chars() :: integer(). + +-type chars() :: io_lib:chars(). +-type option() :: {column, column()} + | {line_length, line_length()} + | {depth, depth()} + | {max_chars, max_chars()} + | {record_print_fun, rec_print_fun()} + | {strings, boolean()} + | {encoding, latin1 | utf8 | unicode}. +-type options() :: [option()]. + +-spec print(term(), rec_print_fun()) -> chars(); + (term(), options()) -> chars(). + +print(Term, Options) when is_list(Options) -> + Col = proplists:get_value(column, Options, 1), + Ll = proplists:get_value(line_length, Options, 80), + D = proplists:get_value(depth, Options, -1), + M = proplists:get_value(max_chars, Options, -1), + RecDefFun = proplists:get_value(record_print_fun, Options, no_fun), + Encoding = proplists:get_value(encoding, Options, epp:default_encoding()), + Strings = proplists:get_value(strings, Options, true), + print(Term, Col, Ll, D, M, RecDefFun, Encoding, Strings); print(Term, RecDefFun) -> print(Term, -1, RecDefFun). +-spec print(term(), depth(), rec_print_fun()) -> chars(). + print(Term, Depth, RecDefFun) -> print(Term, 1, 80, Depth, RecDefFun). +-spec print(term(), column(), line_length(), depth()) -> chars(). + print(Term, Col, Ll, D) -> - print(Term, Col, Ll, D, _M=-1, no_fun). + print(Term, Col, Ll, D, _M=-1, no_fun, latin1, true). +-spec print(term(), column(), line_length(), depth(), rec_print_fun()) -> + chars(). print(Term, Col, Ll, D, RecDefFun) -> print(Term, Col, Ll, D, _M=-1, RecDefFun). -print(_, _, _, 0, _M, _RF) -> "..."; -print(Term, Col, Ll, D, M, RecDefFun) when Col =< 0 -> - print(Term, 1, Ll, D, M, RecDefFun); -print(Term, Col, Ll, D, M0, RecDefFun) when is_tuple(Term); - is_list(Term) -> - If = {_S, Len} = print_length(Term, D, RecDefFun), - M = max_cs(M0, Len), - if - Len < Ll - Col, Len =< M -> - write(If); - true -> - TInd = while_fail([-1, 4], - fun(I) -> cind(If, Col, Ll, M, I, 0, 0) end, - 1), - pp(If, Col, Ll, M, TInd, indent(Col), 0, 0) - end; -print(<<_/bitstring>>=Term, Col, Ll, D, M0, RecDefFun) -> - If = {_S, Len} = print_length(Term, D, RecDefFun), +-spec print(term(), column(), line_length(), depth(), max_chars(), + rec_print_fun()) -> chars(). + +print(Term, Col, Ll, D, M, RecDefFun) -> + print(Term, Col, Ll, D, M, RecDefFun, latin1, true). + +print(_, _, _, 0, _M, _RF, _Enc, _Str) -> "..."; +print(Term, Col, Ll, D, M, RecDefFun, Enc, Str) when Col =< 0 -> + print(Term, 1, Ll, D, M, RecDefFun, Enc, Str); +print(Term, Col, Ll, D, M0, RecDefFun, Enc, Str) when is_tuple(Term); + is_list(Term); + is_bitstring(Term) -> + If = {_S, Len} = print_length(Term, D, RecDefFun, Enc, Str), M = max_cs(M0, Len), if Len < Ll - Col, Len =< M -> @@ -80,7 +113,7 @@ print(<<_/bitstring>>=Term, Col, Ll, D, M0, RecDefFun) -> 1), pp(If, Col, Ll, M, TInd, indent(Col), 0, 0) end; -print(Term, _Col, _Ll, _D, _M, _RF) -> +print(Term, _Col, _Ll, _D, _M, _RF, _Enc, _Str) -> io_lib:write(Term). %%% @@ -294,50 +327,56 @@ write_tail(E, S) -> %% counted but need to be added later. %% D =/= 0 -print_length([], _D, _RF) -> +print_length([], _D, _RF, _Enc, _Str) -> {"[]", 2}; -print_length({}, _D, _RF) -> +print_length({}, _D, _RF, _Enc, _Str) -> {"{}", 2}; -print_length(List, D, RF) when is_list(List) -> - case printable_list(List, D) of +print_length(List, D, RF, Enc, Str) when is_list(List) -> + case Str andalso printable_list(List, D, Enc) of true -> - S = io_lib:write_string(List, $"), %" + S = write_string(List, Enc), {S, length(S)}; %% Truncated lists could break some existing code. % {true, Prefix} -> - % S = io_lib:write_string(Prefix, $"), %" + % S = write_string(Prefix, Enc), % {[S | "..."], 3 + length(S)}; false -> - print_length_list(List, D, RF) + print_length_list(List, D, RF, Enc, Str) end; -print_length(Fun, _D, _RF) when is_function(Fun) -> +print_length(Fun, _D, _RF, _Enc, _Str) when is_function(Fun) -> S = io_lib:write(Fun), {S, iolist_size(S)}; -print_length(R, D, RF) when is_atom(element(1, R)), - is_function(RF) -> +print_length(R, D, RF, Enc, Str) when is_atom(element(1, R)), + is_function(RF) -> case RF(element(1, R), tuple_size(R) - 1) of no -> - print_length_tuple(R, D, RF); + print_length_tuple(R, D, RF, Enc, Str); RDefs -> - print_length_record(R, D, RF, RDefs) + print_length_record(R, D, RF, RDefs, Enc, Str) end; -print_length(Tuple, D, RF) when is_tuple(Tuple) -> - print_length_tuple(Tuple, D, RF); -print_length(<<>>, _D, _RF) -> +print_length(Tuple, D, RF, Enc, Str) when is_tuple(Tuple) -> + print_length_tuple(Tuple, D, RF, Enc, Str); +print_length(<<>>, _D, _RF, _Enc, _Str) -> {"<<>>", 4}; -print_length(<<_/bitstring>>, 1, _RF) -> +print_length(<<_/bitstring>>, 1, _RF, _Enc, _Str) -> {"<<...>>", 7}; -print_length(<<_/bitstring>>=Bin, D, _RF) -> +print_length(<<_/bitstring>>=Bin, D, _RF, Enc, Str) -> case bit_size(Bin) rem 8 of 0 -> D1 = D - 1, - case printable_bin(Bin, D1) of - List when is_list(List) -> - S = io_lib:write_string(List, $"), + case Str andalso printable_bin(Bin, D1, Enc) of + {true, List} when is_list(List) -> + S = io_lib:write_string(List, $"), %" {[$<,$<,S,$>,$>], 4 + length(S)}; - {true, Prefix} -> - S = io_lib:write_string(Prefix, $"), - {[$<,$<, S | "...>>"], 4 + length(S)}; + {false, List} when is_list(List) -> + S = io_lib:write_string(List, $"), %" + {[$<,$<,S,"/utf8>>"], 9 + length(S)}; + {true, true, Prefix} -> + S = io_lib:write_string(Prefix, $"), %" + {[$<,$<, S | "...>>"], 7 + length(S)}; + {false, true, Prefix} -> + S = io_lib:write_string(Prefix, $"), %" + {[$<,$<, S | "/utf8...>>"], 12 + length(S)}; false -> S = io_lib:write(Bin, D), {{bin,S}, iolist_size(S)} @@ -346,51 +385,53 @@ print_length(<<_/bitstring>>=Bin, D, _RF) -> S = io_lib:write(Bin, D), {{bin,S}, iolist_size(S)} end; -print_length(Term, _D, _RF) -> +print_length(Term, _D, _RF, _Enc, _Str) -> S = io_lib:write(Term), - {S, iolist_size(S)}. + {S, lists:flatlength(S)}. -print_length_tuple(_Tuple, 1, _RF) -> +print_length_tuple(_Tuple, 1, _RF, _Enc, _Str) -> {"{...}", 5}; -print_length_tuple(Tuple, D, RF) -> - L = print_length_list1(tuple_to_list(Tuple), D, RF), +print_length_tuple(Tuple, D, RF, Enc, Str) -> + L = print_length_list1(tuple_to_list(Tuple), D, RF, Enc, Str), IsTagged = is_atom(element(1, Tuple)) and (tuple_size(Tuple) > 1), {{tuple,IsTagged,L}, list_length(L, 2)}. -print_length_record(_Tuple, 1, _RF, _RDefs) -> +print_length_record(_Tuple, 1, _RF, _RDefs, _Enc, _Str) -> {"{...}", 5}; -print_length_record(Tuple, D, RF, RDefs) -> +print_length_record(Tuple, D, RF, RDefs, Enc, Str) -> Name = [$# | io_lib:write_atom(element(1, Tuple))], NameL = length(Name), - L = print_length_fields(RDefs, D - 1, tl(tuple_to_list(Tuple)), RF), + Elements = tl(tuple_to_list(Tuple)), + L = print_length_fields(RDefs, D - 1, Elements, RF, Enc, Str), {{record, [{Name,NameL} | L]}, list_length(L, NameL + 2)}. -print_length_fields([], _D, [], _RF) -> +print_length_fields([], _D, [], _RF, _Enc, _Str) -> []; -print_length_fields(_, 1, _, _RF) -> +print_length_fields(_, 1, _, _RF, _Enc, _Str) -> {dots, 3}; -print_length_fields([Def | Defs], D, [E | Es], RF) -> - [print_length_field(Def, D - 1, E, RF) | - print_length_fields(Defs, D - 1, Es, RF)]. +print_length_fields([Def | Defs], D, [E | Es], RF, Enc, Str) -> + [print_length_field(Def, D - 1, E, RF, Enc, Str) | + print_length_fields(Defs, D - 1, Es, RF, Enc, Str)]. -print_length_field(Def, D, E, RF) -> +print_length_field(Def, D, E, RF, Enc, Str) -> Name = io_lib:write_atom(Def), - {S, L} = print_length(E, D, RF), + {S, L} = print_length(E, D, RF, Enc, Str), NameL = length(Name) + 3, {{field, Name, NameL, {S, L}}, NameL + L}. -print_length_list(List, D, RF) -> - L = print_length_list1(List, D, RF), +print_length_list(List, D, RF, Enc, Str) -> + L = print_length_list1(List, D, RF, Enc, Str), {{list, L}, list_length(L, 2)}. -print_length_list1([], _D, _RF) -> +print_length_list1([], _D, _RF, _Enc, _Str) -> []; -print_length_list1(_, 1, _RF) -> +print_length_list1(_, 1, _RF, _Enc, _Str) -> {dots, 3}; -print_length_list1([E | Es], D, RF) -> - [print_length(E, D - 1, RF) | print_length_list1(Es, D - 1, RF)]; -print_length_list1(E, D, RF) -> - print_length(E, D - 1, RF). +print_length_list1([E | Es], D, RF, Enc, Str) -> + [print_length(E, D - 1, RF, Enc, Str) | + print_length_list1(Es, D - 1, RF, Enc, Str)]; +print_length_list1(E, D, RF, Enc, Str) -> + print_length(E, D - 1, RF, Enc, Str). list_length([], Acc) -> Acc; @@ -409,51 +450,53 @@ list_length_tail({_, Len}, Acc) -> %% ?CHARS printable characters has depth 1. -define(CHARS, 4). -printable_list(L, D) when D < 0 -> - io_lib:printable_list(L); -printable_list(_L, 1) -> +printable_list(_L, 1, _Enc) -> false; -printable_list(L, _D) -> +printable_list(L, _D, latin1) -> + io_lib:printable_latin1_list(L); +printable_list(L, _D, _Uni) -> io_lib:printable_list(L). -%% Truncated lists could break some existing code. -% printable_list(L, D) -> -% Len = ?CHARS * (D - 1), -% case printable_list1(L, Len) of -% all -> -% true; -% N when is_integer(N), Len - N >= D - 1 -> -% {L1, _} = lists:split(Len - N, L), -% {true, L1}; -% N when is_integer(N) -> -% false -% end. - -printable_bin(Bin, D) when D >= 0, ?CHARS * D =< byte_size(Bin) -> - printable_bin(Bin, erlang:min(?CHARS * D, byte_size(Bin)), D); -printable_bin(Bin, D) -> - printable_bin(Bin, byte_size(Bin), D). - -printable_bin(Bin, Len, D) -> + +printable_bin(Bin, D, Enc) when D >= 0, ?CHARS * D =< byte_size(Bin) -> + printable_bin(Bin, erlang:min(?CHARS * D, byte_size(Bin)), D, Enc); +printable_bin(Bin, D, Enc) -> + printable_bin(Bin, byte_size(Bin), D, Enc). + +printable_bin(Bin, Len, D, latin1) -> N = erlang:min(20, Len), L = binary_to_list(Bin, 1, N), - case printable_list1(L, N) of + case printable_latin1_list(L, N) of all when N =:= byte_size(Bin) -> - L; - all when N =:= Len -> % N < byte_size(Bin) {true, L}; + all when N =:= Len -> % N < byte_size(Bin) + {true, true, L}; all -> case printable_bin1(Bin, 1 + N, Len - N) of 0 when byte_size(Bin) =:= Len -> - binary_to_list(Bin); + {true, binary_to_list(Bin)}; NC when D > 0, Len - NC >= D -> - {true, binary_to_list(Bin, 1, Len - NC)}; + {true, true, binary_to_list(Bin, 1, Len - NC)}; NC when is_integer(NC) -> false end; NC when is_integer(NC), D > 0, N - NC >= D -> - {true, binary_to_list(Bin, 1, N - NC)}; + {true, true, binary_to_list(Bin, 1, N - NC)}; NC when is_integer(NC) -> false + end; +printable_bin(Bin, Len, D, _Uni) -> + case valid_utf8(Bin,Len) of + true -> + case printable_unicode(Bin, Len, [], io:printable_range()) of + {_, <<>>, L} -> + {byte_size(Bin) =:= length(L), L}; + {NC, Bin1, L} when D > 0, Len - NC >= D -> + {byte_size(Bin)-byte_size(Bin1) =:= length(L), true, L}; + {_NC, _Bin, _L} -> + false + end; + false -> + printable_bin(Bin, Len, D, latin1) end. printable_bin1(_Bin, _Start, 0) -> @@ -461,7 +504,7 @@ printable_bin1(_Bin, _Start, 0) -> printable_bin1(Bin, Start, Len) -> N = erlang:min(10000, Len), L = binary_to_list(Bin, Start, Start + N - 1), - case printable_list1(L, N) of + case printable_latin1_list(L, N) of all -> printable_bin1(Bin, Start + N, Len - N); NC when is_integer(NC) -> @@ -469,20 +512,60 @@ printable_bin1(Bin, Start, Len) -> end. %% -> all | integer() >=0. Adopted from io_lib.erl. -% printable_list1([_ | _], 0) -> 0; -printable_list1([C | Cs], N) when is_integer(C), C >= $\s, C =< $~ -> - printable_list1(Cs, N - 1); -printable_list1([C | Cs], N) when is_integer(C), C >= $\240, C =< $\377 -> - printable_list1(Cs, N - 1); -printable_list1([$\n | Cs], N) -> printable_list1(Cs, N - 1); -printable_list1([$\r | Cs], N) -> printable_list1(Cs, N - 1); -printable_list1([$\t | Cs], N) -> printable_list1(Cs, N - 1); -printable_list1([$\v | Cs], N) -> printable_list1(Cs, N - 1); -printable_list1([$\b | Cs], N) -> printable_list1(Cs, N - 1); -printable_list1([$\f | Cs], N) -> printable_list1(Cs, N - 1); -printable_list1([$\e | Cs], N) -> printable_list1(Cs, N - 1); -printable_list1([], _) -> all; -printable_list1(_, N) -> N. +% printable_latin1_list([_ | _], 0) -> 0; +printable_latin1_list([C | Cs], N) when C >= $\s, C =< $~ -> + printable_latin1_list(Cs, N - 1); +printable_latin1_list([C | Cs], N) when C >= $\240, C =< $\377 -> + printable_latin1_list(Cs, N - 1); +printable_latin1_list([$\n | Cs], N) -> printable_latin1_list(Cs, N - 1); +printable_latin1_list([$\r | Cs], N) -> printable_latin1_list(Cs, N - 1); +printable_latin1_list([$\t | Cs], N) -> printable_latin1_list(Cs, N - 1); +printable_latin1_list([$\v | Cs], N) -> printable_latin1_list(Cs, N - 1); +printable_latin1_list([$\b | Cs], N) -> printable_latin1_list(Cs, N - 1); +printable_latin1_list([$\f | Cs], N) -> printable_latin1_list(Cs, N - 1); +printable_latin1_list([$\e | Cs], N) -> printable_latin1_list(Cs, N - 1); +printable_latin1_list([], _) -> all; +printable_latin1_list(_, N) -> N. + +valid_utf8(<<>>,_) -> + true; +valid_utf8(_,0) -> + true; +valid_utf8(<<_/utf8, R/binary>>,N) -> + valid_utf8(R,N-1); +valid_utf8(_,_) -> + false. + +printable_unicode(<<C/utf8, R/binary>>=Bin, I, L, Range) when I > 0 -> + case printable_char(C,Range) of + true -> + printable_unicode(R, I - 1, [C | L],Range); + false -> + {I, Bin, lists:reverse(L)} + end; +printable_unicode(Bin, I, L,_) -> + {I, Bin, lists:reverse(L)}. + +printable_char($\n,_) -> true; +printable_char($\r,_) -> true; +printable_char($\t,_) -> true; +printable_char($\v,_) -> true; +printable_char($\b,_) -> true; +printable_char($\f,_) -> true; +printable_char($\e,_) -> true; +printable_char(C,latin1) -> + C >= $\s andalso C =< $~ orelse + C >= 16#A0 andalso C =< 16#FF; +printable_char(C,unicode) -> + C >= $\s andalso C =< $~ orelse + C >= 16#A0 andalso C < 16#D800 orelse + C > 16#DFFF andalso C < 16#FFFE orelse + C > 16#FFFF andalso C =< 16#10FFFF. + +write_string(S, latin1) -> + io_lib:write_latin1_string(S, $"); %" +write_string(S, _Uni) -> + io_lib:write_string(S, $"). %" %% Throw 'no_good' if the indentation exceeds half the line length %% unless there is room for M characters on the line. diff --git a/lib/stdlib/src/lib.erl b/lib/stdlib/src/lib.erl index cf4b87d7eb..8351376691 100644 --- a/lib/stdlib/src/lib.erl +++ b/lib/stdlib/src/lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2012. All Rights Reserved. +%% Copyright Ericsson AB 1996-2013. 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 @@ -21,8 +21,9 @@ -export([flush_receive/0, error_message/2, progname/0, nonl/1, send/2, sendw/2, eval_str/1]). --export([format_exception/6, format_stacktrace/4, - format_call/4, format_fun/1]). +-export([format_exception/6, format_exception/7, + format_stacktrace/4, format_stacktrace/5, + format_call/4, format_call/5, format_fun/1]). -spec flush_receive() -> 'ok'. @@ -43,7 +44,7 @@ flush_receive() -> Args :: [term()]. error_message(Format, Args) -> - io:format(<<"** ~s **\n">>, [io_lib:format(Format, Args)]). + io:format(<<"** ~ts **\n">>, [io_lib:format(Format, Args)]). %% Return the name of the script that starts (this) erlang %% @@ -83,10 +84,14 @@ sendw(To, Msg) -> %% eval_str(InStr) -> {ok, OutStr} | {error, ErrStr'} %% InStr must represent a body +%% Note: If InStr is a binary it has to be a Latin-1 string. +%% If you have a UTF-8 encoded binary you have to call +%% unicode:characters_to_list/1 before the call to eval_str(). -define(result(F,D), lists:flatten(io_lib:format(F, D))). --spec eval_str(string() | binary()) -> {'ok', string()} | {'error', string()}. +-spec eval_str(string() | unicode:latin1_binary()) -> + {'ok', string()} | {'error', string()}. eval_str(Str) when is_list(Str) -> case erl_scan:tokens([], Str, 0) of @@ -104,12 +109,12 @@ eval_str(Str) when is_list(Str) -> {error, ?result("*** eval: ~p", [Other])} end; {error, {_Line, Mod, Args}} -> - Msg = ?result("*** ~s",[Mod:format_error(Args)]), + Msg = ?result("*** ~ts",[Mod:format_error(Args)]), {error, Msg} end; false -> {error, ?result("Non-white space found after " - "end-of-form :~s", [Rest])} + "end-of-form :~ts", [Rest])} end end; eval_str(Bin) when is_binary(Bin) -> @@ -128,32 +133,49 @@ all_white(_) -> false. %% as indentation whenever newline has been inserted); %% Class, Reason and StackTrace are the exception; %% FormatFun = fun(Term, I) -> iolist() formats terms; -%% StackFun = fun(Mod, Fun, Arity) -> bool() is used for trimming the +%% StackFun = fun(Mod, Fun, Arity) -> boolean() is used for trimming the %% end of the stack (typically calls to erl_eval are skipped). -format_exception(I, Class, Reason, StackTrace, StackFun, FormatFun) +format_exception(I, Class, Reason, StackTrace, StackFun, FormatFun) -> + format_exception(I, Class, Reason, StackTrace, StackFun, FormatFun, + latin1). + +%% -> iolist() | unicode:charlist() (no \n at end) +%% FormatFun = fun(Term, I) -> iolist() | unicode:charlist(). +format_exception(I, Class, Reason, StackTrace, StackFun, FormatFun, Encoding) when is_integer(I), I >= 1, is_function(StackFun, 3), is_function(FormatFun, 2) -> S = n_spaces(I-1), {Term,Trace1,Trace} = analyze_exception(Class, Reason, StackTrace), - Expl0 = explain_reason(Term, Class, Trace1, FormatFun, S), - Expl = io_lib:fwrite(<<"~s~s">>, [exited(Class), Expl0]), - case format_stacktrace1(S, Trace, FormatFun, StackFun) of + Expl0 = explain_reason(Term, Class, Trace1, FormatFun, S, Encoding), + FormatString = case Encoding of + latin1 -> "~s~s"; + _ -> "~s~ts" + end, + Expl = io_lib:fwrite(FormatString, [exited(Class), Expl0]), + case format_stacktrace1(S, Trace, FormatFun, StackFun, Encoding) of [] -> Expl; Stack -> [Expl, $\n, Stack] end. %% -> iolist() (no \n at end) -format_stacktrace(I, StackTrace, StackFun, FormatFun) +format_stacktrace(I, StackTrace, StackFun, FormatFun) -> + format_stacktrace(I, StackTrace, StackFun, FormatFun, latin1). + +%% -> iolist() | unicode:charlist() (no \n at end) +format_stacktrace(I, StackTrace, StackFun, FormatFun, Encoding) when is_integer(I), I >= 1, is_function(StackFun, 3), is_function(FormatFun, 2) -> S = n_spaces(I-1), - format_stacktrace1(S, StackTrace, FormatFun, StackFun). + format_stacktrace1(S, StackTrace, FormatFun, StackFun, Encoding). %% -> iolist() (no \n at end) -format_call(I, ForMForFun, As, FormatFun) when is_integer(I), I >= 1, - is_list(As), - is_function(FormatFun, 2) -> - format_call("", n_spaces(I-1), ForMForFun, As, FormatFun). +format_call(I, ForMForFun, As, FormatFun) -> + format_call(I, ForMForFun, As, FormatFun, latin1). + +%% -> iolist() | unicode:charlist() (no \n at end) +format_call(I, ForMForFun, As, FormatFun, Enc) + when is_integer(I), I >= 1, is_list(As), is_function(FormatFun, 2) -> + format_call("", n_spaces(I-1), ForMForFun, As, FormatFun, Enc). %% -> iolist() (no \n at end) format_fun(Fun) when is_function(Fun) -> @@ -204,79 +226,80 @@ is_stacktrace(_) -> false. %% ERTS exit codes (some of them are also returned by erl_eval): -explain_reason(badarg, error, [], _PF, _S) -> +explain_reason(badarg, error, [], _PF, _S, _Enc) -> <<"bad argument">>; -explain_reason({badarg,V}, error=Cl, [], PF, S) -> % orelse, andalso +explain_reason({badarg,V}, error=Cl, [], PF, S, _Enc) -> % orelse, andalso format_value(V, <<"bad argument: ">>, Cl, PF, S); -explain_reason(badarith, error, [], _PF, _S) -> +explain_reason(badarith, error, [], _PF, _S, _Enc) -> <<"an error occurred when evaluating an arithmetic expression">>; -explain_reason({badarity,{Fun,As}}, error, [], _PF, _S) +explain_reason({badarity,{Fun,As}}, error, [], _PF, _S, _Enc) when is_function(Fun) -> %% Only the arity is displayed, not the arguments As. io_lib:fwrite(<<"~s called with ~s">>, [format_fun(Fun), argss(length(As))]); -explain_reason({badfun,Term}, error=Cl, [], PF, S) -> +explain_reason({badfun,Term}, error=Cl, [], PF, S, _Enc) -> format_value(Term, <<"bad function ">>, Cl, PF, S); -explain_reason({badmatch,Term}, error=Cl, [], PF, S) -> - format_value(Term, <<"no match of right hand side value ">>, Cl, PF, S); -explain_reason({case_clause,V}, error=Cl, [], PF, S) -> +explain_reason({badmatch,Term}, error=Cl, [], PF, S, _Enc) -> + Str = <<"no match of right hand side value ">>, + format_value(Term, Str, Cl, PF, S); +explain_reason({case_clause,V}, error=Cl, [], PF, S, _Enc) -> %% "there is no case clause with a true guard sequence and a %% pattern matching..." format_value(V, <<"no case clause matching ">>, Cl, PF, S); -explain_reason(function_clause, error, [{F,A}], _PF, _S) -> +explain_reason(function_clause, error, [{F,A}], _PF, _S, _Enc) -> %% Shell commands FAs = io_lib:fwrite(<<"~w/~w">>, [F, A]), [<<"no function clause matching call to ">> | FAs]; -explain_reason(function_clause, error=Cl, [{M,F,As,Loc}], PF, S) -> +explain_reason(function_clause, error=Cl, [{M,F,As,Loc}], PF, S, Enc) -> Str = <<"no function clause matching ">>, - [format_errstr_call(Str, Cl, {M,F}, As, PF, S),$\s|location(Loc)]; -explain_reason(if_clause, error, [], _PF, _S) -> + [format_errstr_call(Str, Cl, {M,F}, As, PF, S, Enc),$\s|location(Loc)]; +explain_reason(if_clause, error, [], _PF, _S, _Enc) -> <<"no true branch found when evaluating an if expression">>; -explain_reason(noproc, error, [], _PF, _S) -> +explain_reason(noproc, error, [], _PF, _S, _Enc) -> <<"no such process or port">>; -explain_reason(notalive, error, [], _PF, _S) -> +explain_reason(notalive, error, [], _PF, _S, _Enc) -> <<"the node cannot be part of a distributed system">>; -explain_reason(system_limit, error, [], _PF, _S) -> +explain_reason(system_limit, error, [], _PF, _S, _Enc) -> <<"a system limit has been reached">>; -explain_reason(timeout_value, error, [], _PF, _S) -> +explain_reason(timeout_value, error, [], _PF, _S, _Enc) -> <<"bad receive timeout value">>; -explain_reason({try_clause,V}, error=Cl, [], PF, S) -> +explain_reason({try_clause,V}, error=Cl, [], PF, S, _Enc) -> %% "there is no try clause with a true guard sequence and a %% pattern matching..." format_value(V, <<"no try clause matching ">>, Cl, PF, S); -explain_reason(undef, error, [{M,F,A,_}], _PF, _S) -> +explain_reason(undef, error, [{M,F,A,_}], _PF, _S, _Enc) -> %% Only the arity is displayed, not the arguments, if there are any. io_lib:fwrite(<<"undefined function ~s">>, [mfa_to_string(M, F, n_args(A))]); -explain_reason({shell_undef,F,A,_}, error, [], _PF, _S) -> +explain_reason({shell_undef,F,A,_}, error, [], _PF, _S, _Enc) -> %% Give nicer reports for undefined shell functions %% (but not when the user actively calls shell_default:F(...)). io_lib:fwrite(<<"undefined shell command ~s/~w">>, [F, n_args(A)]); %% Exit codes returned by erl_eval only: -explain_reason({argument_limit,_Fun}, error, [], _PF, _S) -> +explain_reason({argument_limit,_Fun}, error, [], _PF, _S, _Enc) -> io_lib:fwrite(<<"limit of number of arguments to interpreted function" " exceeded">>, []); -explain_reason({bad_filter,V}, error=Cl, [], PF, S) -> +explain_reason({bad_filter,V}, error=Cl, [], PF, S, _Enc) -> format_value(V, <<"bad filter ">>, Cl, PF, S); -explain_reason({bad_generator,V}, error=Cl, [], PF, S) -> +explain_reason({bad_generator,V}, error=Cl, [], PF, S, _Enc) -> format_value(V, <<"bad generator ">>, Cl, PF, S); -explain_reason({unbound,V}, error, [], _PF, _S) -> +explain_reason({unbound,V}, error, [], _PF, _S, _Enc) -> io_lib:fwrite(<<"variable ~w is unbound">>, [V]); %% Exit codes local to the shell module (restricted shell): -explain_reason({restricted_shell_bad_return, V}, exit=Cl, [], PF, S) -> +explain_reason({restricted_shell_bad_return, V}, exit=Cl, [], PF, S, _Enc) -> Str = <<"restricted shell module returned bad value ">>, format_value(V, Str, Cl, PF, S); explain_reason({restricted_shell_disallowed,{ForMF,As}}, - exit=Cl, [], PF, S) -> + exit=Cl, [], PF, S, Enc) -> %% ForMF can be a fun, but not a shell fun. Str = <<"restricted shell does not allow ">>, - format_errstr_call(Str, Cl, ForMF, As, PF, S); -explain_reason(restricted_shell_started, exit, [], _PF, _S) -> + format_errstr_call(Str, Cl, ForMF, As, PF, S, Enc); +explain_reason(restricted_shell_started, exit, [], _PF, _S, _Enc) -> <<"restricted shell starts now">>; -explain_reason(restricted_shell_stopped, exit, [], _PF, _S) -> +explain_reason(restricted_shell_stopped, exit, [], _PF, _S, _Enc) -> <<"restricted shell stopped">>; %% Other exit code: -explain_reason(Reason, Class, [], PF, S) -> +explain_reason(Reason, Class, [], PF, S, _Enc) -> PF(Reason, (iolist_size(S)+1) + exited_size(Class)). n_args(A) when is_integer(A) -> @@ -293,28 +316,28 @@ argss(2) -> argss(I) -> io_lib:fwrite(<<"~w arguments">>, [I]). -format_stacktrace1(S0, Stack0, PF, SF) -> +format_stacktrace1(S0, Stack0, PF, SF, Enc) -> Stack1 = lists:dropwhile(fun({M,F,A,_}) -> SF(M, F, A) end, lists:reverse(Stack0)), S = [" " | S0], Stack = lists:reverse(Stack1), - format_stacktrace2(S, Stack, 1, PF). + format_stacktrace2(S, Stack, 1, PF, Enc). -format_stacktrace2(S, [{M,F,A,L}|Fs], N, PF) when is_integer(A) -> +format_stacktrace2(S, [{M,F,A,L}|Fs], N, PF, Enc) when is_integer(A) -> [io_lib:fwrite(<<"~s~s ~s ~s">>, [sep(N, S), origin(N, M, F, A), mfa_to_string(M, F, A), location(L)]) - | format_stacktrace2(S, Fs, N + 1, PF)]; -format_stacktrace2(S, [{M,F,As,_}|Fs], N, PF) when is_list(As) -> + | format_stacktrace2(S, Fs, N + 1, PF, Enc)]; +format_stacktrace2(S, [{M,F,As,_}|Fs], N, PF, Enc) when is_list(As) -> A = length(As), CalledAs = [S,<<" called as ">>], - C = format_call("", CalledAs, {M,F}, As, PF), - [io_lib:fwrite(<<"~s~s ~s\n~s~s">>, + C = format_call("", CalledAs, {M,F}, As, PF, Enc), + [io_lib:fwrite(<<"~s~s ~s\n~s~ts">>, [sep(N, S), origin(N, M, F, A), mfa_to_string(M, F, A), CalledAs, C]) - | format_stacktrace2(S, Fs, N + 1, PF)]; -format_stacktrace2(_S, [], _N, _PF) -> + | format_stacktrace2(S, Fs, N + 1, PF, Enc)]; +format_stacktrace2(_S, [], _N, _PF, _Enc) -> "". location(L) -> @@ -338,22 +361,22 @@ origin(1, M, F, A) -> origin(_N, _M, _F, _A) -> <<"in call from">>. -format_errstr_call(ErrStr, Class, ForMForFun, As, PF, Pre0) -> +format_errstr_call(ErrStr, Class, ForMForFun, As, PF, Pre0, Enc) -> Pre1 = [Pre0 | n_spaces(exited_size(Class))], - format_call(ErrStr, Pre1, ForMForFun, As, PF). + format_call(ErrStr, Pre1, ForMForFun, As, PF, Enc). -format_call(ErrStr, Pre1, ForMForFun, As, PF) -> +format_call(ErrStr, Pre1, ForMForFun, As, PF, Enc) -> Arity = length(As), [ErrStr | case is_op(ForMForFun, Arity) of {yes,Op} -> - format_op(ErrStr, Pre1, Op, As, PF); + format_op(ErrStr, Pre1, Op, As, PF, Enc); no -> MFs = mf_to_string(ForMForFun, Arity), I1 = iolist_size([Pre1,ErrStr|MFs]), - S1 = pp_arguments(PF, As, I1), - S2 = pp_arguments(PF, As, iolist_size([Pre1|MFs])), - Long = count_nl(pp_arguments(PF, [a2345,b2345], I1)) > 0, + S1 = pp_arguments(PF, As, I1, Enc), + S2 = pp_arguments(PF, As, iolist_size([Pre1|MFs]), Enc), + Long = count_nl(pp_arguments(PF, [a2345,b2345], I1, Enc)) > 0, case Long or (count_nl(S2) < count_nl(S1)) of true -> [$\n, Pre1, MFs, S2]; @@ -362,11 +385,11 @@ format_call(ErrStr, Pre1, ForMForFun, As, PF) -> end end]. -format_op(ErrStr, Pre, Op, [A1], PF) -> +format_op(ErrStr, Pre, Op, [A1], PF, _Enc) -> OpS = io_lib:fwrite(<<"~s ">>, [Op]), I1 = iolist_size([ErrStr,Pre,OpS]), [OpS | PF(A1, I1+1)]; -format_op(ErrStr, Pre, Op, [A1, A2], PF) -> +format_op(ErrStr, Pre, Op, [A1, A2], PF, Enc) -> I1 = iolist_size([ErrStr,Pre]), S1 = PF(A1, I1+1), S2 = PF(A2, I1+1), @@ -377,33 +400,40 @@ format_op(ErrStr, Pre, Op, [A1, A2], PF) -> [S1,Pre1,OpS,Pre1|S2]; false -> OpS2 = io_lib:fwrite(<<" ~s ">>, [Op]), - S2_2 = PF(A2, iolist_size([ErrStr,Pre,S1|OpS2])+1), + Size1 = iolist_size([ErrStr,Pre|OpS2]), + {Size2,S1_2} = size(Enc, S1), + S2_2 = PF(A2, Size1+Size2+1), case count_nl(S2) < count_nl(S2_2) of true -> - [S1,Pre1,OpS,Pre1|S2]; + [S1_2,Pre1,OpS,Pre1|S2]; false -> - [S1,OpS2|S2_2] + [S1_2,OpS2|S2_2] end end. -pp_arguments(PF, As, I) -> - case {As, io_lib:printable_list(As)} of +pp_arguments(PF, As, I, Enc) -> + case {As, printable_list(Enc, As)} of {[Int | T], true} -> L = integer_to_list(Int), Ll = length(L), A = list_to_atom(lists:duplicate(Ll, $a)), - S0 = binary_to_list(iolist_to_binary(PF([A | T], I+1))), - brackets_to_parens([$[,L,string:sub_string(S0, 2+Ll)]); + S0 = unicode:characters_to_list(PF([A | T], I+1), Enc), + brackets_to_parens([$[,L,string:sub_string(S0, 2+Ll)], Enc); _ -> - brackets_to_parens(PF(As, I+1)) + brackets_to_parens(PF(As, I+1), Enc) end. -brackets_to_parens(S) -> - B = iolist_to_binary(S), +brackets_to_parens(S, Enc) -> + B = unicode:characters_to_binary(S, Enc), Sz = byte_size(B) - 2, <<$[,R:Sz/binary,$]>> = B, [$(,R,$)]. +printable_list(latin1, As) -> + io_lib:printable_latin1_list(As); +printable_list(_, As) -> + io_lib:printable_list(As). + mfa_to_string(M, F, A) -> io_lib:fwrite(<<"~s/~w">>, [mf_to_string({M, F}, A), A]). @@ -472,3 +502,10 @@ exited(exit) -> <<"exception exit: ">>; exited(throw) -> <<"exception throw: ">>. + +size(latin1, S) -> + {iolist_size(S),S}; +size(_, S0) -> + S = unicode:characters_to_list(S0, unicode), + true = is_list(S), + {length(S),S}. diff --git a/lib/stdlib/src/lists.erl b/lib/stdlib/src/lists.erl index e73c087753..961c060019 100644 --- a/lib/stdlib/src/lists.erl +++ b/lib/stdlib/src/lists.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 @@ -33,9 +33,6 @@ keysort/2, keymerge/3, rkeymerge/3, rukeymerge/3, ukeysort/2, ukeymerge/3, keymap/3]). -%% Bifs: member/2, reverse/2 -%% Bifs: keymember/3, keysearch/3, keyfind/3 - -export([merge/3, rmerge/3, sort/2, umerge/3, rumerge/3, usort/2]). -export([all/2,any/2,map/2,flatmap/2,foldl/3,foldr/3,filter/2, @@ -43,6 +40,60 @@ mapfoldl/3,mapfoldr/3,foreach/2,takewhile/2,dropwhile/2,splitwith/2, split/2]). +%%% BIFs +-export([keyfind/3, keymember/3, keysearch/3, member/2, reverse/2]). + +%% Shadowed by erl_bif_types: lists:keyfind/3 +-spec keyfind(Key, N, TupleList) -> Tuple | false when + Key :: term(), + N :: pos_integer(), + TupleList :: [Tuple], + Tuple :: tuple(). + +keyfind(_, _, _) -> + erlang:nif_error(undef). + +%% Shadowed by erl_bif_types: lists:keymember/3 +-spec keymember(Key, N, TupleList) -> boolean() when + Key :: term(), + N :: pos_integer(), + TupleList :: [Tuple], + Tuple :: tuple(). + +keymember(_, _, _) -> + erlang:nif_error(undef). + +%% Shadowed by erl_bif_types: lists:keysearch/3 +-spec keysearch(Key, N, TupleList) -> {value, Tuple} | false when + Key :: term(), + N :: pos_integer(), + TupleList :: [Tuple], + Tuple :: tuple(). + +keysearch(_, _, _) -> + erlang:nif_error(undef). + +%% Shadowed by erl_bif_types: lists:member/2 +-spec member(Elem, List) -> boolean() when + Elem :: T, + List :: [T], + T :: term(). + +member(_, _) -> + erlang:nif_error(undef). + +%% Shadowed by erl_bif_types: lists:reverse/2 +-spec reverse(List1, Tail) -> List2 when + List1 :: [T], + Tail :: term(), + List2 :: [T], + T :: term(). + +reverse(_, _) -> + erlang:nif_error(undef). + +%%% End of BIFs + %% member(X, L) -> (true | false) %% test if X is a member of the list L %% Now a BIF! @@ -84,7 +135,7 @@ append([]) -> []. subtract(L1, L2) -> L1 -- L2. -%% reverse(L) reverse all elements in the list L. Is now a BIF! +%% reverse(L) reverse all elements in the list L. reverse/2 is now a BIF! -spec reverse(List1) -> List2 when List1 :: [T], @@ -581,6 +632,7 @@ flatlength([_|T], L) -> flatlength([], L) -> L. %% keymember(Key, Index, [Tuple]) Now a BIF! +%% keyfind(Key, Index, [Tuple]) A BIF! %% keysearch(Key, Index, [Tuple]) Now a BIF! %% keydelete(Key, Index, [Tuple]) %% keyreplace(Key, Index, [Tuple], NewTuple) @@ -1126,8 +1178,7 @@ rumerge(T1, [H2 | T2]) -> %% takewhile(Predicate, List) %% dropwhile(Predicate, List) %% splitwith(Predicate, List) -%% for list programming. Function here is a 'fun'. For backward compatibility, -%% {Module,Function} is still accepted. +%% for list programming. Function here is a 'fun'. %% %% The name zf is a joke! %% diff --git a/lib/stdlib/src/log_mf_h.erl b/lib/stdlib/src/log_mf_h.erl index f7f128dac7..19b555a48c 100644 --- a/lib/stdlib/src/log_mf_h.erl +++ b/lib/stdlib/src/log_mf_h.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 @@ -25,6 +25,8 @@ -export([init/1, handle_event/2, handle_info/2, terminate/2]). -export([handle_call/2, code_change/3]). +-export_type([args/0]). + %%----------------------------------------------------------------- -type b() :: non_neg_integer(). diff --git a/lib/stdlib/src/math.erl b/lib/stdlib/src/math.erl index b2ea6195c5..c3fb684ec3 100644 --- a/lib/stdlib/src/math.erl +++ b/lib/stdlib/src/math.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 @@ -20,6 +20,116 @@ -export([pi/0]). +%%% BIFs + +-export([sin/1, cos/1, tan/1, asin/1, acos/1, atan/1, atan2/2, sinh/1, + cosh/1, tanh/1, asinh/1, acosh/1, atanh/1, exp/1, log/1, + log10/1, pow/2, sqrt/1, erf/1, erfc/1]). + +-spec acos(X) -> float() when + X :: number(). +acos(_) -> + erlang:nif_error(undef). + +-spec acosh(X) -> float() when + X :: number(). +acosh(_) -> + erlang:nif_error(undef). + +-spec asin(X) -> float() when + X :: number(). +asin(_) -> + erlang:nif_error(undef). + +-spec asinh(X) -> float() when + X :: number(). +asinh(_) -> + erlang:nif_error(undef). + +-spec atan(X) -> float() when + X :: number(). +atan(_) -> + erlang:nif_error(undef). + +-spec atan2(X, Y) -> float() when + X :: number(), + Y :: number(). +atan2(_, _) -> + erlang:nif_error(undef). + +-spec atanh(X) -> float() when + X :: number(). +atanh(_) -> + erlang:nif_error(undef). + +-spec cos(X) -> float() when + X :: number(). +cos(_) -> + erlang:nif_error(undef). + +-spec cosh(X) -> float() when + X :: number(). +cosh(_) -> + erlang:nif_error(undef). + +-spec erf(X) -> float() when + X :: number(). +erf(_) -> + erlang:nif_error(undef). + +-spec erfc(X) -> float() when + X :: number(). +erfc(_) -> + erlang:nif_error(undef). + +-spec exp(X) -> float() when + X :: number(). +exp(_) -> + erlang:nif_error(undef). + +-spec log(X) -> float() when + X :: number(). +log(_) -> + erlang:nif_error(undef). + +-spec log10(X) -> float() when + X :: number(). +log10(_) -> + erlang:nif_error(undef). + +-spec pow(X, Y) -> float() when + X :: number(), + Y :: number(). +pow(_, _) -> + erlang:nif_error(undef). + +-spec sin(X) -> float() when + X :: number(). +sin(_) -> + erlang:nif_error(undef). + +-spec sinh(X) -> float() when + X :: number(). +sinh(_) -> + erlang:nif_error(undef). + +-spec sqrt(X) -> float() when + X :: number(). +sqrt(_) -> + erlang:nif_error(undef). + +-spec tan(X) -> float() when + X :: number(). +tan(_) -> + erlang:nif_error(undef). + +-spec tanh(X) -> float() when + X :: number(). +tanh(_) -> + erlang:nif_error(undef). + +%%% End of BIFs + -spec pi() -> float(). pi() -> 3.1415926535897932. diff --git a/lib/stdlib/src/ms_transform.erl b/lib/stdlib/src/ms_transform.erl index 4389fd457c..4e2ce39ec2 100644 --- a/lib/stdlib/src/ms_transform.erl +++ b/lib/stdlib/src/ms_transform.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2011. All Rights Reserved. +%% Copyright Ericsson AB 2002-2013. 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 @@ -100,7 +100,7 @@ format_error({?ERR_GUARDREMOTECALL, Module, Name, Arithy}) -> [Module,Name,Arithy])); format_error({?ERR_GUARDELEMENT, Str}) -> lists:flatten( - io_lib:format("the language element ~s (in guard) cannot be translated " + io_lib:format("the language element ~ts (in guard) cannot be translated " "into match_spec", [Str])); format_error({?ERR_GUARDBINCONSTRUCT, Var}) -> lists:flatten( @@ -126,7 +126,7 @@ format_error({?ERR_BODYREMOTECALL, Module, Name, Arithy}) -> [Module,Name,Arithy])); format_error({?ERR_BODYELEMENT, Str}) -> lists:flatten( - io_lib:format("the language element ~s (in body) cannot be translated " + io_lib:format("the language element ~ts (in body) cannot be translated " "into match_spec", [Str])); format_error({?ERR_BODYBINCONSTRUCT, Var}) -> lists:flatten( diff --git a/lib/stdlib/src/otp_internal.erl b/lib/stdlib/src/otp_internal.erl index b9fbef9ed0..a4f4035c79 100644 --- a/lib/stdlib/src/otp_internal.erl +++ b/lib/stdlib/src/otp_internal.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2012. All Rights Reserved. +%% Copyright Ericsson AB 1999-2013. 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 @@ -78,89 +78,89 @@ obsolete_1(snmp, N, A) -> end; obsolete_1(snmpm, agent_info, 3) -> - {deprecated, {snmpm, agent_info, 2}, "R16B"}; + {removed, {snmpm, agent_info, 2}, "R16B"}; obsolete_1(snmpm, update_agent_info, 5) -> - {deprecated, {snmpm, update_agent_info, 4}, "R16B"}; + {removed, {snmpm, update_agent_info, 4}, "R16B"}; obsolete_1(snmpm, g, 3) -> - {deprecated, {snmpm, sync_get, 3}, "R16B"}; + {removed, {snmpm, sync_get, 3}, "R16B"}; obsolete_1(snmpm, g, 4) -> - {deprecated, {snmpm, sync_get, [3,4]}, "R16B"}; + {removed, {snmpm, sync_get, [3,4]}, "R16B"}; obsolete_1(snmpm, g, 5) -> - {deprecated, {snmpm, sync_get, [4,5]}, "R16B"}; + {removed, {snmpm, sync_get, [4,5]}, "R16B"}; obsolete_1(snmpm, g, 6) -> - {deprecated, {snmpm, sync_get, [5,6]}, "R16B"}; + {removed, {snmpm, sync_get, [5,6]}, "R16B"}; obsolete_1(snmpm, g, 7) -> - {deprecated, {snmpm, sync_get, 6}, "R16B"}; + {removed, {snmpm, sync_get, 6}, "R16B"}; obsolete_1(snmpm, ag, 3) -> - {deprecated, {snmpm, async_get, 3}, "R16B"}; + {removed, {snmpm, async_get, 3}, "R16B"}; obsolete_1(snmpm, ag, 4) -> - {deprecated, {snmpm, async_get, [3,4]}, "R16B"}; + {removed, {snmpm, async_get, [3,4]}, "R16B"}; obsolete_1(snmpm, ag, 5) -> - {deprecated, {snmpm, async_get, [4,5]}, "R16B"}; + {removed, {snmpm, async_get, [4,5]}, "R16B"}; obsolete_1(snmpm, ag, 6) -> - {deprecated, {snmpm, async_get, [5,6]}, "R16B"}; + {removed, {snmpm, async_get, [5,6]}, "R16B"}; obsolete_1(snmpm, ag, 7) -> - {deprecated, {snmpm, async_get, 6}, "R16B"}; + {removed, {snmpm, async_get, 6}, "R16B"}; obsolete_1(snmpm, gn, 3) -> - {deprecated, {snmpm, sync_get_next, 3}, "R16B"}; + {removed, {snmpm, sync_get_next, 3}, "R16B"}; obsolete_1(snmpm, gn, 4) -> - {deprecated, {snmpm, sync_get_next, [3,4]}, "R16B"}; + {removed, {snmpm, sync_get_next, [3,4]}, "R16B"}; obsolete_1(snmpm, gn, 5) -> - {deprecated, {snmpm, sync_get_next, [4,5]}, "R16B"}; + {removed, {snmpm, sync_get_next, [4,5]}, "R16B"}; obsolete_1(snmpm, gn, 6) -> - {deprecated, {snmpm, sync_get_next, [5,6]}, "R16B"}; + {removed, {snmpm, sync_get_next, [5,6]}, "R16B"}; obsolete_1(snmpm, gn, 7) -> - {deprecated, {snmpm, sync_get_next, 6}, "R16B"}; + {removed, {snmpm, sync_get_next, 6}, "R16B"}; obsolete_1(snmpm, agn, 3) -> - {deprecated, {snmpm, async_get_next, 3}, "R16B"}; + {removed, {snmpm, async_get_next, 3}, "R16B"}; obsolete_1(snmpm, agn, 4) -> - {deprecated, {snmpm, async_get_next, [3,4]}, "R16B"}; + {removed, {snmpm, async_get_next, [3,4]}, "R16B"}; obsolete_1(snmpm, agn, 5) -> - {deprecated, {snmpm, async_get_next, [4,5]}, "R16B"}; + {removed, {snmpm, async_get_next, [4,5]}, "R16B"}; obsolete_1(snmpm, agn, 6) -> - {deprecated, {snmpm, async_get_next, [5,6]}, "R16B"}; + {removed, {snmpm, async_get_next, [5,6]}, "R16B"}; obsolete_1(snmpm, agn, 7) -> - {deprecated, {snmpm, async_get_next, 6}, "R16B"}; + {removed, {snmpm, async_get_next, 6}, "R16B"}; obsolete_1(snmpm, s, 3) -> - {deprecated, {snmpm, sync_set, 3}, "R16B"}; + {removed, {snmpm, sync_set, 3}, "R16B"}; obsolete_1(snmpm, s, 4) -> - {deprecated, {snmpm, sync_set, [3,4]}, "R16B"}; + {removed, {snmpm, sync_set, [3,4]}, "R16B"}; obsolete_1(snmpm, s, 5) -> - {deprecated, {snmpm, sync_set, [4,5]}, "R16B"}; + {removed, {snmpm, sync_set, [4,5]}, "R16B"}; obsolete_1(snmpm, s, 6) -> - {deprecated, {snmpm, sync_set, [5,6]}, "R16B"}; + {removed, {snmpm, sync_set, [5,6]}, "R16B"}; obsolete_1(snmpm, s, 7) -> - {deprecated, {snmpm, sync_set, 6}, "R16B"}; + {removed, {snmpm, sync_set, 6}, "R16B"}; obsolete_1(snmpm, as, 3) -> - {deprecated, {snmpm, async_set, 3}, "R16B"}; + {removed, {snmpm, async_set, 3}, "R16B"}; obsolete_1(snmpm, as, 4) -> - {deprecated, {snmpm, async_set, [3,4]}, "R16B"}; + {removed, {snmpm, async_set, [3,4]}, "R16B"}; obsolete_1(snmpm, as, 5) -> - {deprecated, {snmpm, async_set, [4,5]}, "R16B"}; + {removed, {snmpm, async_set, [4,5]}, "R16B"}; obsolete_1(snmpm, as, 6) -> - {deprecated, {snmpm, async_set, [5,6]}, "R16B"}; + {removed, {snmpm, async_set, [5,6]}, "R16B"}; obsolete_1(snmpm, as, 7) -> - {deprecated, {snmpm, async_set, 6}, "R16B"}; + {removed, {snmpm, async_set, 6}, "R16B"}; obsolete_1(snmpm, gb, 5) -> - {deprecated, {snmpm, sync_get_bulk, 5}, "R16B"}; + {removed, {snmpm, sync_get_bulk, 5}, "R16B"}; obsolete_1(snmpm, gb, 6) -> - {deprecated, {snmpm, sync_get_bulk, [5,6]}, "R16B"}; + {removed, {snmpm, sync_get_bulk, [5,6]}, "R16B"}; obsolete_1(snmpm, gb, 7) -> - {deprecated, {snmpm, sync_get_bulk, [6,7]}, "R16B"}; + {removed, {snmpm, sync_get_bulk, [6,7]}, "R16B"}; obsolete_1(snmpm, gb, 8) -> - {deprecated, {snmpm, sync_get_bulk, [7,8]}, "R16B"}; + {removed, {snmpm, sync_get_bulk, [7,8]}, "R16B"}; obsolete_1(snmpm, gb, 9) -> - {deprecated, {snmpm, sync_get_bulk, 8}, "R16B"}; + {removed, {snmpm, sync_get_bulk, 8}, "R16B"}; obsolete_1(snmpm, agb, 5) -> - {deprecated, {snmpm, async_get_bulk, 5}, "R16B"}; + {removed, {snmpm, async_get_bulk, 5}, "R16B"}; obsolete_1(snmpm, agb, 6) -> - {deprecated, {snmpm, async_get_bulk, [5,6]}, "R16B"}; + {removed, {snmpm, async_get_bulk, [5,6]}, "R16B"}; obsolete_1(snmpm, agb, 7) -> - {deprecated, {snmpm, async_get_bulk, [6,7]}, "R16B"}; + {removed, {snmpm, async_get_bulk, [6,7]}, "R16B"}; obsolete_1(snmpm, agb, 8) -> - {deprecated, {snmpm, async_get_bulk, [7,8]}, "R16B"}; + {removed, {snmpm, async_get_bulk, [7,8]}, "R16B"}; obsolete_1(snmpm, agb, 9) -> - {deprecated, {snmpm, async_get_bulk, 8}, "R16B"}; + {removed, {snmpm, async_get_bulk, 8}, "R16B"}; %% *** MEGACO *** @@ -347,18 +347,57 @@ obsolete_1(docb_xml_check, _, _) -> obsolete_1(asn1rt, F, _) when F == load_driver; F == unload_driver -> {deprecated,"deprecated (will be removed in R16A); has no effect as drivers are no longer used."}; obsolete_1(ssl, pid, 1) -> - {deprecated,"deprecated (will be removed in R17); is no longer needed"}; + {removed,"was removed in R16; is no longer needed"}; obsolete_1(inviso, _, _) -> - {deprecated,"the inviso application has been deprecated and will be removed in R16"}; + {removed,"the inviso application was removed in R16"}; %% Added in R15B01. obsolete_1(gs, _, _) -> - {deprecated,"the gs application has been deprecated and will be removed in R16; use the wx application instead"}; + {deprecated,"the gs application has been deprecated and will be removed in R17; use the wx application instead"}; obsolete_1(ssh, sign_data, 2) -> {deprecated,"deprecated (will be removed in R16A); use public_key:pem_decode/1, public_key:pem_entry_decode/1 " "and public_key:sign/3 instead"}; obsolete_1(ssh, verify_data, 3) -> {deprecated,"deprecated (will be removed in R16A); use public_key:ssh_decode/1, and public_key:verify/4 instead"}; + +%% Added in R16 +obsolete_1(wxCalendarCtrl, enableYearChange, _) -> %% wx bug documented? + {deprecated,"deprecated function not available in wxWidgets-2.9 and later"}; +obsolete_1(wxDC, computeScaleAndOrigin, 1) -> + {deprecated,"deprecated function not available in wxWidgets-2.9 and later"}; +obsolete_1(wxClientDC, new, 0) -> + {deprecated,"deprecated function not available in wxWidgets-2.9 and later"}; +obsolete_1(wxPaintDC, new, 0) -> + {deprecated,"deprecated function not available in wxWidgets-2.9 and later"}; +obsolete_1(wxWindowDC, new, 0) -> + {deprecated,"deprecated function not available in wxWidgets-2.9 and later"}; +obsolete_1(wxGraphicsContext, createLinearGradientBrush, 7) -> + {deprecated,"deprecated function not available in wxWidgets-2.9 and later"}; +obsolete_1(wxGraphicsContext, createRadialGradientBrush, 8) -> + {deprecated,"deprecated function not available in wxWidgets-2.9 and later"}; +obsolete_1(wxGraphicsRenderer, createLinearGradientBrush, 7) -> + {deprecated,"deprecated function not available in wxWidgets-2.9 and later"}; +obsolete_1(wxGraphicsRenderer, createRadialGradientBrush, 8) -> + {deprecated,"deprecated function not available in wxWidgets-2.9 and later"}; +obsolete_1(wxGridCellEditor, endEdit, 4) -> + {deprecated,"deprecated function not available in wxWidgets-2.9 and later"}; +obsolete_1(wxGridCellEditor, paintBackground, 3) -> + {deprecated,"deprecated function not available in wxWidgets-2.9 and later"}; +obsolete_1(wxIdleEvent, canSend, 1) -> + {deprecated,"deprecated function not available in wxWidgets-2.9 and later"}; +obsolete_1(wxMDIClientWindow, new, 1) -> + {deprecated,"deprecated function not available in wxWidgets-2.9 and later"}; +obsolete_1(wxMDIClientWindow, new, 2) -> + {deprecated,"deprecated function not available in wxWidgets-2.9 and later"}; +obsolete_1(wxPostScriptDC, getResolution, 0) -> + {deprecated,"deprecated function not available in wxWidgets-2.9 and later"}; +obsolete_1(wxPostScriptDC, setResolution, 1) -> + {deprecated,"deprecated function not available in wxWidgets-2.9 and later"}; +obsolete_1(wxCursor, new, 3) -> + {deprecated,"deprecated function not available in wxWidgets-2.9 and later"}; +obsolete_1(wxCursor, new, 4) -> + {deprecated,"deprecated function not available in wxWidgets-2.9 and later"}; + obsolete_1(_, _, _) -> no. diff --git a/lib/stdlib/src/proc_lib.erl b/lib/stdlib/src/proc_lib.erl index 02bcbb5a60..1eb6fc2e86 100644 --- a/lib/stdlib/src/proc_lib.erl +++ b/lib/stdlib/src/proc_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2013. 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 @@ -29,7 +29,8 @@ start/3, start/4, start/5, start_link/3, start_link/4, start_link/5, hibernate/3, init_ack/1, init_ack/2, - init_p/3,init_p/5,format/1,initial_call/1,translate_initial_call/1]). + init_p/3,init_p/5,format/1,format/2,initial_call/1, + translate_initial_call/1]). %% Internal exports. -export([wake_up/3]). @@ -184,6 +185,17 @@ check_for_monitor(SpawnOpts) -> false end. +spawn_mon(M,F,A) -> + Parent = get_my_name(), + Ancestors = get_ancestors(), + erlang:spawn_monitor(?MODULE, init_p, [Parent,Ancestors,M,F,A]). + +spawn_opt_mon(M, F, A, Opts) when is_atom(M), is_atom(F), is_list(A) -> + Parent = get_my_name(), + Ancestors = get_ancestors(), + check_for_monitor(Opts), + erlang:spawn_opt(?MODULE, init_p, [Parent,Ancestors,M,F,A], [monitor|Opts]). + -spec hibernate(Module, Function, Args) -> no_return() when Module :: module(), Function :: atom(), @@ -270,8 +282,8 @@ start(M, F, A) when is_atom(M), is_atom(F), is_list(A) -> Ret :: term() | {error, Reason :: term()}. start(M, F, A, Timeout) when is_atom(M), is_atom(F), is_list(A) -> - Pid = ?MODULE:spawn(M, F, A), - sync_wait(Pid, Timeout). + PidRef = spawn_mon(M, F, A), + sync_wait_mon(PidRef, Timeout). -spec start(Module, Function, Args, Time, SpawnOpts) -> Ret when Module :: module(), @@ -282,8 +294,8 @@ start(M, F, A, Timeout) when is_atom(M), is_atom(F), is_list(A) -> Ret :: term() | {error, Reason :: term()}. start(M, F, A, Timeout, SpawnOpts) when is_atom(M), is_atom(F), is_list(A) -> - Pid = ?MODULE:spawn_opt(M, F, A, SpawnOpts), - sync_wait(Pid, Timeout). + PidRef = spawn_opt_mon(M, F, A, SpawnOpts), + sync_wait_mon(PidRef, Timeout). -spec start_link(Module, Function, Args) -> Ret when Module :: module(), @@ -330,6 +342,23 @@ sync_wait(Pid, Timeout) -> {error, timeout} end. +sync_wait_mon({Pid, Ref}, Timeout) -> + receive + {ack, Pid, Return} -> + erlang:demonitor(Ref, [flush]), + Return; + {'DOWN', Ref, _Type, Pid, Reason} -> + {error, Reason}; + {'EXIT', Pid, Reason} -> %% link as spawn_opt? + erlang:demonitor(Ref, [flush]), + {error, Reason} + after Timeout -> + erlang:demonitor(Ref, [flush]), + exit(Pid, kill), + flush(Pid), + {error, timeout} + end. + -spec flush(pid()) -> 'true'. flush(Pid) -> @@ -664,34 +693,41 @@ check(Res) -> Res. -spec format(CrashReport) -> string() when CrashReport :: [term()]. - -format([OwnReport,LinkReport]) -> - OwnFormat = format_report(OwnReport), - LinkFormat = format_report(LinkReport), - S = io_lib:format(" crasher:~n~s neighbours:~n~s",[OwnFormat,LinkFormat]), - lists:flatten(S). - -format_report(Rep) when is_list(Rep) -> - format_rep(Rep); -format_report(Rep) -> - io_lib:format("~p~n", [Rep]). - -format_rep([{initial_call,InitialCall}|Rep]) -> - [format_mfa(InitialCall)|format_rep(Rep)]; -format_rep([{error_info,{Class,Reason,StackTrace}}|Rep]) -> - [format_exception(Class, Reason, StackTrace)|format_rep(Rep)]; -format_rep([{Tag,Data}|Rep]) -> - [format_tag(Tag, Data)|format_rep(Rep)]; -format_rep(_) -> +format(CrashReport) -> + format(CrashReport, latin1). + +-spec format(CrashReport, Encoding) -> string() when + CrashReport :: [term()], + Encoding :: latin1 | unicode | utf8. + +format([OwnReport,LinkReport], Encoding) -> + OwnFormat = format_report(OwnReport, Encoding), + LinkFormat = format_report(LinkReport, Encoding), + Str = io_lib:format(" crasher:~n~ts neighbours:~n~ts", + [OwnFormat, LinkFormat]), + lists:flatten(Str). + +format_report(Rep, Enc) when is_list(Rep) -> + format_rep(Rep,Enc); +format_report(Rep, Enc) -> + io_lib:format("~"++modifier(Enc)++"p~n", [Rep]). + +format_rep([{initial_call,InitialCall}|Rep], Enc) -> + [format_mfa(InitialCall)|format_rep(Rep, Enc)]; +format_rep([{error_info,{Class,Reason,StackTrace}}|Rep], Enc) -> + [format_exception(Class, Reason, StackTrace, Enc)|format_rep(Rep, Enc)]; +format_rep([{Tag,Data}|Rep], Enc) -> + [format_tag(Tag, Data)|format_rep(Rep, Enc)]; +format_rep(_, _Enc) -> []. -format_exception(Class, Reason, StackTrace) -> - PF = pp_fun(), +format_exception(Class, Reason, StackTrace, Enc) -> + PF = pp_fun(Enc), StackFun = fun(M, _F, _A) -> (M =:= erl_eval) or (M =:= ?MODULE) end, %% EI = " exception: ", EI = " ", [EI, lib:format_exception(1+length(EI), Class, Reason, - StackTrace, StackFun, PF), "\n"]. + StackTrace, StackFun, PF, Enc), "\n"]. format_mfa({M,F,Args}=StartF) -> try @@ -703,10 +739,14 @@ format_mfa({M,F,Args}=StartF) -> format_tag(initial_call, StartF) end. -pp_fun() -> +pp_fun(Enc) -> + P = modifier(Enc) ++ "p", fun(Term, I) -> - io_lib:format("~." ++ integer_to_list(I) ++ "p", [Term]) + io_lib:format("~." ++ integer_to_list(I) ++ P, [Term]) end. format_tag(Tag, Data) -> io_lib:format(" ~p: ~80.18p~n", [Tag, Data]). + +modifier(latin1) -> ""; +modifier(_) -> "t". diff --git a/lib/stdlib/src/proplists.erl b/lib/stdlib/src/proplists.erl index e3eda5d932..634724019f 100644 --- a/lib/stdlib/src/proplists.erl +++ b/lib/stdlib/src/proplists.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2011. All Rights Reserved. +%% Copyright Ericsson AB 2001-2013. 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 @@ -51,20 +51,21 @@ -export_type([property/0, proplist/0]). --type property() :: atom() | tuple(). +-type property() :: atom() | tuple(). -type proplist() :: [property()]. %% --------------------------------------------------------------------- %% @doc Creates a normal form (minimal) representation of a property. If -%% <code>P</code> is <code>{Key, true}</code> where <code>Key</code> is -%% an atom, this returns <code>Key</code>, otherwise the whole term -%% <code>P</code> is returned. +%% <code>PropertyIn</code> is <code>{Key, true}</code> where +%% <code>Key</code> is an atom, this returns <code>Key</code>, otherwise +%% the whole term <code>PropertyIn</code> is returned. %% %% @see property/2 --spec property(Property) -> Property when - Property :: property(). +-spec property(PropertyIn) -> PropertyOut when + PropertyIn :: property(), + PropertyOut :: property(). property({Key, true}) when is_atom(Key) -> Key; @@ -92,13 +93,14 @@ property(Key, Value) -> %% --------------------------------------------------------------------- -%% @doc Unfolds all occurences of atoms in <code>List</code> to tuples +%% @doc Unfolds all occurences of atoms in <code>ListIn</code> to tuples %% <code>{Atom, true}</code>. %% %% @see compact/1 --spec unfold(List) -> List when - List :: [term()]. +-spec unfold(ListIn) -> ListOut when + ListIn :: [term()], + ListOut :: [term()]. unfold([P | Ps]) -> if is_atom(P) -> @@ -110,16 +112,17 @@ unfold([]) -> []. %% @doc Minimizes the representation of all entries in the list. This is -%% equivalent to <code>[property(P) || P <- List]</code>. +%% equivalent to <code>[property(P) || P <- ListIn]</code>. %% %% @see unfold/1 %% @see property/1 --spec compact(List) -> List when - List :: [property()]. +-spec compact(ListIn) -> ListOut when + ListIn :: [property()], + ListOut :: [property()]. -compact(List) -> - [property(P) || P <- List]. +compact(ListIn) -> + [property(P) || P <- ListIn]. %% --------------------------------------------------------------------- @@ -199,7 +202,7 @@ is_defined(_Key, []) -> -spec get_value(Key, List) -> term() when Key :: term(), - List :: List::[term()]. + List :: [term()]. get_value(Key, List) -> get_value(Key, List, undefined). @@ -272,9 +275,10 @@ get_all_values(_Key, []) -> %% %% @see get_all_values/2 --spec append_values(Key, List) -> List when +-spec append_values(Key, ListIn) -> ListOut when Key :: term(), - List :: [term()]. + ListIn :: [term()], + ListOut :: [term()]. append_values(Key, [P | Ps]) -> if is_atom(P), P =:= Key -> @@ -357,7 +361,7 @@ get_keys([], Keys) -> -spec delete(Key, List) -> List when Key :: term(), - List::[term()]. + List :: [term()]. delete(Key, [P | Ps]) -> if is_atom(P), P =:= Key -> @@ -374,7 +378,7 @@ delete(_, []) -> %% --------------------------------------------------------------------- %% @doc Substitutes keys of properties. For each entry in -%% <code>List</code>, if it is associated with some key <code>K1</code> +%% <code>ListIn</code>, if it is associated with some key <code>K1</code> %% such that <code>{K1, K2}</code> occurs in <code>Aliases</code>, the %% key of the entry is changed to <code>Key2</code>. If the same %% <code>K1</code> occurs more than once in <code>Aliases</code>, only @@ -388,10 +392,11 @@ delete(_, []) -> %% @see substitute_negations/2 %% @see normalize/2 --spec substitute_aliases(Aliases, List) -> List when +-spec substitute_aliases(Aliases, ListIn) -> ListOut when Aliases :: [{Key, Key}], Key :: term(), - List::[term()]. + ListIn :: [term()], + ListOut :: [term()]. substitute_aliases(As, Props) -> [substitute_aliases_1(As, P) || P <- Props]. @@ -411,13 +416,13 @@ substitute_aliases_1([], P) -> %% --------------------------------------------------------------------- %% @doc Substitutes keys of boolean-valued properties and simultaneously -%% negates their values. For each entry in <code>List</code>, if it is +%% negates their values. For each entry in <code>ListIn</code>, if it is %% associated with some key <code>K1</code> such that <code>{K1, %% K2}</code> occurs in <code>Negations</code>, then if the entry was %% <code>{K1, true}</code> it will be replaced with <code>{K2, %% false}</code>, otherwise it will be replaced with <code>{K2, %% true}</code>, thus changing the name of the option and simultaneously -%% negating the value given by <code>get_bool(List)</code>. If the same +%% negating the value given by <code>get_bool(ListIn)</code>. If the same %% <code>K1</code> occurs more than once in <code>Negations</code>, only %% the first occurrence is used. %% @@ -431,10 +436,11 @@ substitute_aliases_1([], P) -> %% @see substitute_aliases/2 %% @see normalize/2 --spec substitute_negations(Negations, List) -> List when +-spec substitute_negations(Negations, ListIn) -> ListOut when Negations :: [{Key, Key}], Key :: term(), - List :: [term()]. + ListIn :: [term()], + ListOut :: [term()]. substitute_negations(As, Props) -> [substitute_negations_1(As, P) || P <- Props]. @@ -466,11 +472,11 @@ substitute_negations_1([], P) -> %% @doc Expands particular properties to corresponding sets of %% properties (or other terms). For each pair <code>{Property, %% Expansion}</code> in <code>Expansions</code>, if <code>E</code> is -%% the first entry in <code>List</code> with the same key as +%% the first entry in <code>ListIn</code> with the same key as %% <code>Property</code>, and <code>E</code> and <code>Property</code> %% have equivalent normal forms, then <code>E</code> is replaced with %% the terms in <code>Expansion</code>, and any following entries with -%% the same key are deleted from <code>List</code>. +%% the same key are deleted from <code>ListIn</code>. %% %% <p>For example, the following expressions all return <code>[fie, bar, %% baz, fum]</code>: @@ -497,9 +503,10 @@ substitute_negations_1([], P) -> %% %% @see normalize/2 --spec expand(Expansions, List) -> List when +-spec expand(Expansions, ListIn) -> ListOut when Expansions :: [{Property :: property(), Expansion :: [term()]}], - List :: [term()]. + ListIn :: [term()], + ListOut :: [term()]. expand(Es, Ps) when is_list(Ps) -> Es1 = [{property(P), V} || {P, V} <- Es], @@ -599,15 +606,16 @@ flatten([]) -> %% @see expand/2 %% @see compact/1 --spec normalize(List, Stages) -> List when - List :: [term()], +-spec normalize(ListIn, Stages) -> ListOut when + ListIn :: [term()], Stages :: [Operation], Operation :: {'aliases', Aliases} | {'negations', Negations} | {'expand', Expansions}, Aliases :: [{Key, Key}], Negations :: [{Key, Key}], - Expansions :: [{Property :: property(), Expansion :: [term()]}]. + Expansions :: [{Property :: property(), Expansion :: [term()]}], + ListOut :: [term()]. normalize(L, [{aliases, As} | Xs]) -> normalize(substitute_aliases(As, L), Xs); diff --git a/lib/stdlib/src/qlc.erl b/lib/stdlib/src/qlc.erl index 2b691e6abf..48f6622565 100644 --- a/lib/stdlib/src/qlc.erl +++ b/lib/stdlib/src/qlc.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2011. All Rights Reserved. +%% Copyright Ericsson AB 2004-2013. 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 @@ -125,7 +125,7 @@ -define(THROWN_ERROR, {?MODULE, throw_error, _, _}). --export_type([query_handle/0]). +-export_type([query_cursor/0, query_handle/0]). %%% A query handle is a tuple {qlc_handle, Handle} where Handle is one %%% of #qlc_append, #qlc_table, #qlc_sort, and #qlc_lc. @@ -386,25 +386,25 @@ format_error(nomatch_pattern) -> format_error(nomatch_filter) -> io_lib:format("filter evaluates to 'false'", []); format_error({Line, Mod, Reason}) when is_integer(Line) -> - io_lib:format("~p: ~s~n", + io_lib:format("~p: ~ts~n", [Line, lists:flatten(Mod:format_error(Reason))]); %% file_sorter errors format_error({bad_object, FileName}) -> - io_lib:format("the temporary file \"~s\" holding answers is corrupt", + io_lib:format("the temporary file \"~ts\" holding answers is corrupt", [FileName]); format_error(bad_object) -> io_lib:format("the keys could not be extracted from some term", []); format_error({file_error, FileName, Reason}) -> - io_lib:format("\"~s\": ~p~n",[FileName, file:format_error(Reason)]); + io_lib:format("\"~ts\": ~tp~n",[FileName, file:format_error(Reason)]); format_error({premature_eof, FileName}) -> - io_lib:format("\"~s\": end-of-file was encountered inside some binary term", + io_lib:format("\"~ts\": end-of-file was encountered inside some binary term", [FileName]); format_error({tmpdir_usage, Why}) -> io_lib:format("temporary file was needed for ~w~n", [Why]); format_error({error, Module, Reason}) -> Module:format_error(Reason); format_error(E) -> - io_lib:format("~p~n", [E]). + io_lib:format("~tp~n", [E]). -spec(info(QH) -> Info when QH :: query_handle_or_list(), @@ -3709,7 +3709,7 @@ maybe_error_logger(Name, Why) -> Trimmer = fun(M, _F, _A) -> M =:= erl_eval end, Formater = fun(Term, I) -> io_lib:print(Term, I, 80, -1) end, X = lib:format_stacktrace(1, Stacktrace, Trimmer, Formater), - error_logger:Name("qlc: temporary file was needed for ~w\n~s\n", + error_logger:Name("qlc: temporary file was needed for ~w\n~ts\n", [Why, lists:flatten(X)]). expand_stacktrace() -> diff --git a/lib/stdlib/src/qlc_pt.erl b/lib/stdlib/src/qlc_pt.erl index 21504d707b..26bc4d1bdf 100644 --- a/lib/stdlib/src/qlc_pt.erl +++ b/lib/stdlib/src/qlc_pt.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2011. All Rights Reserved. +%% Copyright Ericsson AB 2004-2013. 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,9 +31,6 @@ %% Also in qlc.erl. -define(QLC_Q(L1, L2, L3, L4, LC, Os), {call,L1,{remote,L2,{atom,L3,?APIMOD},{atom,L4,?Q}},[LC | Os]}). --define(QLC_QQ(L1, L2, L3, L4, L5, L6, LC, Os), % packages... - {call,L1,{remote,L2,{record_field,L3,{atom,L4,''}, - {atom,L5,?APIMOD}},{atom,L6,?Q}},[LC | Os]}). -define(IMP_Q(L1, L2, LC, Os), {call,L,{atom,L2,?Q},[LC | Os]}). %% Also in qlc.erl. @@ -217,7 +214,7 @@ compile_messages(Forms, FormsNoShadows, Options, State) -> end, {_,BGens} = qual_fold(BGenF, [], [], FormsNoShadows, State), GenForm = used_genvar_check(FormsNoShadows, State), - ?DEBUG("GenForm = ~s~n", [catch erl_pp:form(GenForm)]), + ?DEBUG("GenForm = ~ts~n", [catch erl_pp:form(GenForm)]), WarnFun = fun(Id, LC, A) -> {tag_lines(LC, get_lcid_no(Id)), A} end, {WForms,ok} = qlc_mapfold(WarnFun, ok, Forms, State), {Es,Ws} = compile_forms(WForms ++ [GenForm], Options), @@ -340,7 +337,7 @@ compile_errors(FormsNoShadows) -> {[], _Warnings} -> []; {Errors, _Warnings} -> - ?DEBUG("got errors ~p~n", [Errors]), + ?DEBUG("got errors ~tp~n", [Errors]), lists:flatmap(fun({_File,Es}) -> Es end, Errors) end. @@ -2186,7 +2183,7 @@ try_ms(E, P, Fltr, State) -> {function,L,foo,0,[{clause,L,[],[],[MS0]}]} = lists:last(X), MS = erl_parse:normalise(var2const(MS0)), XMS = ets:match_spec_compile(MS), - true = is_binary(XMS), + true = ets:is_compiled_ms(XMS), {ok, MS, MS0} end of {'EXIT', _Reason} -> @@ -2475,13 +2472,6 @@ qlcmf(?QLC_Q(L1, L2, L3, L4, LC0, Os0), F, Imp, A0, No0) when length(Os0) < 2 -> NL = make_lcid(L1, No), {T, A} = F(NL, LC, A2), {?QLC_Q(L1, L2, L3, L4, T, Os), A, No + 1}; -qlcmf(?QLC_QQ(L1, L2, L3, L4, L5, L6, LC0, Os0), - F, Imp, A0, No0) when length(Os0) < 2 -> - {Os, A1, No1} = qlcmf(Os0, F, Imp, A0, No0), - {LC, A2, No} = qlcmf(LC0, F, Imp, A1, No1), % nested... - NL = make_lcid(L1, No), - {T, A} = F(NL, LC, A2), - {?QLC_QQ(L1, L2, L3, L4, L5, L6, T, Os), A, No + 1}; qlcmf(?IMP_Q(L1, L2, LC0, Os0), F, Imp=true, A0, No0) when length(Os0) < 2 -> {Os, A1, No1} = qlcmf(Os0, F, Imp, A0, No0), {LC, A2, No} = qlcmf(LC0, F, Imp, A1, No1), % nested... @@ -2752,7 +2742,7 @@ family(L) -> display_forms(Forms) -> io:format("Forms ***~n"), lists:foreach(fun(Form) -> - io:format("~s~n", [catch erl_pp:form(Form)]) + io:format("~ts~n", [catch erl_pp:form(Form)]) end, Forms), io:format("End Forms ***~n"). -else. diff --git a/lib/stdlib/src/queue.erl b/lib/stdlib/src/queue.erl index afe917b151..4bbf5de8a5 100644 --- a/lib/stdlib/src/queue.erl +++ b/lib/stdlib/src/queue.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2013. 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 @@ -472,22 +472,24 @@ init(Q) -> drop_r(Q). -compile({inline, [{r2f,1},{f2r,1}]}). -%% Move all but two from R to F, if there are at least three +%% Move half of elements from R to F, if there are at least three r2f([]) -> {[],[]}; r2f([_]=R) -> {[],R}; r2f([X,Y]) -> {[X],[Y]}; -r2f([X,Y|R]) -> - {[X,Y],lists:reverse(R, [])}. +r2f(List) -> + {FF,RR} = lists:split(length(List) div 2 + 1, List), + {FF,lists:reverse(RR, [])}. -%% Move all but two from F to R, if there are enough +%% Move half of elements from F to R, if there are enough f2r([]) -> {[],[]}; f2r([_]=F) -> {F,[]}; f2r([X,Y]) -> {[Y],[X]}; -f2r([X,Y|F]) -> - {lists:reverse(F, []),[X,Y]}. +f2r(List) -> + {FF,RR} = lists:split(length(List) div 2 + 1, List), + {lists:reverse(RR, []),FF}. diff --git a/lib/stdlib/src/re.erl b/lib/stdlib/src/re.erl index 246d535943..c5109ec455 100644 --- a/lib/stdlib/src/re.erl +++ b/lib/stdlib/src/re.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2011. All Rights Reserved. +%% Copyright Ericsson AB 2008-2012. 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 @@ -30,11 +30,65 @@ | {newline, nl_spec()}| bsr_anycrlf | bsr_unicode. -%% Emulator builtins in this module: -%% re:compile/1 -%% re:compile/2 -%% re:run/2 -%% re:run/3 +%%% BIFs + +-export([compile/1, compile/2, run/2, run/3]). + +-spec compile(Regexp) -> {ok, MP} | {error, ErrSpec} when + Regexp :: iodata(), + MP :: mp(), + ErrSpec :: {ErrString :: string(), Position :: non_neg_integer()}. + +compile(_) -> + erlang:nif_error(undef). + +-spec compile(Regexp, Options) -> {ok, MP} | {error, ErrSpec} when + Regexp :: iodata() | unicode:charlist(), + Options :: [Option], + Option :: compile_option(), + MP :: mp(), + ErrSpec :: {ErrString :: string(), Position :: non_neg_integer()}. + +compile(_, _) -> + erlang:nif_error(undef). + +-spec run(Subject, RE) -> {match, Captured} | nomatch when + Subject :: iodata() | unicode:charlist(), + RE :: mp() | iodata(), + Captured :: [CaptureData], + CaptureData :: {integer(), integer()}. + +run(_, _) -> + erlang:nif_error(undef). + +-spec run(Subject, RE, Options) -> {match, Captured} | + match | + nomatch when + Subject :: iodata() | unicode:charlist(), + RE :: mp() | iodata() | unicode:charlist(), + Options :: [Option], + Option :: anchored | global | notbol | noteol | notempty + | {offset, non_neg_integer()} | + {newline, NLSpec :: nl_spec()} | + bsr_anycrlf | bsr_unicode | {capture, ValueSpec} | + {capture, ValueSpec, Type} | CompileOpt, + Type :: index | list | binary, + ValueSpec :: all | all_but_first | first | none | ValueList, + ValueList :: [ValueID], + ValueID :: integer() | string() | atom(), + CompileOpt :: compile_option(), + Captured :: [CaptureData] | [[CaptureData]], + CaptureData :: {integer(), integer()} + | ListConversionData + | binary(), + ListConversionData :: string() + | {error, string(), binary()} + | {incomplete, string(), binary()}. + +run(_, _, _) -> + erlang:nif_error(undef). + +%%% End of BIFs -spec split(Subject, RE) -> SplitList when Subject :: iodata() | unicode:charlist(), @@ -355,6 +409,12 @@ apply_mlist(Subject,Replacement,Mlist) -> precomp_repl(<<>>) -> []; +precomp_repl(<<$\\,$g,${,Rest/binary>>) when byte_size(Rest) > 0 -> + {NS, <<$},NRest/binary>>} = pick_int(Rest), + [list_to_integer(NS) | precomp_repl(NRest)]; +precomp_repl(<<$\\,$g,Rest/binary>>) when byte_size(Rest) > 0 -> + {NS,NRest} = pick_int(Rest), + [list_to_integer(NS) | precomp_repl(NRest)]; precomp_repl(<<$\\,X,Rest/binary>>) when X < $1 ; X > $9 -> %% Escaped character case precomp_repl(Rest) of diff --git a/lib/stdlib/src/sets.erl b/lib/stdlib/src/sets.erl index 3fd6c81e5f..e6f05b71d4 100644 --- a/lib/stdlib/src/sets.erl +++ b/lib/stdlib/src/sets.erl @@ -1,7 +1,8 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2011. All Rights Reserved. +%% Copyright Ericsson AB 2000-2012. 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 @@ -17,7 +18,7 @@ %% %CopyrightEnd% %% -%% We use the dynamic hashing techniques by Per-�ke Larsson as +%% We use the dynamic hashing techniques by Per-Åke Larsson as %% described in "The Design and Implementation of Dynamic Hashing for %% Sets and Tables in Icon" by Griswold and Townsend. Much of the %% terminology comes from that paper as well. diff --git a/lib/stdlib/src/shell.erl b/lib/stdlib/src/shell.erl index dc450f0ee6..c6c706c3a7 100644 --- a/lib/stdlib/src/shell.erl +++ b/lib/stdlib/src/shell.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2013. 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 @@ -22,7 +22,7 @@ -export([whereis_evaluator/0, whereis_evaluator/1]). -export([start_restricted/1, stop_restricted/0]). -export([local_allowed/3, non_local_allowed/3]). --export([prompt_func/1]). +-export([prompt_func/1, strings/1]). -define(LINEMAX, 30). -define(CHAR_MAX, 60). @@ -30,6 +30,7 @@ -define(DEF_RESULTS, 20). -define(DEF_CATCH_EXCEPTION, false). -define(DEF_PROMPT_FUNC, default). +-define(DEF_STRINGS, true). -define(RECORDS, shell_records). @@ -128,7 +129,7 @@ start_restricted(RShMod) when is_atom(RShMod) -> error_logger:error_report( lists:flatten( io_lib:fwrite( - <<"Restricted shell module ~w not found: ~p\n">>, + "Restricted shell module ~w not found: ~tp\n", [RShMod,What]))), Error end. @@ -139,16 +140,6 @@ stop_restricted() -> application:unset_env(stdlib, restricted_shell), exit(restricted_shell_stopped). -default_packages() -> - []. -%%% ['erl','erl.lang']. - -default_modules() -> - []. -%%% [{pdict, 'erl.lang.proc.pdict'}, -%%% {keylist, 'erl.lang.list.keylist'}, -%%% {debug, 'erl.system.debug'}]. - -spec server(boolean(), boolean()) -> 'terminated'. server(NoCtrlG, StartSync) -> @@ -183,16 +174,7 @@ server(StartSync) -> end end, %% Our spawner has fixed the process groups. - Bs0 = erl_eval:new_bindings(), - Bs = lists:foldl(fun ({K, V}, D) -> - erl_eval:add_binding({module,K}, V, D) - end, - lists:foldl(fun (P, D) -> - import_all(P, D) - end, - Bs0, default_packages()), - default_modules()), - %% io:fwrite("Imported modules: ~p.\n", [erl_eval:bindings(Bs)]), + Bs = erl_eval:new_bindings(), %% Use an Ets table for record definitions. It takes too long to %% send a huge term to and from the evaluator. Ets makes it @@ -204,7 +186,7 @@ server(StartSync) -> %% Check if we're in user restricted mode. RShErr = case application:get_env(stdlib, restricted_shell) of - {ok,RShMod} -> + {ok,RShMod} when is_atom(RShMod) -> io:fwrite(<<"Restricted ">>, []), case code:ensure_loaded(RShMod) of {module,RShMod} -> @@ -212,6 +194,8 @@ server(StartSync) -> {error,What} -> {RShMod,What} end; + {ok, Term} -> + {Term,not_an_atom}; undefined -> undefined end, @@ -230,9 +214,9 @@ server(StartSync) -> ok; {RShMod2,What2} -> io:fwrite( - <<"Warning! Restricted shell module ~w not found: ~p.\n" - "Only the commands q() and init:stop() will be allowed!\n">>, - [RShMod2,What2]), + ("Warning! Restricted shell module ~w not found: ~tp.\n" + "Only the commands q() and init:stop() will be allowed!\n"), + [RShMod2,What2]), application:set_env(stdlib, restricted_shell, ?MODULE) end, @@ -244,7 +228,7 @@ server_loop(N0, Eval_0, Bs00, RT, Ds00, History0, Results0) -> {Eval_1,Bs0,Ds0,Prompt} = prompt(N, Eval_0, Bs00, RT, Ds00), {Res,Eval0} = get_command(Prompt, Eval_1, Bs0, RT, Ds0), case Res of - {ok,Es0,_EndLine} -> + {ok,Es0} -> case expand_hist(Es0, N) of {ok,Es} -> {V,Eval,Bs,Ds} = shell_cmd(Es, Eval0, Bs0, RT, Ds0, cmd), @@ -263,11 +247,11 @@ server_loop(N0, Eval_0, Bs00, RT, Ds00, History0, Results0) -> end, server_loop(N, Eval, Bs, RT, Ds, History, Results); {error,E} -> - fwrite_severity(benign, <<"~s">>, [E]), + fwrite_severity(benign, <<"~ts">>, [E]), server_loop(N0, Eval0, Bs0, RT, Ds0, History0, Results0) end; - {error,{Line,Mod,What},_EndLine} -> - fwrite_severity(benign, <<"~w: ~s">>, + {error,{Line,Mod,What}} -> + fwrite_severity(benign, <<"~w: ~ts">>, [Line, Mod:format_error(What)]), server_loop(N0, Eval0, Bs0, RT, Ds0, History0, Results0); {error,terminated} -> %Io process terminated @@ -277,20 +261,40 @@ server_loop(N0, Eval_0, Bs00, RT, Ds00, History0, Results0) -> exit(Eval0, kill), {_,Eval,_,_} = shell_rep(Eval0, Bs0, RT, Ds0), server_loop(N0, Eval, Bs0, RT, Ds0, History0, Results0); - {error,tokens} -> %Most probably unicode > 255 + {error,tokens} -> %Most probably character > 255 fwrite_severity(benign, <<"~w: Invalid tokens.">>, [N]), server_loop(N0, Eval0, Bs0, RT, Ds0, History0, Results0); - {eof,_EndLine} -> - fwrite_severity(fatal, <<"Terminating erlang (~w)">>, [node()]), - halt(); eof -> fwrite_severity(fatal, <<"Terminating erlang (~w)">>, [node()]), halt() end. get_command(Prompt, Eval, Bs, RT, Ds) -> - Parse = fun() -> exit(io:parse_erl_exprs(Prompt)) end, + Parse = + fun() -> + exit( + case + io:scan_erl_exprs(group_leader(), Prompt, 1) + of + {ok,Toks,_EndPos} -> + erl_parse:parse_exprs(Toks); + {eof,_EndPos} -> + eof; + {error,ErrorInfo,_EndPos} -> + %% Skip the rest of the line: + Opts = io:getopts(), + TmpOpts = lists:keyreplace(echo, 1, Opts, + {echo, false}), + _ = io:setopts(TmpOpts), + _ = io:get_line(''), + _ = io:setopts(Opts), + {error,ErrorInfo}; + Else -> + Else + end + ) + end, Pid = spawn_link(Parse), get_command1(Pid, Eval, Bs, RT, Ds). @@ -337,7 +341,7 @@ get_prompt_func() -> end. bad_prompt_func(M) -> - fwrite_severity(benign, <<"Bad prompt function: ~p">>, [M]). + fwrite_severity(benign, "Bad prompt function: ~tp", [M]). default_prompt(N) -> %% Don't bother flattening the list irrespective of what the @@ -453,7 +457,8 @@ expand_bin_elements([{bin_element,L,E,Sz,Ts}|Fs], C) -> no_command(N) -> throw({error, - io_lib:fwrite(<<"~s: command not found">>, [erl_pp:expr(N)])}). + io_lib:fwrite(<<"~ts: command not found">>, + [erl_pp:expr(N, enc())])}). %% add_cmd(Number, Expressions, Value) %% get_cmd(Number, CurrentCommand) @@ -518,7 +523,7 @@ shell_rep(Ev, Bs0, RT, Ds0) -> {shell_rep,Ev,{value,V,Bs,Ds}} -> {V,Ev,Bs,Ds}; {shell_rep,Ev,{command_error,{Line,M,Error}}} -> - fwrite_severity(benign, <<"~w: ~s">>, + fwrite_severity(benign, <<"~w: ~ts">>, [Line, M:format_error(Error)]), {{'EXIT',Error},Ev,Bs0,Ds0}; {shell_req,Ev,get_cmd} -> @@ -570,9 +575,10 @@ report_exception(Class, Severity, {Reason,Stacktrace}, RT) -> I = iolist_size(Tag) + 1, PF = fun(Term, I1) -> pp(Term, I1, RT) end, SF = fun(M, _F, _A) -> (M =:= erl_eval) or (M =:= ?MODULE) end, - io:requests([{put_chars, Tag}, - {put_chars, - lib:format_exception(I, Class, Reason, Stacktrace, SF, PF)}, + Enc = encoding(), + Str = lib:format_exception(I, Class, Reason, Stacktrace, SF, PF, Enc), + io:requests([{put_chars, latin1, Tag}, + {put_chars, unicode, Str}, nl]). start_eval(Bs, RT, Ds) -> @@ -671,7 +677,8 @@ exprs([E0|Es], Bs1, RT, Lf, Ef, Bs0, W) -> if Es =:= [] -> VS = pp(V0, 1, RT), - [io:requests([{put_chars, VS}, nl]) || W =:= cmd], + [io:requests([{put_chars, unicode, VS}, nl]) || + W =:= cmd], %% Don't send the result back if it will be %% discarded anyway. V = if @@ -753,7 +760,7 @@ used_records(E) -> {expr, E}. fwrite_severity(Severity, S, As) -> - io:fwrite(<<"~s\n">>, [format_severity(Severity, S, As)]). + io:fwrite(<<"~ts\n">>, [format_severity(Severity, S, As)]). format_severity(Severity, S, As) -> add_severity(Severity, io_lib:fwrite(S, As)). @@ -836,16 +843,6 @@ not_restricted(h, []) -> true; not_restricted(b, []) -> true; -not_restricted(which, [_]) -> - true; -not_restricted(import, [_]) -> - true; -not_restricted(import_all, [_]) -> - true; -not_restricted(use, [_]) -> - true; -not_restricted(use_all, [_]) -> - true; not_restricted(history, [_]) -> true; not_restricted(results, [_]) -> @@ -964,7 +961,7 @@ local_func(rd, [{atom,_,RecName},RecDef0], Bs, _Shell, RT, _Lf, _Ef) -> [RN] = add_records([AttrForm], Bs, RT), {value,RN,Bs}; {error,{_Line,M,ErrDesc}} -> - ErrStr = io_lib:fwrite(<<"~s">>, [M:format_error(ErrDesc)]), + ErrStr = io_lib:fwrite(<<"~ts">>, [M:format_error(ErrDesc)]), exit(lists:flatten(ErrStr)) end; local_func(rd, [_,_], _Bs, _Shell, _RT, _Lf, _Ef) -> @@ -988,11 +985,13 @@ local_func(rl, [A], Bs0, _Shell, RT, Lf, Ef) -> {value,list_records(record_defs(RT, listify(Recs))),Bs}; local_func(rp, [A], Bs0, _Shell, RT, Lf, Ef) -> {[V],Bs} = expr_list([A], Bs0, Lf, Ef), - W = columns(), - io:requests([{put_chars, - io_lib_pretty:print(V, 1, W, -1, ?CHAR_MAX, - record_print_fun(RT))}, - nl]), + Cs = io_lib_pretty:print(V, ([{column, 1}, + {line_length, columns()}, + {depth, -1}, + {max_chars, ?CHAR_MAX}, + {record_print_fun, record_print_fun(RT)}] + ++ enc())), + io:requests([{put_chars, unicode, Cs}, nl]), {value,ok,Bs}; local_func(rr, [A], Bs0, _Shell, RT, Lf, Ef) -> {[File],Bs} = expr_list([A], Bs0, Lf, Ef), @@ -1003,47 +1002,6 @@ local_func(rr, [_,_]=As0, Bs0, _Shell, RT, Lf, Ef) -> local_func(rr, [_,_,_]=As0, Bs0, _Shell, RT, Lf, Ef) -> {[File,Sel,Options],Bs} = expr_list(As0, Bs0, Lf, Ef), {value,read_and_add_records(File, Sel, Options, Bs, RT),Bs}; -local_func(which, [{atom,_,M}], Bs, _Shell, _RT, _Lf, _Ef) -> - case erl_eval:binding({module,M}, Bs) of - {value, M1} -> - {value,M1,Bs}; - unbound -> - {value,M,Bs} - end; -local_func(which, [_Other], _Bs, _Shell, _RT, _Lf, _Ef) -> - erlang:raise(error, function_clause, [{shell,which,1}]); -local_func(import, [M], Bs, _Shell, _RT, _Lf, _Ef) -> - case erl_parse:package_segments(M) of - error -> erlang:raise(error, function_clause, [{shell,import,1}]); - M1 -> - Mod = packages:concat(M1), - case packages:is_valid(Mod) of - true -> - Key = list_to_atom(packages:last(Mod)), - Mod1 = list_to_atom(Mod), - {value,ok,erl_eval:add_binding({module,Key}, Mod1, Bs)}; - false -> - exit({{bad_module_name, Mod}, [{shell,import,1}]}) - end - end; -local_func(import_all, [P], Bs0, _Shell, _RT, _Lf, _Ef) -> - case erl_parse:package_segments(P) of - error -> erlang:raise(error, function_clause, [{shell,import_all,1}]); - P1 -> - Name = packages:concat(P1), - case packages:is_valid(Name) of - true -> - Bs1 = import_all(Name, Bs0), - {value,ok,Bs1}; - false -> - exit({{bad_package_name, Name}, - [{shell,import_all,1}]}) - end - end; -local_func(use, [M], Bs, Shell, RT, Lf, Ef) -> - local_func(import, [M], Bs, Shell, RT, Lf, Ef); -local_func(use_all, [M], Bs, Shell, RT, Lf, Ef) -> - local_func(import_all, [M], Bs, Shell, RT, Lf, Ef); local_func(history, [{integer,_,N}], Bs, _Shell, _RT, _Lf, _Ef) -> {value,history(N),Bs}; local_func(history, [_Other], _Bs, _Shell, _RT, _Lf, _Ef) -> @@ -1166,7 +1124,7 @@ add_records(RAs, Bs0, RT) -> case check_command([], Bs1) of {error,{_Line,M,ErrDesc}} -> %% A source file that has not been compiled. - ErrStr = io_lib:fwrite(<<"~s">>, [M:format_error(ErrDesc)]), + ErrStr = io_lib:fwrite(<<"~ts">>, [M:format_error(ErrDesc)]), exit(lists:flatten(ErrStr)); ok -> true = ets:insert(RT, Recs), @@ -1179,18 +1137,13 @@ listify(E) -> [E]. check_command(Es, Bs) -> - erl_eval:check_command(Es, strip_bindings(Bs)). + erl_eval:check_command(Es, Bs). expr(E, Bs, Lf, Ef) -> - erl_eval:expr(E, strip_bindings(Bs), Lf, Ef). + erl_eval:expr(E, Bs, Lf, Ef). expr_list(Es, Bs, Lf, Ef) -> - erl_eval:expr_list(Es, strip_bindings(Bs), Lf, Ef). - --spec strip_bindings(erl_eval:binding_struct()) -> erl_eval:binding_struct(). - -strip_bindings(Bs) -> - Bs -- [B || {{module,_},_}=B <- Bs]. + erl_eval:expr_list(Es, Bs, Lf, Ef). %% Note that a sequence number is used here to make sure that if a %% record is used by another record, then the first record is parsed @@ -1323,15 +1276,6 @@ record_attrs(Forms) -> %%% End of reading record information from file(s) -import_all(P, Bs0) -> - Ms = packages:find_modules(P), - lists:foldl(fun (M, Bs) -> - Key = list_to_atom(M), - M1 = list_to_atom(packages:concat(P, M)), - erl_eval:add_binding({module,Key}, M1, Bs) - end, - Bs0, Ms). - shell_req(Shell, Req) -> Shell ! {shell_req,self(),Req}, receive @@ -1343,39 +1287,36 @@ list_commands([{{N,command},Es0}, {{N,result}, V} |Ds], RT) -> VS = pp(V, 4, RT), Ns = io_lib:fwrite(<<"~w: ">>, [N]), I = iolist_size(Ns), - io:requests([{put_chars, Ns}, - {format,<<"~s\n">>,[erl_pp:exprs(Es, I, none)]}, + io:requests([{put_chars, latin1, Ns}, + {format,<<"~ts\n">>,[erl_pp:exprs(Es, I, enc())]}, {format,<<"-> ">>,[]}, - {put_chars, VS}, + {put_chars, unicode, VS}, nl]), list_commands(Ds, RT); list_commands([{{N,command},Es0} |Ds], RT) -> Es = prep_list_commands(Es0), Ns = io_lib:fwrite(<<"~w: ">>, [N]), I = iolist_size(Ns), - io:requests([{put_chars, Ns}, - {format,<<"~s\n">>,[erl_pp:exprs(Es, I, none)]}]), + io:requests([{put_chars, latin1, Ns}, + {format,<<"~ts\n">>,[erl_pp:exprs(Es, I, enc())]}]), list_commands(Ds, RT); list_commands([_D|Ds], RT) -> list_commands(Ds, RT); list_commands([], _RT) -> ok. -list_bindings([{{module,M},Val}|Bs], RT) -> - io:fwrite(<<"~p is ~p\n">>, [M,Val]), - list_bindings(Bs, RT); list_bindings([{Name,Val}|Bs], RT) -> case erl_eval:fun_data(Val) of {fun_data,_FBs,FCs0} -> FCs = expand_value(FCs0), % looks nicer F = {'fun',0,{clauses,FCs}}, M = {match,0,{var,0,Name},F}, - io:fwrite(<<"~s\n">>, [erl_pp:expr(M)]); + io:fwrite(<<"~ts\n">>, [erl_pp:expr(M, enc())]); false -> Namel = io_lib:fwrite(<<"~s = ">>, [Name]), Nl = iolist_size(Namel)+1, ValS = pp(Val, Nl, RT), - io:requests([{put_chars, Namel}, - {put_chars, ValS}, + io:requests([{put_chars, latin1, Namel}, + {put_chars, unicode, ValS}, nl]) end, list_bindings(Bs, RT); @@ -1384,7 +1325,7 @@ list_bindings([], _RT) -> list_records(Records) -> lists:foreach(fun({_Name,Attr}) -> - io:fwrite(<<"~s">>, [erl_pp:attribute(Attr)]) + io:fwrite(<<"~ts">>, [erl_pp:attribute(Attr, enc())]) end, Records). record_defs(RT, Names) -> @@ -1427,14 +1368,35 @@ get_history_and_results() -> {History, erlang:min(Results, History)}. pp(V, I, RT) -> - io_lib_pretty:print(V, I, columns(), ?LINEMAX, ?CHAR_MAX, - record_print_fun(RT)). + pp(V, I, RT, enc()). + +pp(V, I, RT, Enc) -> + Strings = + case application:get_env(stdlib, shell_strings) of + {ok, false} -> + false; + _ -> + true + end, + io_lib_pretty:print(V, ([{column, I}, {line_length, columns()}, + {depth, ?LINEMAX}, {max_chars, ?CHAR_MAX}, + {strings, Strings}, + {record_print_fun, record_print_fun(RT)}] + ++ Enc)). columns() -> case io:columns() of {ok,N} -> N; _ -> 80 end. +encoding() -> + [{encoding, Encoding}] = enc(), + Encoding. +enc() -> + case lists:keyfind(encoding, 1, io:getopts()) of + false -> [{encoding,latin1}]; % should never happen + Enc -> [Enc] + end. garb(Shell) -> erlang:garbage_collect(Shell), @@ -1457,9 +1419,9 @@ check_env(V) -> {ok, Val} when is_integer(Val), Val >= 0 -> ok; {ok, Val} -> - Txt = io_lib:fwrite( - <<"Invalid value of STDLIB configuration parameter ~p: ~p\n">>, - [V, Val]), + Txt = io_lib:fwrite + ("Invalid value of STDLIB configuration parameter" + "~w: ~tp\n", [V, Val]), error_logger:info_report(lists:flatten(Txt)) end. @@ -1485,14 +1447,22 @@ history(L) when is_integer(L), L >= 0 -> results(L) when is_integer(L), L >= 0 -> set_env(stdlib, shell_saved_results, L, ?DEF_RESULTS). --spec catch_exception(Bool) -> Bool when +-spec catch_exception(Bool) -> boolean() when Bool :: boolean(). catch_exception(Bool) -> set_env(stdlib, shell_catch_exception, Bool, ?DEF_CATCH_EXCEPTION). --spec prompt_func(PromptFunc) -> PromptFunc when - PromptFunc :: 'default' | {module(),atom()}. +-spec prompt_func(PromptFunc) -> PromptFunc2 when + PromptFunc :: 'default' | {module(),atom()}, + PromptFunc2 :: 'default' | {module(),atom()}. prompt_func(String) -> set_env(stdlib, shell_prompt_func, String, ?DEF_PROMPT_FUNC). + +-spec strings(Strings) -> Strings2 when + Strings :: boolean(), + Strings2 :: boolean(). + +strings(Strings) -> + set_env(stdlib, shell_strings, Strings, ?DEF_STRINGS). diff --git a/lib/stdlib/src/slave.erl b/lib/stdlib/src/slave.erl index de0179da59..9c74041f56 100644 --- a/lib/stdlib/src/slave.erl +++ b/lib/stdlib/src/slave.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2013. 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 @@ -229,7 +229,7 @@ wait_for_slave(Parent, Host, Name, Node, Args, LinkTo, Prog) -> Waiter = register_unique_name(0), case mk_cmd(Host, Name, Args, Waiter, Prog) of {ok, Cmd} -> -%% io:format("Command: ~s~n", [Cmd]), +%% io:format("Command: ~ts~n", [Cmd]), open_port({spawn, Cmd}, [stream]), receive {SlavePid, slave_started} -> diff --git a/lib/stdlib/src/stdlib.appup.src b/lib/stdlib/src/stdlib.appup.src index 94e81188b5..55c8087475 100644 --- a/lib/stdlib/src/stdlib.appup.src +++ b/lib/stdlib/src/stdlib.appup.src @@ -17,11 +17,11 @@ %% %CopyrightEnd% {"%VSN%", %% Up from - max two major revisions back - [{<<"1\\.18(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R15 - {<<"1\\.17(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R14 - {<<"1\\.16(\\.[0-9]+)*">>,[restart_new_emulator]}],%% R13 + [{<<"1\\.19(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R16 + {<<"1\\.18(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R15 + {<<"1\\.17(\\.[0-9]+)*">>,[restart_new_emulator]}],%% R14 %% Down to - max two major revisions back - [{<<"1\\.18(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R15 - {<<"1\\.17(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R14 - {<<"1\\.16(\\.[0-9]+)*">>,[restart_new_emulator]}] %% R13 + [{<<"1\\.19(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R16 + {<<"1\\.18(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R15 + {<<"1\\.17(\\.[0-9]+)*">>,[restart_new_emulator]}] %% R14 }. diff --git a/lib/stdlib/src/string.erl b/lib/stdlib/src/string.erl index 30eac4f07d..4ed27ff4eb 100644 --- a/lib/stdlib/src/string.erl +++ b/lib/stdlib/src/string.erl @@ -1,7 +1,8 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2013. 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 @@ -29,6 +30,30 @@ %%--------------------------------------------------------------------------- +%%% BIFs + +-export([to_float/1, to_integer/1]). + +-spec to_float(String) -> {Float, Rest} | {error, Reason} when + String :: string(), + Float :: float(), + Rest :: string(), + Reason :: no_float | not_a_list. + +to_float(_) -> + erlang:nif_error(undef). + +-spec to_integer(String) -> {Int, Rest} | {error, Reason} when + String :: string(), + Int :: integer(), + Rest :: string(), + Reason :: no_integer | not_a_list. + +to_integer(_) -> + erlang:nif_error(undef). + +%%% End of BIFs + %% Robert's bit %% len(String) @@ -233,7 +258,7 @@ chars(C, N, Tail) when N > 0 -> chars(C, 0, Tail) when is_integer(C) -> Tail. -%% Torbj�rn's bit. +%% Torbjörn's bit. %%% COPIES %%% @@ -459,8 +484,8 @@ to_upper_char(C) -> C. -spec to_lower(String) -> Result when - String :: string(), - Result :: string() + String :: io_lib:latin1_string(), + Result :: io_lib:latin1_string() ; (Char) -> CharResult when Char :: char(), CharResult :: char(). @@ -471,8 +496,8 @@ to_lower(C) when is_integer(C) -> to_lower_char(C). -spec to_upper(String) -> Result when - String :: string(), - Result :: string() + String :: io_lib:latin1_string(), + Result :: io_lib:latin1_string() ; (Char) -> CharResult when Char :: char(), CharResult :: char(). diff --git a/lib/stdlib/src/supervisor.erl b/lib/stdlib/src/supervisor.erl index 7d3c5a0e21..9f93747c3e 100644 --- a/lib/stdlib/src/supervisor.erl +++ b/lib/stdlib/src/supervisor.erl @@ -104,7 +104,9 @@ %%% SupName = {local, atom()} | {global, atom()}. %%% --------------------------------------------------- --type startlink_err() :: {'already_started', pid()} | 'shutdown' | term(). +-type startlink_err() :: {'already_started', pid()} + | {'shutdown', term()} + | term(). -type startlink_ret() :: {'ok', pid()} | 'ignore' | {'error', startlink_err()}. -spec start_link(Module, Args) -> startlink_ret() when @@ -221,8 +223,10 @@ cast(Supervisor, Req) -> -type init_sup_name() :: sup_name() | 'self'. --type stop_rsn() :: 'shutdown' | {'bad_return', {module(),'init', term()}} - | {'bad_start_spec', term()} | {'start_spec', term()} +-type stop_rsn() :: {'shutdown', term()} + | {'bad_return', {module(),'init', term()}} + | {'bad_start_spec', term()} + | {'start_spec', term()} | {'supervisor_data', term()}. -spec init({init_sup_name(), module(), [term()]}) -> @@ -253,9 +257,9 @@ init_children(State, StartSpec) -> case start_children(Children, SupName) of {ok, NChildren} -> {ok, State#state{children = NChildren}}; - {error, NChildren} -> + {error, NChildren, Reason} -> terminate_children(NChildren, SupName), - {stop, shutdown} + {stop, {shutdown, Reason}} end; Error -> {stop, {start_spec, Error}} @@ -275,9 +279,9 @@ init_dynamic(_State, StartSpec) -> %% Func: start_children/2 %% Args: Children = [child_rec()] in start order %% SupName = {local, atom()} | {global, atom()} | {pid(), Mod} -%% Purpose: Start all children. The new list contains #child's +%% Purpose: Start all children. The new list contains #child's %% with pids. -%% Returns: {ok, NChildren} | {error, NChildren} +%% Returns: {ok, NChildren} | {error, NChildren, Reason} %% NChildren = [child_rec()] in termination order (reversed %% start order) %%----------------------------------------------------------------- @@ -293,7 +297,8 @@ start_children([Child|Chs], NChildren, SupName) -> start_children(Chs, [Child#child{pid = Pid}|NChildren], SupName); {error, Reason} -> report_error(start_error, Reason, Child, SupName), - {error, lists:reverse(Chs) ++ [Child | NChildren]} + {error, lists:reverse(Chs) ++ [Child | NChildren], + {failed_to_start_child,Child#child.name,Reason}} end; start_children([], NChildren, _SupName) -> {ok, NChildren}. @@ -793,7 +798,7 @@ restart(rest_for_one, Child, State) -> case start_children(ChAfter2, State#state.name) of {ok, ChAfter3} -> {ok, State#state{children = ChAfter3 ++ ChBefore}}; - {error, ChAfter3} -> + {error, ChAfter3, _Reason} -> NChild = Child#child{pid=restarting(Child#child.pid)}, NState = State#state{children = ChAfter3 ++ ChBefore}, {try_again, replace_child(NChild,NState)} @@ -804,7 +809,7 @@ restart(one_for_all, Child, State) -> case start_children(Children2, State#state.name) of {ok, NChs} -> {ok, State#state{children = NChs}}; - {error, NChs} -> + {error, NChs, _Reason} -> NChild = Child#child{pid=restarting(Child#child.pid)}, NState = State#state{children = NChs}, {try_again, replace_child(NChild,NState)} diff --git a/lib/stdlib/src/sys.erl b/lib/stdlib/src/sys.erl index f34201604c..2d6287814e 100644 --- a/lib/stdlib/src/sys.erl +++ b/lib/stdlib/src/sys.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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,12 +32,25 @@ %% Types %%----------------------------------------------------------------- +-export_type([dbg_opt/0]). + -type name() :: pid() | atom() | {'global', atom()}. -type system_event() :: {'in', Msg :: _} | {'in', Msg :: _, From :: _} | {'out', Msg :: _, To :: _} | term(). --opaque dbg_opt() :: list(). +-opaque dbg_opt() :: {'trace', 'true'} + | {'log', + {N :: non_neg_integer(), + [{Event :: system_event(), + FuncState :: _, + FormFunc :: dbg_fun()}]}} + | {'statistics', {file:date_time(), + {'reductions', non_neg_integer()}, + MessagesIn :: non_neg_integer(), + MessagesOut :: non_neg_integer()}} + | {'log_to_file', file:io_device()} + | {Func :: dbg_fun(), FuncState :: term()}. -type dbg_fun() :: fun((FuncState :: _, Event :: system_event(), ProcState :: _) -> 'done' | (NewFuncState :: _)). @@ -45,24 +58,22 @@ %%----------------------------------------------------------------- %% System messages %%----------------------------------------------------------------- --spec suspend(Name) -> Void when - Name :: name(), - Void :: term(). +-spec suspend(Name) -> 'ok' when + Name :: name(). suspend(Name) -> send_system_msg(Name, suspend). --spec suspend(Name, Timeout) -> Void when + +-spec suspend(Name, Timeout) -> 'ok' when Name :: name(), - Timeout :: timeout(), - Void :: term(). + Timeout :: timeout(). suspend(Name, Timeout) -> send_system_msg(Name, suspend, Timeout). --spec resume(Name) -> Void when - Name :: name(), - Void :: term(). +-spec resume(Name) -> 'ok' when + Name :: name(). resume(Name) -> send_system_msg(Name, resume). --spec resume(Name, Timeout) -> Void when + +-spec resume(Name, Timeout) -> 'ok' when Name :: name(), - Timeout :: timeout(), - Void :: term(). + Timeout :: timeout(). resume(Name, Timeout) -> send_system_msg(Name, resume, Timeout). -spec get_status(Name) -> Status when @@ -71,9 +82,10 @@ resume(Name, Timeout) -> send_system_msg(Name, resume, Timeout). SItem :: (PDict :: [{Key :: term(), Value :: term()}]) | (SysState :: 'running' | 'suspended') | (Parent :: pid()) - | (Dbg :: dbg_opt()) + | (Dbg :: [dbg_opt()]) | (Misc :: term()). get_status(Name) -> send_system_msg(Name, get_status). + -spec get_status(Name, Timeout) -> Status when Name :: name(), Timeout :: timeout(), @@ -81,7 +93,7 @@ get_status(Name) -> send_system_msg(Name, get_status). SItem :: (PDict :: [{Key :: term(), Value :: term()}]) | (SysState :: 'running' | 'suspended') | (Parent :: pid()) - | (Dbg :: dbg_opt()) + | (Dbg :: [dbg_opt()]) | (Misc :: term()). get_status(Name, Timeout) -> send_system_msg(Name, get_status, Timeout). @@ -93,6 +105,7 @@ get_status(Name, Timeout) -> send_system_msg(Name, get_status, Timeout). Reason :: term(). change_code(Name, Mod, Vsn, Extra) -> send_system_msg(Name, {change_code, Mod, Vsn, Extra}). + -spec change_code(Name, Module, OldVsn, Extra, Timeout) -> 'ok' | {error, Reason} when Name :: name(), @@ -189,35 +202,33 @@ no_debug(Name) -> send_system_msg(Name, {debug, no_debug}). Timeout :: timeout(). no_debug(Name, Timeout) -> send_system_msg(Name, {debug, no_debug}, Timeout). --spec install(Name, FuncSpec) -> Void when +-spec install(Name, FuncSpec) -> 'ok' when Name :: name(), FuncSpec :: {Func, FuncState}, Func :: dbg_fun(), - FuncState :: term(), - Void :: term(). + FuncState :: term(). install(Name, {Func, FuncState}) -> send_system_msg(Name, {debug, {install, {Func, FuncState}}}). --spec install(Name, FuncSpec, Timeout) -> Void when + +-spec install(Name, FuncSpec, Timeout) -> 'ok' when Name :: name(), FuncSpec :: {Func, FuncState}, Func :: dbg_fun(), FuncState :: term(), - Timeout :: timeout(), - Void :: term(). + Timeout :: timeout(). install(Name, {Func, FuncState}, Timeout) -> send_system_msg(Name, {debug, {install, {Func, FuncState}}}, Timeout). --spec remove(Name, Func) -> Void when +-spec remove(Name, Func) -> 'ok' when Name :: name(), - Func :: dbg_fun(), - Void :: term(). + Func :: dbg_fun(). remove(Name, Func) -> send_system_msg(Name, {debug, {remove, Func}}). --spec remove(Name, Func, Timeout) -> Void when + +-spec remove(Name, Func, Timeout) -> 'ok' when Name :: name(), Func :: dbg_fun(), - Timeout :: timeout(), - Void :: term(). + Timeout :: timeout(). remove(Name, Func, Timeout) -> send_system_msg(Name, {debug, {remove, Func}}, Timeout). @@ -243,18 +254,13 @@ mfa(Name, {change_code, Mod, Vsn, Extra}) -> {sys, change_code, [Name, Mod, Vsn, Extra]}; mfa(Name, Atom) -> {sys, Atom, [Name]}. + mfa(Name, Req, Timeout) -> {M, F, A} = mfa(Name, Req), {M, F, A ++ [Timeout]}. %%----------------------------------------------------------------- %% Func: handle_system_msg/6 -%% Args: Msg ::= term() -%% From ::= {pid(),Ref} but don't count on that -%% Parent ::= pid() -%% Module ::= atom() -%% Debug ::= [debug_opts()] -%% Misc ::= term() %% Purpose: Used by a process module that wishes to take care of %% system messages. The process receives a {system, From, %% Msg} message, and passes the Msg to this function. @@ -266,14 +272,14 @@ mfa(Name, Req, Timeout) -> %% The Module must export system_continue/3, system_terminate/4 %% and format_status/2 for status information. %%----------------------------------------------------------------- --spec handle_system_msg(Msg, From, Parent, Module, Debug, Misc) -> Void when +-spec handle_system_msg(Msg, From, Parent, Module, Debug, Misc) -> + no_return() when Msg :: term(), From :: {pid(), Tag :: _}, Parent :: pid(), Module :: module(), Debug :: [dbg_opt()], - Misc :: term(), - Void :: term(). + Misc :: term(). handle_system_msg(Msg, From, Parent, Module, Debug, Misc) -> handle_system_msg(running, Msg, From, Parent, Module, Debug, Misc, false). @@ -292,10 +298,6 @@ handle_system_msg(SysState, Msg, From, Parent, Mod, Debug, Misc, Hib) -> %%----------------------------------------------------------------- %% Func: handle_debug/4 -%% Args: Debug ::= [debug_opts()] -%% Func ::= {M,F} | fun() arity 3 -%% State ::= term() -%% Event ::= {in, Msg} | {in, Msg, From} | {out, Msg, To} | term() %% Purpose: Called by a process that wishes to debug an event. %% Func is a formatting function, called as Func(Device, Event). %% Returns: [debug_opts()] @@ -451,6 +453,7 @@ print_event(Dev, {Event, State, FormFunc}) -> FormFunc(Dev, Event, State). init_stat() -> {erlang:localtime(), process_info(self(), reductions), 0, 0}. + get_stat({Time, {reductions, Reds}, In, Out}) -> {reductions, Reds2} = process_info(self(), reductions), [{start_time, Time}, {current_time, erlang:localtime()}, @@ -490,9 +493,8 @@ get_debug2(Item, Debug, Default) -> _ -> Default end. --spec print_log(Debug) -> Void when - Debug :: [dbg_opt()], - Void :: term(). +-spec print_log(Debug) -> 'ok' when + Debug :: [dbg_opt()]. print_log(Debug) -> {_N, Logs} = get_debug(log, Debug, {0, []}), lists:foreach(fun print_event/1, @@ -509,8 +511,6 @@ close_log_file(Debug) -> %%----------------------------------------------------------------- %% Func: debug_options/1 -%% Args: [trace|log|{log,N}|statistics|{log_to_file, FileName}| -%% {install, {Func, FuncState}}] %% Purpose: Initiate a debug structure. Called by a process that %% wishes to initiate the debug structure without the %% system messages. @@ -519,7 +519,11 @@ close_log_file(Debug) -> -spec debug_options(Options) -> [dbg_opt()] when Options :: [Opt], - Opt :: 'trace' | 'log' | 'statistics' | {'log_to_file', FileName} + Opt :: 'trace' + | 'log' + | {'log', pos_integer()} + | 'statistics' + | {'log_to_file', FileName} | {'install', FuncSpec}, FileName :: file:name(), FuncSpec :: {Func, FuncState}, @@ -527,6 +531,7 @@ close_log_file(Debug) -> FuncState :: term(). debug_options(Options) -> debug_options(Options, []). + debug_options([trace | T], Debug) -> debug_options(T, install_debug(trace, true, Debug)); debug_options([log | T], Debug) -> diff --git a/lib/stdlib/src/unicode.erl b/lib/stdlib/src/unicode.erl index e9b90befe6..49529cffd4 100644 --- a/lib/stdlib/src/unicode.erl +++ b/lib/stdlib/src/unicode.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2011. All Rights Reserved. +%% Copyright Ericsson AB 2008-2013. 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 @@ -18,39 +18,78 @@ %% -module(unicode). -%% Implemented in the emulator: -%% characters_to_binary/2 (will trap to characters_to_binary_int/2 -%% if InEncoding is not {latin1 | unicode | utf8}) -%% characters_to_list/2 (will trap to characters_to_list_int/2 if -%% InEncoding is not {latin1 | unicode | utf8}) -%% - -export([characters_to_list/1, characters_to_list_int/2, characters_to_binary/1, characters_to_binary_int/2, characters_to_binary/3, bom_to_encoding/1, encoding_to_bom/1]). -export_type([chardata/0, charlist/0, encoding/0, external_chardata/0, - external_charlist/0, latin1_chardata/0, - latin1_charlist/0, unicode_binary/0, unicode_char/0]). + external_charlist/0, latin1_char/0, latin1_chardata/0, + latin1_charlist/0, latin1_binary/0, unicode_binary/0]). -type encoding() :: 'latin1' | 'unicode' | 'utf8' | 'utf16' | {'utf16', endian()} | 'utf32' | {'utf32', endian()}. -type endian() :: 'big' | 'little'. -type unicode_binary() :: binary(). --type unicode_char() :: non_neg_integer(). --type charlist() :: [unicode_char() | unicode_binary() | charlist()]. +-type charlist() :: + maybe_improper_list(char() | unicode_binary() | charlist(), + unicode_binary() | nil()). -type chardata() :: charlist() | unicode_binary(). -type external_unicode_binary() :: binary(). -type external_chardata() :: external_charlist() | external_unicode_binary(). --type external_charlist() :: [unicode_char() | external_unicode_binary() - | external_charlist()]. +-type external_charlist() :: + maybe_improper_list(char() | + external_unicode_binary() | + external_charlist(), + external_unicode_binary() | nil()). -type latin1_binary() :: binary(). -type latin1_char() :: byte(). -type latin1_chardata() :: latin1_charlist() | latin1_binary(). --type latin1_charlist() :: [latin1_char() | latin1_binary() - | latin1_charlist()]. +-type latin1_charlist() :: + maybe_improper_list(latin1_char() | + latin1_binary() | + latin1_charlist(), + latin1_binary() | nil()). + +%%% BIFs +%%% +%%% characters_to_binary/2 (will trap to characters_to_binary_int/2 +%%% if InEncoding is not {latin1 | unicode | utf8}) +%%% characters_to_list/2 (will trap to characters_to_list_int/2 if +%%% InEncoding is not {latin1 | unicode | utf8}) + +-export([bin_is_7bit/1, characters_to_binary/2, characters_to_list/2]). + +-spec bin_is_7bit(Binary) -> boolean() when + Binary :: binary(). + +bin_is_7bit(_) -> + erlang:nif_error(undef). + +-spec characters_to_binary(Data, InEncoding) -> Result when + Data :: latin1_chardata() | chardata() | external_chardata(), + InEncoding :: encoding(), + Result :: binary() + | {error, binary(), RestData} + | {incomplete, binary(), binary()}, + RestData :: latin1_chardata() | chardata() | external_chardata(). + +characters_to_binary(_, _) -> + erlang:nif_error(undef). + +-spec characters_to_list(Data, InEncoding) -> Result when + Data :: latin1_chardata() | chardata() | external_chardata(), + InEncoding :: encoding(), + Result :: list() + | {error, list(), RestData} + | {incomplete, list(), binary()}, + RestData :: latin1_chardata() | chardata() | external_chardata(). + +characters_to_list(_, _) -> + erlang:nif_error(undef). + +%%% End of BIFs -spec characters_to_list(Data) -> Result when Data :: latin1_chardata() | chardata() | external_chardata(), diff --git a/lib/stdlib/src/win32reg.erl b/lib/stdlib/src/win32reg.erl index 598e77ffdc..48a7e262be 100644 --- a/lib/stdlib/src/win32reg.erl +++ b/lib/stdlib/src/win32reg.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2011. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. 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 @@ -25,6 +25,8 @@ expand/1, format_error/1]). +-export_type([reg_handle/0]). + %% Key handles (always open). -define(hkey_classes_root, 16#80000000). -define(hkey_current_user, 16#80000001). diff --git a/lib/stdlib/src/zip.erl b/lib/stdlib/src/zip.erl index c383540db7..c40ce8e203 100644 --- a/lib/stdlib/src/zip.erl +++ b/lib/stdlib/src/zip.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2012. All Rights Reserved. +%% Copyright Ericsson AB 2006-2013. 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 @@ -610,9 +610,9 @@ get_zip_opt([Unknown | _Rest], _Opts) -> %% feedback funs silent(_) -> ok. -verbose_unzip(FN) -> io:format("extracting: ~p\n", [FN]). +verbose_unzip(FN) -> io:format("extracting: ~tp\n", [FN]). -verbose_zip(FN) -> io:format("adding: ~p\n", [FN]). +verbose_zip(FN) -> io:format("adding: ~tp\n", [FN]). %% file filter funs all(_) -> true. @@ -943,7 +943,7 @@ raw_short_print_info_etc(EOCD, X, Comment, Y, Acc) when is_record(EOCD, eocd) -> raw_long_print_info_etc(EOCD, X, Comment, Y, Acc). print_file_name(FileName) -> - io:format("~s\n", [FileName]). + io:format("~ts\n", [FileName]). %% for printing directory (tt/1) @@ -960,14 +960,14 @@ raw_long_print_info_etc(EOCD, _, Comment, _, Acc) when is_record(EOCD, eocd) -> Acc. print_header(CompSize, MTime, UncompSize, FileName, FileComment) -> - io:format("~8w ~s ~8w ~2w% ~s ~s\n", + io:format("~8w ~s ~8w ~2w% ~ts ~ts\n", [CompSize, time_to_string(MTime), UncompSize, get_percent(CompSize, UncompSize), FileName, FileComment]). print_comment("") -> ok; print_comment(Comment) -> - io:format("Archive comment: ~s\n", [Comment]). + io:format("Archive comment: ~ts\n", [Comment]). get_percent(_, 0) -> 100; get_percent(CompSize, Size) -> round(CompSize * 100 / Size). diff --git a/lib/stdlib/test/Makefile b/lib/stdlib/test/Makefile index 29b8e28d3a..6aa09d7bd0 100644 --- a/lib/stdlib/test/Makefile +++ b/lib/stdlib/test/Makefile @@ -23,7 +23,6 @@ MODULES= \ dummy_via \ edlin_expand_SUITE \ epp_SUITE \ - erl_eval_helper \ erl_eval_SUITE \ erl_expand_records_SUITE \ erl_internal_SUITE \ diff --git a/lib/stdlib/test/base64_SUITE.erl b/lib/stdlib/test/base64_SUITE.erl index c64a961ffa..b28df94221 100644 --- a/lib/stdlib/test/base64_SUITE.erl +++ b/lib/stdlib/test/base64_SUITE.erl @@ -1,7 +1,8 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2011. All Rights Reserved. +%% Copyright Ericsson AB 2007-2012. 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 @@ -20,7 +21,6 @@ -module(base64_SUITE). -include_lib("common_test/include/ct.hrl"). --include("test_server_line.hrl"). %% Test server specific exports -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, @@ -33,7 +33,7 @@ mime_decode_to_string/1, roundtrip/1]). init_per_testcase(_, Config) -> - Dog = test_server:timetrap(?t:minutes(2)), + Dog = test_server:timetrap(?t:minutes(4)), NewConfig = lists:keydelete(watchdog, 1, Config), [{watchdog, Dog} | NewConfig]. @@ -180,7 +180,7 @@ mime_decode(Config) when is_list(Config) -> <<"o">> = base64:mime_decode(<<"b=w=====">>), %% Test misc white space and illegals with embedded padding <<"one">> = base64:mime_decode(<<" b~2=\r\n5()l===">>), - <<"on">> = base64:mime_decode(<<"\tb =2\"�4=�= ==">>), + <<"on">> = base64:mime_decode(<<"\tb =2\"¤4=¤= ==">>), <<"o">> = base64:mime_decode(<<"\nb=w=====">>), %% Two pads <<"Aladdin:open sesame">> = @@ -189,7 +189,7 @@ mime_decode(Config) when is_list(Config) -> <<"Hello World!!">> = base64:mime_decode(<<"SGVsb)(G8gV29ybGQ=h IQ= =">>), %% No pad <<"Aladdin:open sesam">> = - base64:mime_decode("QWxhZGRpbjpvcG�\")(VuIHNlc2Ft"), + base64:mime_decode("QWxhZGRpbjpvcG¤\")(VuIHNlc2Ft"), %% Encoded base 64 strings may be divided by non base 64 chars. %% In this cases whitespaces. <<"0123456789!@#0^&*();:<>,. []{}">> = @@ -223,7 +223,7 @@ mime_decode_to_string(Config) when is_list(Config) -> "o" = base64:mime_decode_to_string(<<"b=w=====">>), %% Test misc white space and illegals with embedded padding "one" = base64:mime_decode_to_string(<<" b~2=\r\n5()l===">>), - "on" = base64:mime_decode_to_string(<<"\tb =2\"�4=�= ==">>), + "on" = base64:mime_decode_to_string(<<"\tb =2\"¤4=¤= ==">>), "o" = base64:mime_decode_to_string(<<"\nb=w=====">>), %% Two pads "Aladdin:open sesame" = @@ -232,7 +232,7 @@ mime_decode_to_string(Config) when is_list(Config) -> "Hello World!!" = base64:mime_decode_to_string(<<"SGVsb)(G8gV29ybGQ=h IQ= =">>), %% No pad "Aladdin:open sesam" = - base64:mime_decode_to_string("QWxhZGRpbjpvcG�\")(VuIHNlc2Ft"), + base64:mime_decode_to_string("QWxhZGRpbjpvcG¤\")(VuIHNlc2Ft"), %% Encoded base 64 strings may be divided by non base 64 chars. %% In this cases whitespaces. "0123456789!@#0^&*();:<>,. []{}" = diff --git a/lib/stdlib/test/binary_module_SUITE.erl b/lib/stdlib/test/binary_module_SUITE.erl index bac59a3107..9b6f628aa9 100644 --- a/lib/stdlib/test/binary_module_SUITE.erl +++ b/lib/stdlib/test/binary_module_SUITE.erl @@ -1,7 +1,8 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2011. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. 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 @@ -1331,9 +1332,9 @@ one_random_number(N) -> one_random(N) -> M = ((N - 1) rem 68) + 1, element(M,{$a,$b,$c,$d,$e,$f,$g,$h,$i,$j,$k,$l,$m,$n,$o,$p,$q,$r,$s,$t, - $u,$v,$w,$x,$y,$z,$�,$�,$�,$A,$B,$C,$D,$E,$F,$G,$H, - $I,$J,$K,$L,$M,$N,$O,$P,$Q,$R,$S,$T,$U,$V,$W,$X,$Y,$Z,$�, - $�,$�,$0,$1,$2,$3,$4,$5,$6,$7,$8,$9}). + $u,$v,$w,$x,$y,$z,$å,$ä,$ö,$A,$B,$C,$D,$E,$F,$G,$H, + $I,$J,$K,$L,$M,$N,$O,$P,$Q,$R,$S,$T,$U,$V,$W,$X,$Y,$Z,$Å, + $Ä,$Ö,$0,$1,$2,$3,$4,$5,$6,$7,$8,$9}). random_number({Min,Max}) -> % Min and Max are *length* of number in % decimal positions diff --git a/lib/stdlib/test/dets_SUITE.erl b/lib/stdlib/test/dets_SUITE.erl index 6f77cff2b9..66799f4d05 100644 --- a/lib/stdlib/test/dets_SUITE.erl +++ b/lib/stdlib/test/dets_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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,7 +38,7 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, - not_run/1, newly_started/1, basic_v8/1, basic_v9/1, + newly_started/1, basic_v8/1, basic_v9/1, open_v8/1, open_v9/1, sets_v8/1, sets_v9/1, bags_v8/1, bags_v9/1, duplicate_bags_v8/1, duplicate_bags_v9/1, access_v8/1, access_v9/1, dirty_mark/1, dirty_mark2/1, @@ -95,27 +95,25 @@ end_per_testcase(_Case, _Config) -> suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - case os:type() of - vxworks -> [not_run]; - _ -> - [basic_v8, basic_v9, open_v8, open_v9, sets_v8, sets_v9, - bags_v8, bags_v9, duplicate_bags_v8, duplicate_bags_v9, - newly_started, open_file_v8, open_file_v9, - init_table_v8, init_table_v9, repair_v8, repair_v9, - access_v8, access_v9, oldbugs_v8, oldbugs_v9, - unsafe_assumptions, truncated_segment_array_v8, - truncated_segment_array_v9, dirty_mark, dirty_mark2, - bag_next_v8, bag_next_v9, hash_v8b_v8c, phash, fold_v8, - fold_v9, fixtable_v8, fixtable_v9, match_v8, match_v9, - select_v8, select_v9, update_counter, badarg, - cache_sets_v8, cache_sets_v9, cache_bags_v8, - cache_bags_v9, cache_duplicate_bags_v8, - cache_duplicate_bags_v9, otp_4208, otp_4989, - many_clients, otp_4906, otp_5402, simultaneous_open, - insert_new, repair_continuation, otp_5487, otp_6206, - otp_6359, otp_4738, otp_7146, otp_8070, otp_8856, otp_8898, - otp_8899, otp_8903, otp_8923, otp_9282, otp_9607] - end. + [ + basic_v8, basic_v9, open_v8, open_v9, sets_v8, sets_v9, + bags_v8, bags_v9, duplicate_bags_v8, duplicate_bags_v9, + newly_started, open_file_v8, open_file_v9, + init_table_v8, init_table_v9, repair_v8, repair_v9, + access_v8, access_v9, oldbugs_v8, oldbugs_v9, + unsafe_assumptions, truncated_segment_array_v8, + truncated_segment_array_v9, dirty_mark, dirty_mark2, + bag_next_v8, bag_next_v9, hash_v8b_v8c, phash, fold_v8, + fold_v9, fixtable_v8, fixtable_v9, match_v8, match_v9, + select_v8, select_v9, update_counter, badarg, + cache_sets_v8, cache_sets_v9, cache_bags_v8, + cache_bags_v9, cache_duplicate_bags_v8, + cache_duplicate_bags_v9, otp_4208, otp_4989, + many_clients, otp_4906, otp_5402, simultaneous_open, + insert_new, repair_continuation, otp_5487, otp_6206, + otp_6359, otp_4738, otp_7146, otp_8070, otp_8856, otp_8898, + otp_8899, otp_8903, otp_8923, otp_9282, otp_9607 + ]. groups() -> []. @@ -132,10 +130,6 @@ init_per_group(_GroupName, Config) -> end_per_group(_GroupName, Config) -> Config. -not_run(suite) -> []; -not_run(Conf) when is_list(Conf) -> - {comment, "Not runnable VxWorks/NFS"}. - newly_started(doc) -> ["OTP-3621"]; newly_started(suite) -> @@ -1949,7 +1943,7 @@ match(Config, Version) -> %% match, badarg MSpec = [{'_',[],['$_']}], ?line check_badarg(catch dets:match(no_table, '_'), - dets, safe_fixtable, [no_table,true]), + dets, match, [no_table,'_']), ?line check_badarg(catch dets:match(T, '_', not_a_number), dets, match, [T,'_',not_a_number]), ?line {EC1, _} = dets:select(T, MSpec, 1), @@ -1958,7 +1952,7 @@ match(Config, Version) -> %% match_object, badarg ?line check_badarg(catch dets:match_object(no_table, '_'), - dets, safe_fixtable, [no_table,true]), + dets, match_object, [no_table,'_']), ?line check_badarg(catch dets:match_object(T, '_', not_a_number), dets, match_object, [T,'_',not_a_number]), ?line {EC2, _} = dets:select(T, MSpec, 1), @@ -2127,7 +2121,7 @@ select(Config, Version) -> %% badarg MSpec = [{'_',[],['$_']}], ?line check_badarg(catch dets:select(no_table, MSpec), - dets, safe_fixtable, [no_table,true]), + dets, select, [no_table,MSpec]), ?line check_badarg(catch dets:select(T, <<17>>), dets, select, [T,<<17>>]), ?line check_badarg(catch dets:select(T, []), @@ -2330,7 +2324,7 @@ badarg(Config) when is_list(Config) -> %% match_delete ?line check_badarg(catch dets:match_delete(no_table, '_'), - dets, safe_fixtable, [no_table,true]), + dets, match_delete, [no_table,'_']), %% delete_all_objects ?line check_badarg(catch dets:delete_all_objects(no_table), @@ -2339,17 +2333,19 @@ badarg(Config) when is_list(Config) -> %% select_delete MSpec = [{'_',[],['$_']}], ?line check_badarg(catch dets:select_delete(no_table, MSpec), - dets, safe_fixtable, [no_table,true]), + dets, select_delete, [no_table,MSpec]), ?line check_badarg(catch dets:select_delete(T, <<17>>), dets, select_delete, [T, <<17>>]), %% traverse, fold - ?line check_badarg(catch dets:traverse(no_table, fun(_) -> continue end), - dets, safe_fixtable, [no_table,true]), - ?line check_badarg(catch dets:foldl(fun(_, A) -> A end, [], no_table), - dets, safe_fixtable, [no_table,true]), - ?line check_badarg(catch dets:foldr(fun(_, A) -> A end, [], no_table), - dets, safe_fixtable, [no_table,true]), + TF = fun(_) -> continue end, + ?line check_badarg(catch dets:traverse(no_table, TF), + dets, traverse, [no_table,TF]), + FF = fun(_, A) -> A end, + ?line check_badarg(catch dets:foldl(FF, [], no_table), + dets, foldl, [FF,[],no_table]), + ?line check_badarg(catch dets:foldr(FF, [], no_table), + dets, foldl, [FF,[],no_table]), %% close ?line ok = dets:close(T), diff --git a/lib/stdlib/test/dict_SUITE.erl b/lib/stdlib/test/dict_SUITE.erl index c46fc47b34..0223240479 100644 --- a/lib/stdlib/test/dict_SUITE.erl +++ b/lib/stdlib/test/dict_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2011. All Rights Reserved. +%% Copyright Ericsson AB 2008-2013. 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 @@ -53,7 +53,7 @@ end_per_group(_GroupName, Config) -> init_per_testcase(_Case, Config) -> - ?line Dog = ?t:timetrap(?t:minutes(5)), + Dog = ?t:timetrap(?t:minutes(5)), [{watchdog,Dog}|Config]. end_per_testcase(_Case, Config) -> @@ -65,22 +65,22 @@ create(Config) when is_list(Config) -> test_all(fun create_1/1). create_1(M) -> - ?line D0 = M:empty(), - ?line [] = M:to_list(D0), - ?line 0 = M:size(D0), + D0 = M(empty, []), + [] = M(to_list, D0), + 0 = M(size, D0), D0. store(Config) when is_list(Config) -> test_all([{0,132},{253,258},{510,514}], fun store_1/2). store_1(List, M) -> - ?line D0 = M:from_list(List), + D0 = M(from_list, List), %% Make sure that we get the same result by inserting %% elements one at the time. - ?line D1 = foldl(fun({K,V}, Dict) -> M:enter(K, V, Dict) end, - M:empty(), List), - ?line true = M:equal(D0, D1), + D1 = foldl(fun({K,V}, Dict) -> M(enter, {K,V,Dict}) end, + M(empty, []), List), + true = M(equal, {D0,D1}), D0. %%% @@ -98,7 +98,7 @@ dict_mods() -> [Orddict,Dict,Gb]. test_all(Tester) -> - ?line Pids = [spawn_tester(M, Tester) || M <- dict_mods()], + Pids = [spawn_tester(M, Tester) || M <- dict_mods()], collect_all(Pids, []). spawn_tester(M, Tester) -> @@ -106,7 +106,7 @@ spawn_tester(M, Tester) -> spawn_link(fun() -> random:seed(1, 2, 42), S = Tester(M), - Res = {M:size(S),lists:sort(M:to_list(S))}, + Res = {M(size, S),lists:sort(M(to_list, S))}, Parent ! {result,self(),Res} end). diff --git a/lib/stdlib/test/dict_test_lib.erl b/lib/stdlib/test/dict_test_lib.erl index 92a75dad89..e308fd0721 100644 --- a/lib/stdlib/test/dict_test_lib.erl +++ b/lib/stdlib/test/dict_test_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2010. All Rights Reserved. +%% Copyright Ericsson AB 2008-2013. 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 @@ -17,67 +17,48 @@ %% %CopyrightEnd% %% --module(dict_test_lib, [Mod,Equal]). +-module(dict_test_lib). --export([module/0,equal/2,empty/0,size/1,to_list/1,from_list/1, - enter/3,delete/2,lookup/2]). +-export([new/2]). -module() -> - Mod. - -equal(X, Y) -> - Equal(X, Y). +new(Mod, Eq) -> + fun (enter, {K,V,D}) -> enter(Mod, K, V, D); + (empty, []) -> empty(Mod); + (equal, {D1,D2}) -> Eq(D1, D2); + (from_list, L) -> from_list(Mod, L); + (module, []) -> Mod; + (size, D) -> Mod:size(D); + (to_list, D) -> to_list(Mod, D) + end. -empty() -> +empty(Mod) -> case erlang:function_exported(Mod, new, 0) of false -> Mod:empty(); true -> Mod:new() end. -size(S) -> - Mod:size(S). - -to_list(S) -> - Mod:to_list(S). +to_list(Mod, D) -> + Mod:to_list(D). -from_list(S) -> +from_list(Mod, L) -> case erlang:function_exported(Mod, from_orddict, 1) of false -> - Mod:from_list(S); + Mod:from_list(L); true -> %% The gb_trees module has no from_list/1 function. %% %% The keys in S are not unique. To make sure %% that we pick the same key/value pairs as %% dict/orddict, first convert the list to an orddict. - Orddict = orddict:from_list(S), + Orddict = orddict:from_list(L), Mod:from_orddict(Orddict) end. %% Store new value into dictionary or update previous value in dictionary. -enter(Key, Val, Dict) -> +enter(Mod, Key, Val, Dict) -> case erlang:function_exported(Mod, store, 3) of false -> Mod:enter(Key, Val, Dict); true -> Mod:store(Key, Val, Dict) end. - -%% Delete an EXISTING key. -delete(Key, Dict) -> - case erlang:function_exported(Mod, delete, 2) of - true -> Mod:delete(Key, Dict); - false -> Mod:erase(Key, Dict) - end. - -%% -> none | {value,Value} -lookup(Key, Dict) -> - case erlang:function_exported(Mod, lookup, 2) of - false -> - case Mod:find(Key, Dict) of - error -> none; - {ok,Value} -> {value,Value} - end; - true -> - Mod:lookup(Key, Dict) - end. diff --git a/lib/stdlib/test/digraph_SUITE.erl b/lib/stdlib/test/digraph_SUITE.erl index 1d1326d60e..ed01b32a59 100644 --- a/lib/stdlib/test/digraph_SUITE.erl +++ b/lib/stdlib/test/digraph_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 @@ -400,7 +400,7 @@ sane1(G) -> lists:foreach( fun(V) -> InEs = digraph:in_edges(G, V), - %% Nu har man *alla* inkanter f�r V + %% *All* in-edoges of V lists:foreach( fun(E) -> case digraph:edge(G, E) of diff --git a/lib/stdlib/test/epp_SUITE.erl b/lib/stdlib/test/epp_SUITE.erl index f79414db49..b2f1aa955a 100644 --- a/lib/stdlib/test/epp_SUITE.erl +++ b/lib/stdlib/test/epp_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2011. All Rights Reserved. +%% Copyright Ericsson AB 1998-2013. 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 @@ -25,7 +25,7 @@ variable_1/1, otp_4870/1, otp_4871/1, otp_5362/1, pmod/1, not_circular/1, skip_header/1, otp_6277/1, otp_7702/1, otp_8130/1, overload_mac/1, otp_8388/1, otp_8470/1, otp_8503/1, - otp_8562/1, otp_8665/1, otp_8911/1]). + otp_8562/1, otp_8665/1, otp_8911/1, otp_10302/1, otp_10820/1]). -export([epp_parse_erl_form/2]). @@ -67,7 +67,7 @@ all() -> {group, variable}, otp_4870, otp_4871, otp_5362, pmod, not_circular, skip_header, otp_6277, otp_7702, otp_8130, overload_mac, otp_8388, otp_8470, otp_8503, otp_8562, - otp_8665, otp_8911]. + otp_8665, otp_8911, otp_10302, otp_10820]. groups() -> [{upcase_mac, [], [upcase_mac_1, upcase_mac_2]}, @@ -582,12 +582,13 @@ otp_8130(suite) -> otp_8130(Config) when is_list(Config) -> true = os:putenv("epp_inc1", "stdlib"), Ts = [{otp_8130_1, - %% The scanner handles UNICODE in a special way. Hopefully - %% temporarily. <<"-define(M(A), ??A). " "t() -> " - " \"{ 34 , [ $1 , 2730 ] , \\\"34\\\" , X . a , 2730 }\" = " - " ?M({34,\"1\\x{aaa}\",\"34\",X.a,$\\x{aaa}}), ok. ">>, + " L = \"{ 34 , \\\"1\\\\x{AAA}\\\" , \\\"34\\\" , X . a , $\\\\x{AAA} }\", " + " R = ?M({34,\"1\\x{aaa}\",\"34\",X.a,$\\x{aaa}})," + " Lt = erl_scan:string(L, 1, [unicode])," + " Rt = erl_scan:string(R, 1, [unicode])," + " Lt = Rt, ok. ">>, ok}, {otp_8130_2, @@ -1236,6 +1237,13 @@ otp_8911(doc) -> otp_8911(suite) -> []; otp_8911(Config) when is_list(Config) -> + case test_server:is_cover() of + true -> + {skip, "Testing cover, so can not run when cover is already running"}; + false -> + do_otp_8911(Config) + end. +do_otp_8911(Config) -> ?line {ok, CWD} = file:get_cwd(), ?line ok = file:set_cwd(?config(priv_dir, Config)), @@ -1277,6 +1285,104 @@ otp_8665(Config) when is_list(Config) -> ?line [] = compile(Config, Cs), ok. +otp_10302(doc) -> + "OTP-10302. Unicode characters scanner/parser."; +otp_10302(suite) -> + []; +otp_10302(Config) when is_list(Config) -> + %% Two messages (one too many). Keeps otp_4871 happy. + Cs = [{otp_8562, + <<"%% coding: utf-8\n \n \x{E4}">>, + {errors,[{3,epp,cannot_parse}, + {3,file_io_server,invalid_unicode}],[]}} + ], + [] = compile(Config, Cs), + Dir = ?config(priv_dir, Config), + File = filename:join(Dir, "otp_10302.erl"), + utf8 = encoding("coding: utf-8", File), + utf8 = encoding("coding: UTF-8", File), + latin1 = encoding("coding: Latin-1", File), + latin1 = encoding("coding: latin-1", File), + none = encoding_com("coding: utf-8", File), + none = encoding_com("\n\n%% coding: utf-8", File), + none = encoding_nocom("\n\n coding: utf-8", File), + utf8 = encoding_com("\n%% coding: utf-8", File), + utf8 = encoding_nocom("\n coding: utf-8", File), + none = encoding("coding: \nutf-8", File), + latin1 = encoding("Encoding : latin-1", File), + utf8 = encoding("ccoding: UTF-8", File), + utf8 = encoding("coding= utf-8", File), + utf8 = encoding_com(" %% coding= utf-8", File), + utf8 = encoding("coding = utf-8", File), + none = encoding("coding: utf-16 coding: utf-8", File), %first is bad + none = encoding("Coding: utf-8", File), %capital c + utf8 = encoding("-*- coding: utf-8 -*-", File), + utf8 = encoding("-*-coding= utf-8-*-", File), + utf8 = encoding("codingcoding= utf-8", File), + ok = prefix("coding: utf-8", File, utf8), + + "coding: latin-1" = epp:encoding_to_string(latin1), + "coding: utf-8" = epp:encoding_to_string(utf8), + true = lists:member(epp:default_encoding(), [latin1, utf8]), + + ok. + +prefix(S, File, Enc) -> + prefix(0, S, File, Enc). + +prefix(100, _S, _File, _) -> + ok; +prefix(N, S, File, Enc) -> + Enc = encoding(lists:duplicate(N, $\s) ++ S, File), + prefix(N+1, S, File, Enc). + +encoding(Enc, File) -> + E = encoding_com("%% " ++ Enc, File), + none = encoding_com(Enc, File), + E = encoding_nocom(Enc, File). + +encoding_com(Enc, File) -> + B = list_to_binary(Enc), + E = epp:read_encoding_from_binary(B), + ok = file:write_file(File, Enc), + {ok, Fd} = file:open(File, [read]), + E = epp:set_encoding(Fd), + ok = file:close(Fd), + E = epp:read_encoding(File). + +encoding_nocom(Enc, File) -> + Options = [{in_comment_only, false}], + B = list_to_binary(Enc), + E = epp:read_encoding_from_binary(B, Options), + ok = file:write_file(File, Enc), + {ok, Fd} = file:open(File, [read]), + ok = file:close(Fd), + E = epp:read_encoding(File, Options). + +otp_10820(doc) -> + "OTP-10820. Unicode filenames."; +otp_10820(suite) -> + []; +otp_10820(Config) when is_list(Config) -> + L = [915,953,959,973,957,953,954,959,957,964], + Dir = ?config(priv_dir, Config), + File = filename:join(Dir, L++".erl"), + C1 = <<"%% coding: utf-8\n -module(any).">>, + ok = do_otp_10820(File, C1, "+pc latin1"), + ok = do_otp_10820(File, C1, "+pc unicode"), + C2 = <<"\n-module(any).">>, + ok = do_otp_10820(File, C2, "+pc latin1"), + ok = do_otp_10820(File, C2, "+pc unicode"). + +do_otp_10820(File, C, PC) -> + {ok,Node} = start_node(erl_pp_helper, "+fnu " ++ PC), + ok = rpc:call(Node, file, write_file, [File, C]), + {ok,[{attribute,1,file,{File,1}}, + {attribute,2,module,any}, + {eof,2}]} = rpc:call(Node, epp, parse_file, [File, [],[]]), + true = test_server:stop_node(Node), + ok. + check(Config, Tests) -> eval_tests(Config, fun check_test/2, Tests). @@ -1393,3 +1499,8 @@ ln2({error,M}) -> {error,ln2(M)}; ln2(M) -> M. + +%% +fnu means a peer node has to be started; slave will not do +start_node(Name, Xargs) -> + ?line PA = filename:dirname(code:which(?MODULE)), + test_server:start_node(Name, peer, [{args, "-pa " ++ PA ++ " " ++ Xargs}]). diff --git a/lib/stdlib/test/erl_eval_SUITE.erl b/lib/stdlib/test/erl_eval_SUITE.erl index b0c7d562d5..7ff4c81ea6 100644 --- a/lib/stdlib/test/erl_eval_SUITE.erl +++ b/lib/stdlib/test/erl_eval_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2012. All Rights Reserved. +%% Copyright Ericsson AB 1998-2013. 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 @@ -37,6 +37,7 @@ otp_6977/1, otp_7550/1, otp_8133/1, + otp_10622/1, funs/1, try_catch/1, eval_expr_5/1, @@ -79,7 +80,7 @@ all() -> pattern_expr, match_bin, guard_3, guard_4, lc, simple_cases, unary_plus, apply_atom, otp_5269, otp_6539, otp_6543, otp_6787, otp_6977, otp_7550, - otp_8133, funs, try_catch, eval_expr_5, zero_width]. + otp_8133, otp_10622, funs, try_catch, eval_expr_5, zero_width]. groups() -> []. @@ -216,13 +217,13 @@ guard_4(doc) -> guard_4(suite) -> []; guard_4(Config) when is_list(Config) -> - ?line check(fun() -> if {erlang,'+'}(3,a) -> true ; true -> false end end, - "if {erlang,'+'}(3,a) -> true ; true -> false end.", - false), - ?line check(fun() -> if {erlang,is_integer}(3) -> true ; true -> false end - end, - "if {erlang,is_integer}(3) -> true ; true -> false end.", - true), + check(fun() -> if erlang:'+'(3,a) -> true ; true -> false end end, + "if erlang:'+'(3,a) -> true ; true -> false end.", + false), + check(fun() -> if erlang:is_integer(3) -> true ; true -> false end + end, + "if erlang:is_integer(3) -> true ; true -> false end.", + true), ?line check(fun() -> [X || X <- [1,2,3], erlang:is_integer(X)] end, "[X || X <- [1,2,3], erlang:is_integer(X)].", [1,2,3]), @@ -230,11 +231,11 @@ guard_4(Config) when is_list(Config) -> end, "if is_atom(is_integer(a)) -> true ; true -> false end.", true), - ?line check(fun() -> if {erlang,is_atom}({erlang,is_integer}(a)) -> true; - true -> false end end, - "if {erlang,is_atom}({erlang,is_integer}(a)) -> true; " - "true -> false end.", - true), + check(fun() -> if erlang:is_atom(erlang:is_integer(a)) -> true; + true -> false end end, + "if erlang:is_atom(erlang:is_integer(a)) -> true; " + "true -> false end.", + true), ?line check(fun() -> if is_atom(3+a) -> true ; true -> false end end, "if is_atom(3+a) -> true ; true -> false end.", false), @@ -960,6 +961,7 @@ otp_8133(Config) when is_list(Config) -> E = fun(N) -> if is_integer(N) -> <<N/integer>>; + true -> erlang:error(foo) end end, @@ -980,6 +982,48 @@ otp_8133(Config) when is_list(Config) -> ok), ok. +otp_10622(doc) -> + ["OTP-10622. Bugs."]; +otp_10622(suite) -> + []; +otp_10622(Config) when is_list(Config) -> + check(fun() -> <<0>> = <<"\x{400}">> end, + "<<0>> = <<\"\\x{400}\">>. ", + <<0>>), + check(fun() -> <<"\x{aa}ff"/utf8>> = <<"\x{aa}ff"/utf8>> end, + "<<\"\\x{aa}ff\"/utf8>> = <<\"\\x{aa}ff\"/utf8>>. ", + <<"�\xaaff">>), + %% The same bug as last example: + check(fun() -> case <<"foo"/utf8>> of + <<"foo"/utf8>> -> true + end + end, + "case <<\"foo\"/utf8>> of <<\"foo\"/utf8>> -> true end.", + true), + check(fun() -> <<"\x{400}"/utf8>> = <<"\x{400}"/utf8>> end, + "<<\"\\x{400}\"/utf8>> = <<\"\\x{400}\"/utf8>>. ", + <<208,128>>), + error_check("<<\"\\x{aaa}\">> = <<\"\\x{aaa}\">>.", + {badmatch,<<"\xaa">>}), + + check(fun() -> [a || <<"\x{aaa}">> <= <<2703:16>>] end, + "[a || <<\"\\x{aaa}\">> <= <<2703:16>>]. ", + []), + check(fun() -> [a || <<"\x{aa}"/utf8>> <= <<"\x{aa}"/utf8>>] end, + "[a || <<\"\\x{aa}\"/utf8>> <= <<\"\\x{aa}\"/utf8>>]. ", + [a]), + check(fun() -> [a || <<"\x{aa}x"/utf8>> <= <<"\x{aa}y"/utf8>>] end, + "[a || <<\"\\x{aa}x\"/utf8>> <= <<\"\\x{aa}y\"/utf8>>]. ", + []), + check(fun() -> [a || <<"\x{aaa}">> <= <<"\x{aaa}">>] end, + "[a || <<\"\\x{aaa}\">> <= <<\"\\x{aaa}\">>]. ", + []), + check(fun() -> [a || <<"\x{aaa}"/utf8>> <= <<"\x{aaa}"/utf8>>] end, + "[a || <<\"\\x{aaa}\"/utf8>> <= <<\"\\x{aaa}\"/utf8>>]. ", + [a]), + + ok. + funs(doc) -> ["Simple cases, just to cover some code."]; funs(suite) -> @@ -1042,6 +1086,10 @@ funs(Config) when is_list(Config) -> "begin M = lists, F = fun M:reverse/1," " [1,2] = F([2,1]), ok end.", ok), + + %% Test that {M,F} is not accepted as a fun. + error_check("{" ?MODULE_STRING ",module_info}().", + {badfun,{?MODULE,module_info}}), ok. run_many_args({S, As}) -> @@ -1077,11 +1125,6 @@ do_funs(LFH, EFH) -> concat(["begin F1 = fun(F,N) -> apply(", M, ",count_down,[F, N]) end, F1(F1,1000) end."]), 0, ['F1'], LFH, EFH), - ?line check(fun() -> F1 = fun(F,N) -> {?MODULE,count_down}(F,N) - end, F1(F1, 1000) end, - concat(["begin F1 = fun(F,N) -> {", M, - ",count_down}(F, N) end, F1(F1,1000) end."]), - 0, ['F1'], LFH, EFH), ?line check(fun() -> F = fun(F,N) when N > 0 -> apply(F,[F,N-1]); (_F,0) -> ok end, F(F, 1000) @@ -1113,11 +1156,11 @@ do_funs(LFH, EFH) -> true = {2,3} == F(2) end, "begin F = fun(X) -> A = 1+X, {X,A} end, true = {2,3} == F(2) end.", true, ['F'], LFH, EFH), - ?line check(fun() -> F = fun(X) -> {erlang,'+'}(X,2) end, - true = 3 == F(1) end, - "begin F = fun(X) -> {erlang,'+'}(X,2) end," - " true = 3 == F(1) end.", true, ['F'], - LFH, EFH), + check(fun() -> F = fun(X) -> erlang:'+'(X,2) end, + true = 3 == F(1) end, + "begin F = fun(X) -> erlang:'+'(X,2) end," + " true = 3 == F(1) end.", true, ['F'], + LFH, EFH), ?line check(fun() -> F = fun(X) -> byte_size(X) end, ?MODULE:do_apply(F,<<"hej">>) end, concat(["begin F = fun(X) -> size(X) end,", @@ -1165,24 +1208,6 @@ do_funs(LFH, EFH) -> concat(["begin F = fun(F, N) -> [", M, ":count_down(F,N) || X <-[1]] end, F(F,2) end."]), [[[0]]], ['F'], LFH, EFH), - - %% Tests for a bug found by the Dialyzer - used to crash. - case test_server:is_native(erl_eval) of - true -> - %% Parameterized modules are not supported by HiPE. - ok; - false -> - check(fun() -> Pmod = erl_eval_helper:new(42), Pmod:add(5) end, - "begin Pmod = erl_eval_helper:new(42), Pmod:add(5) end.", - 47, - ['Pmod'], LFH, EFH), - check(fun() -> Pmod = erl_eval_helper:new(42), - B = Pmod:add(7), B end, - "begin Pmod = erl_eval_helper:new(42), " - "B = Pmod:add(7), B end.", - 49, - ['B','Pmod'], LFH, EFH) - end, ok. count_down(F, N) when N > 0 -> diff --git a/lib/stdlib/test/erl_eval_helper.erl b/lib/stdlib/test/erl_eval_helper.erl deleted file mode 100644 index 6863b40108..0000000000 --- a/lib/stdlib/test/erl_eval_helper.erl +++ /dev/null @@ -1,28 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2005-2010. 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 -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% -%% - --module(erl_eval_helper, [Base]). - --export([add/1]). - -add(Arg) -> - Base+Arg. - - - diff --git a/lib/stdlib/test/erl_expand_records_SUITE.erl b/lib/stdlib/test/erl_expand_records_SUITE.erl index 01cdb92d7b..94b4397a9c 100644 --- a/lib/stdlib/test/erl_expand_records_SUITE.erl +++ b/lib/stdlib/test/erl_expand_records_SUITE.erl @@ -35,7 +35,7 @@ init_per_group/2,end_per_group/2, init_per_testcase/2, end_per_testcase/2]). --export([abstract_module/1, attributes/1, expr/1, guard/1, +-export([attributes/1, expr/1, guard/1, init/1, pattern/1, strict/1, update/1, otp_5915/1, otp_7931/1, otp_5990/1, otp_7078/1, otp_7101/1]). @@ -55,7 +55,7 @@ end_per_testcase(_Case, _Config) -> suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [abstract_module, attributes, expr, guard, init, + [attributes, expr, guard, init, pattern, strict, update, {group, tickets}]. groups() -> @@ -75,40 +75,12 @@ end_per_group(_GroupName, Config) -> Config. -abstract_module(doc) -> - "Compile an abstract module."; -abstract_module(suite) -> []; -abstract_module(Config) when is_list(Config) -> - %% erl_expand_records does not handle abstract modules. But anyway... - File = filename("param.erl", Config), - Beam = filename("param.beam", Config), - Test = <<"-module(param, [A, B]). - - -export([args/1]). - - args(C) -> - X = local(C), - Z = new(A, B), - {X, Z}. - - local(C) -> - module_info(C). - ">>, - - ?line ok = file:write_file(File, Test), - ?line {ok, param} = compile:file(File, [{outdir,?privdir}]), - - ?line ok = file:delete(File), - ?line ok = file:delete(Beam), - ok. - attributes(doc) -> "Import module and functions."; attributes(suite) -> []; attributes(Config) when is_list(Config) -> Ts = [ - <<"-import(erl_expand_records_SUITE). - -import(lists, [append/2, reverse/1]). + <<"-import(lists, [append/2, reverse/1]). -record(r, {a,b}). @@ -157,13 +129,13 @@ expr(Config) when is_list(Config) -> One = 1 = fun f/1(1), 2 = fun(X) -> X end(One + One), 3 = fun exprec_test:f/1(3), - 4 = {exprec_test,f}(4), - 5 = ''.f(5), + 4 = exprec_test:f(4), + 5 = f(5), L = receive {a,message,L0} -> L0 end, - case catch a.b.c:foo(bar) of + case catch a:foo(bar) of {'EXIT', _} -> ok end, _ = receive %Suppress warning. @@ -197,7 +169,7 @@ guard(suite) -> []; guard(Config) when is_list(Config) -> File = filename("guard.erl", Config), Beam = filename("guard.beam", Config), - Test = <<"-module(guard, [A, B]). + Test = <<"-module(guard). -export([t/1]). @@ -263,8 +235,7 @@ pattern(doc) -> pattern(suite) -> []; pattern(Config) when is_list(Config) -> Ts = [ - <<"-import(erl_expand_records_SUITE). - -import(lists, [append/2, reverse/1]). + <<"-import(lists, [append/2, reverse/1]). -record(r, {a,b}). @@ -292,10 +263,10 @@ pattern(Config) when is_list(Config) -> 21 = t(#r{a = #r{}}), 22 = t(2), 23 = t(#r{a = #r{}, b = b}), - 24 = t(a.b.c), + 24 = t(abc), ok. - t(a.b.c) -> + t(abc) -> 24; t($a) -> 2; diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl index 9f9d97b619..f8345559c4 100644 --- a/lib/stdlib/test/erl_lint_SUITE.erl +++ b/lib/stdlib/test/erl_lint_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2012. All Rights Reserved. +%% Copyright Ericsson AB 1999-2013. 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 @@ -50,14 +50,16 @@ unsafe_vars_try/1, guard/1, otp_4886/1, otp_4988/1, otp_5091/1, otp_5276/1, otp_5338/1, otp_5362/1, otp_5371/1, otp_7227/1, otp_5494/1, otp_5644/1, otp_5878/1, - otp_5917/1, otp_6585/1, otp_6885/1, export_all/1, + otp_5917/1, otp_6585/1, otp_6885/1, otp_10436/1, + export_all/1, bif_clash/1, behaviour_basic/1, behaviour_multiple/1, otp_7550/1, otp_8051/1, format_warn/1, on_load_successful/1, on_load_failing/1, - too_many_arguments/1 + too_many_arguments/1, + basic_errors/1,bin_syntax_errors/1 ]). % Default timetrap timeout (set in init_per_testcase). @@ -80,10 +82,10 @@ all() -> unsafe_vars, unsafe_vars2, unsafe_vars_try, guard, otp_4886, otp_4988, otp_5091, otp_5276, otp_5338, otp_5362, otp_5371, otp_7227, otp_5494, otp_5644, - otp_5878, otp_5917, otp_6585, otp_6885, export_all, + otp_5878, otp_5917, otp_6585, otp_6885, otp_10436, export_all, bif_clash, behaviour_basic, behaviour_multiple, otp_7550, otp_8051, format_warn, {group, on_load}, - too_many_arguments]. + too_many_arguments, basic_errors, bin_syntax_errors]. groups() -> [{unused_vars_warn, [], @@ -1307,44 +1309,30 @@ guard(Config) when is_list(Config) -> foo; t3(A) when erlang:is_record(A, {apa}) -> foo; - t3(A) when {erlang,is_record}(A, {apa}) -> - foo; t3(A) when is_record(A, {apa}, 1) -> foo; t3(A) when erlang:is_record(A, {apa}, 1) -> foo; - t3(A) when {erlang,is_record}(A, {apa}, 1) -> - foo; t3(A) when is_record(A, apa, []) -> foo; t3(A) when erlang:is_record(A, apa, []) -> foo; - t3(A) when {erlang,is_record}(A, apa, []) -> - foo; t3(A) when record(A, apa) -> foo; t3(A) when is_record(A, apa) -> foo; t3(A) when erlang:is_record(A, apa) -> - foo; - t3(A) when {erlang,is_record}(A, apa) -> foo. ">>, [warn_unused_vars, nowarn_obsolete_guard], - {error,[{2,erl_lint,illegal_guard_expr}, - {4,erl_lint,illegal_guard_expr}, - {6,erl_lint,illegal_guard_expr}, - {8,erl_lint,illegal_guard_expr}, - {10,erl_lint,illegal_guard_expr}, - {12,erl_lint,illegal_guard_expr}, - {14,erl_lint,illegal_guard_expr}, - {16,erl_lint,illegal_guard_expr}, - {18,erl_lint,illegal_guard_expr}, - {20,erl_lint,illegal_guard_expr}], - [{8,erl_lint,deprecated_tuple_fun}, - {14,erl_lint,deprecated_tuple_fun}, - {20,erl_lint,deprecated_tuple_fun}, - {28,erl_lint,deprecated_tuple_fun}]}}, + {errors,[{2,erl_lint,illegal_guard_expr}, + {4,erl_lint,illegal_guard_expr}, + {6,erl_lint,illegal_guard_expr}, + {8,erl_lint,illegal_guard_expr}, + {10,erl_lint,illegal_guard_expr}, + {12,erl_lint,illegal_guard_expr}, + {14,erl_lint,illegal_guard_expr}], + []}}, {guard6, <<"-record(apa,{a=a,b=foo:bar()}). apa() -> @@ -1364,7 +1352,17 @@ guard(Config) when is_list(Config) -> (is_record(X, apa)*2)]. ">>, [], - []}], + []}, + {guard8, + <<"t(A) when erlang:is_foobar(A) -> ok; + t(A) when A ! ok -> ok; + t(A) when A ++ [x] -> ok." + >>, + [], + {errors,[{1,erl_lint,illegal_guard_expr}, + {2,erl_lint,illegal_guard_expr}, + {3,erl_lint,illegal_guard_expr}],[]}} + ], ?line [] = run(Config, Ts1), ok. @@ -1652,6 +1650,7 @@ otp_5276(Config) when is_list(Config) -> -deprecated([{'_','_',never}]). -deprecated([{{badly,formed},1}]). -deprecated([{'_','_',next_major_release}]). + -deprecated([{atom_to_list,1}]). -export([t/0]). frutt() -> ok. t() -> ok. @@ -1662,8 +1661,9 @@ otp_5276(Config) when is_list(Config) -> {3,erl_lint,{invalid_deprecated,'foo bar'}}, {5,erl_lint,{bad_deprecated,{f,'_'}}}, {8,erl_lint,{invalid_deprecated,{'_','_',never}}}, - {9,erl_lint,{invalid_deprecated,{{badly,formed},1}}}], - [{12,erl_lint,{unused_function,{frutt,0}}}]}}], + {9,erl_lint,{invalid_deprecated,{{badly,formed},1}}}, + {11,erl_lint,{bad_deprecated,{atom_to_list,1}}}], + [{13,erl_lint,{unused_function,{frutt,0}}}]}}], ?line [] = run(Config, Ts), ok. @@ -1745,7 +1745,7 @@ otp_5362(Config) when is_list(Config) -> {otp_5362_2, <<"-export([inline/0]). - -import(lists.foo, [a/1,b/1]). % b/1 is not used + -import(lists, [a/1,b/1]). % b/1 is not used -compile([{inline,{inl,7}}]). % undefined -compile([{inline,[{inl,17}]}]). % undefined @@ -1777,7 +1777,7 @@ otp_5362(Config) when is_list(Config) -> {6,erl_lint,{bad_inline,{inl,17}}}, {11,erl_lint,{undefined_function,{fipp,0}}}, {22,erl_lint,{bad_nowarn_unused_function,{and_not_used,2}}}], - [{3,erl_lint,{unused_import,{{b,1},'lists.foo'}}}, + [{3,erl_lint,{unused_import,{{b,1},lists}}}, {9,erl_lint,{unused_function,{foop,0}}}, {19,erl_lint,{unused_function,{not_used,0}}}, {23,erl_lint,{unused_function,{and_not_used,1}}}]}}, @@ -1909,9 +1909,23 @@ otp_5362(Config) when is_list(Config) -> warn_deprecated_function, warn_bif_clash]}, {errors, - [{2,erl_lint,disallowed_nowarn_bif_clash}],[]}} + [{2,erl_lint,disallowed_nowarn_bif_clash}],[]}}, - ], + {call_deprecated_function, + <<"t(X) -> erlang:hash(X, 2000).">>, + [], + {warnings, + [{1,erl_lint,{deprecated,{erlang,hash,2}, + {erlang,phash2,2},"in a future release"}}]}}, + + {call_removed_function, + <<"t(X) -> regexp:match(X).">>, + [], + {warnings, + [{1,erl_lint,{removed,{regexp,match,1}, + "removed in R15; use the re module instead"}}]}} + + ], ?line [] = run(Config, Ts), ok. @@ -2200,27 +2214,9 @@ otp_5878(Config) when is_list(Config) -> ?line [] = run(Config, Ts), Abstr = <<"-module(lint_test, [A, B]). - - -export([args/1]). - - -record(r, {a = A, b = THIS}). % A and THIS are unbound - - %% param:args(compile,param:new(1,2)). - - args(C) -> - X = local(C), - Z = new(A, B), - F = fun(THIS) -> {x, A} end, % THIS unused and shadowed - {X, Z, THIS, F, #r{}}. - - local(C) -> - module_info(C). ">>, - ?line {error,[{5,erl_lint,{unbound_var,'A'}}, - {5,erl_lint,{unbound_var,'THIS'}}], - [{12,erl_lint,{unused_var,'THIS'}}, - {12,erl_lint,{shadowed_var,'THIS','fun'}}]} - = run_test2(Config, Abstr, [warn_unused_record]), + {errors,[{1,erl_lint,pmod_unsupported}],[]} = + run_test2(Config, Abstr, [warn_unused_record]), QLC1 = <<"-module(lint_test). -include_lib(\"stdlib/include/qlc.hrl\"). @@ -2400,6 +2396,28 @@ otp_6885(Config) when is_list(Config) -> []} = run_test2(Config, Ts, []), ok. +otp_10436(doc) -> + "OTP-6885. Warnings for opaque types."; +otp_10436(suite) -> []; +otp_10436(Config) when is_list(Config) -> + Ts = <<"-module(otp_10436). + -export_type([t1/0]). + -opaque t1() :: {i, integer()}. + -opaque t2() :: {a, atom()}. + ">>, + {warnings,[{4,erl_lint,{not_exported_opaque,{t2,0}}}, + {4,erl_lint,{unused_type,{t2,0}}}]} = + run_test2(Config, Ts, []), + Ts2 = <<"-module(otp_10436_2). + -export_type([t1/0, t2/0]). + -opaque t1() :: term(). + -opaque t2() :: any(). + ">>, + {warnings,[{3,erl_lint,{underspecified_opaque,{t1,0}}}, + {4,erl_lint,{underspecified_opaque,{t2,0}}}]} = + run_test2(Config, Ts2, []), + ok. + export_all(doc) -> "OTP-7392. Warning for export_all."; export_all(Config) when is_list(Config) -> @@ -2848,10 +2866,10 @@ otp_8051(doc) -> otp_8051(Config) when is_list(Config) -> Ts = [{otp_8051, <<"-opaque foo() :: bar(). + -export_type([foo/0]). ">>, [], - {error,[{1,erl_lint,{undefined_type,{bar,0}}}], - [{1,erl_lint,{unused_type,{foo,0}}}]}}], + {errors,[{1,erl_lint,{undefined_type,{bar,0}}}],[]}}], ?line [] = run(Config, Ts), ok. @@ -2980,6 +2998,77 @@ too_many_arguments(Config) when is_list(Config) -> ok. +%% Test some basic errors to improve coverage. +basic_errors(Config) -> + Ts = [{redefine_module, + <<"-module(redefine_module).">>, + [], + {errors,[{1,erl_lint,redefine_module}],[]}}, + + {attr_after_function, + <<"f() -> ok. + -attr(x).">>, + [], + {errors,[{2,erl_lint,{attribute,attr}}],[]}}, + + {redefine_function, + <<"f() -> ok. + f() -> ok.">>, + [], + {errors,[{2,erl_lint,{redefine_function,{f,0}}}],[]}}, + + {redefine_record, + <<"-record(r, {a}). + -record(r, {a}). + f(#r{}) -> ok.">>, + [], + {errors,[{2,erl_lint,{redefine_record,r}}],[]}}, + + {illegal_record_info, + <<"f1() -> record_info(42, record). + f2() -> record_info(shoe_size, record).">>, + [], + {errors,[{1,erl_lint,illegal_record_info}, + {2,erl_lint,illegal_record_info}],[]}}, + + {illegal_expr, + <<"f() -> a:b.">>, + [], + {errors,[{1,erl_lint,illegal_expr}],[]}}, + + {illegal_pattern, + <<"f(A+B) -> ok.">>, + [], + {errors,[{1,erl_lint,illegal_pattern}],[]}} + ], + [] = run(Config, Ts), + ok. + +%% Test binary syntax errors +bin_syntax_errors(Config) -> + Ts = [{bin_syntax_errors, + <<"t(<<X:bad_size>>) -> X; + t(<<_:(x ! y)/integer>>) -> ok; + t(<<X:all/integer>>) -> X; + t(<<X/bad_type>>) -> X; + t(<<X/unit:8>>) -> X; + t(<<X:7/float>>) -> X; + t(<< <<_:8>> >>) -> ok; + t(<<(x ! y):8/integer>>) -> ok. + ">>, + [], + {error,[{1,erl_lint,illegal_bitsize}, + {2,erl_lint,illegal_bitsize}, + {3,erl_lint,illegal_bitsize}, + {4,erl_lint,{undefined_bittype,bad_type}}, + {5,erl_lint,bittype_unit}, + {7,erl_lint,illegal_pattern}, + {8,erl_lint,illegal_pattern}], + [{6,erl_lint,{bad_bitsize,"float"}}]}} + ], + [] = run(Config, Ts), + ok. + run(Config, Tests) -> F = fun({N,P,Ws,E}, BadL) -> case catch run_test(Config, P, Ws) of diff --git a/lib/stdlib/test/erl_pp_SUITE.erl b/lib/stdlib/test/erl_pp_SUITE.erl index 64853ca078..9c0a43abcc 100644 --- a/lib/stdlib/test/erl_pp_SUITE.erl +++ b/lib/stdlib/test/erl_pp_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2011. All Rights Reserved. +%% Copyright Ericsson AB 2006-2013. 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 @@ -40,7 +40,7 @@ init_per_testcase/2, end_per_testcase/2]). -export([ func/1, call/1, recs/1, try_catch/1, if_then/1, - receive_after/1, bits/1, head_tail/1, package/1, + receive_after/1, bits/1, head_tail/1, cond1/1, block/1, case1/1, ops/1, messages/1, old_mnemosyne_syntax/1, import_export/1, misc_attrs/1, @@ -48,7 +48,8 @@ neg_indent/1, otp_6321/1, otp_6911/1, otp_6914/1, otp_8150/1, otp_8238/1, - otp_8473/1, otp_8522/1, otp_8567/1, otp_8664/1, otp_9147/1]). + otp_8473/1, otp_8522/1, otp_8567/1, otp_8664/1, otp_9147/1, + otp_10302/1, otp_10820/1]). %% Internal export. -export([ehook/6]). @@ -74,12 +75,13 @@ all() -> groups() -> [{expr, [], [func, call, recs, try_catch, if_then, receive_after, - bits, head_tail, package, cond1, block, case1, ops, + bits, head_tail, cond1, block, case1, ops, messages, old_mnemosyne_syntax]}, {attributes, [], [misc_attrs, import_export]}, {tickets, [], [otp_6321, otp_6911, otp_6914, otp_8150, otp_8238, - otp_8473, otp_8522, otp_8567, otp_8664, otp_9147]}]. + otp_8473, otp_8522, otp_8567, otp_8664, otp_9147, + otp_10302, otp_10820]}]. init_per_suite(Config) -> Config. @@ -438,9 +440,6 @@ bits(Config) when is_list(Config) -> ?line ok = pp_expr(<<"<<{a,b}/binary>>">>), ?line ok = pp_expr(<<"<<{foo:bar(),b}/binary>>">>), ?line ok = pp_expr(<<"<<(foo:bar()):(foo:bar())/binary>>">>), - ?line ok = pp_expr(<<"<<(foo.bar)/binary>>">>), - ?line ok = pp_expr(<<"<<(foo.bar):all/binary>>">>), - ?line ok = pp_expr(<<"<<(foo.bar):(foo.bar)/binary>>">>), ok. head_tail(suite) -> @@ -462,17 +461,6 @@ head_tail(Config) when is_list(Config) -> ?line compile(Config, Ts), ok. -package(suite) -> - []; -package(Config) when is_list(Config) -> - Ts = [{package_1, - <<"t() -> a.b:foo().">>}, - {package_2, - <<"t() -> .lists:sort([]).">>} - ], - ?line compile(Config, Ts), - ok. - cond1(suite) -> []; cond1(Config) when is_list(Config) -> @@ -550,29 +538,9 @@ messages(Config) when is_list(Config) -> ?line true = "\n" =:= lists:flatten(erl_pp:form({eof,0})), ok. -old_mnemosyne_syntax(suite) -> - []; old_mnemosyne_syntax(Config) when is_list(Config) -> - %% Since we have kept the 'query' syntax and ':-' token, + %% Since we have kept the ':-' token, %% better test that we can pretty print it. - Q = {'query',6, - {lc,6, - {var,6,'X'}, - [{generate,6, - {var,6,'X'}, - {call,6,{atom,6,table},[{atom,6,tab}]}}, - {match,7, - {record_field,7,{var,7,'X'},{atom,7,foo}}, - {atom,7,bar}}]}}, - ?line "query\n" - " [ \n" % extra space... - " X ||\n" - " X <- table(tab),\n" - " X.foo = bar\n" - " ]\n" - "end" = - lists:flatten(erl_pp:expr(Q)), - R = {rule,12,sales,2, [{clause,12, [{var,12,'E'},{atom,12,employee}], @@ -614,13 +582,11 @@ misc_attrs(suite) -> []; misc_attrs(Config) when is_list(Config) -> ?line ok = pp_forms(<<"-module(m). ">>), - ?line ok = pp_forms(<<"-module(m.p, [A,B]). ">>), ?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, []). ">>), ?line ok = pp_forms(<<"-import(lists, [map/2]). ">>), ?line ok = pp_forms(<<"-import(lists, [map/2, foreach/2]). ">>), @@ -634,8 +600,12 @@ misc_attrs(Config) when is_list(Config) -> hook(suite) -> []; hook(Config) when is_list(Config) -> + F = fun(H) -> H end, + do_hook(F). + +do_hook(HookFun) -> Lc = parse_expr(binary_to_list(<<"[X || X <- [1,2,3]].">>)), - H = fun hook/4, + H = HookFun(fun hook/4), Expr = {call,0,{atom,0,fff},[{foo,Lc},{foo,Lc},{foo,Lc}]}, EChars = lists:flatten(erl_pp:expr(Expr, 0, H)), Call = {call,0,{atom,0,foo},[Lc]}, @@ -692,7 +662,7 @@ hook(Config) when is_list(Config) -> GChars2 = erl_pp:guard(G2), ?line true = GChars =:= lists:flatten(GChars2), - EH = {?MODULE, ehook, [foo,bar]}, + EH = HookFun({?MODULE, ehook, [foo,bar]}), XEChars = erl_pp:expr(Expr, -1, EH), ?line true = remove_indentation(EChars) =:= lists:flatten(XEChars), XEChars2 = erl_pp:expr(Expr, EH), @@ -1068,6 +1038,71 @@ otp_9147(Config) when is_list(Config) -> string:tokens(binary_to_list(Bin), "\n")), ok. +otp_10302(doc) -> + "OTP-10302. Unicode characters scanner/parser."; +otp_10302(suite) -> []; +otp_10302(Config) when is_list(Config) -> + Ts = [{uni_1, + <<"t() -> <<(<<\"abc\\x{aaa}\">>):3/binary>>.">>} + ], + compile(Config, Ts), + ok = pp_expr(<<"$\\x{aaa}">>), + ok = pp_expr(<<"\"1\\x{aaa}\"">>), + ok = pp_expr(<<"<<<<\"hej\">>/binary>>">>), + ok = pp_expr(<<"<< <<\"1\\x{aaa}\">>/binary>>">>), + + U = [{encoding,unicode}], + + do_hook(fun(H) -> [{hook,H}] end), + do_hook(fun(H) -> [{hook,H}]++U end), + + ok = pp_expr(<<"$\\x{aaa}">>, [{hook,fun hook/4}]), + + Opts = [{hook, fun unicode_hook/4},{encoding,unicode}], + Lc = parse_expr("[X || X <- [\"\x{400}\",\"\xFF\"]]."), + Expr = {call,0,{atom,0,fff},[{foo,{foo,Lc}},{foo,{foo,Lc}}]}, + EChars = lists:flatten(erl_pp:expr(Expr, 0, Opts)), + Call = {call,0,{atom,0,foo},[{call,0,{atom,0,foo},[Lc]}]}, + Expr2 = {call,0,{atom,0,fff},[Call,Call]}, + EChars2 = erl_pp:exprs([Expr2], U), + EChars = lists:flatten(EChars2), + [$\x{400},$\x{400}] = [C || C <- EChars, C > 255], + + ok = pp_forms(<<"function() -> {\"\x{400}\",$\x{400}}. "/utf8>>, U), + ok = pp_forms("function() -> {\"\x{400}\",$\x{400}}. ", []), + ok. + +unicode_hook({foo,E}, I, P, H) -> + erl_pp:expr({call,0,{atom,0,foo},[E]}, I, P, H). + +otp_10820(doc) -> + "OTP-10820. Unicode filenames."; +otp_10820(suite) -> []; +otp_10820(Config) when is_list(Config) -> + C1 = <<"%% coding: utf-8\n -module(any).">>, + ok = do_otp_10820(Config, C1, "+pc latin1"), + ok = do_otp_10820(Config, C1, "+pc unicode"), + C2 = <<"-module(any).">>, + ok = do_otp_10820(Config, C2, "+pc latin1"), + ok = do_otp_10820(Config, C2, "+pc unicode"). + +do_otp_10820(Config, C, PC) -> + {ok,Node} = start_node(erl_pp_helper, "+fnu " ++ PC), + L = [915,953,959,973,957,953,954,959,957,964], + FileName = filename(L++".erl", Config), + ok = rpc:call(Node, file, write_file, [FileName, C]), + {ok, _, []} = rpc:call(Node, compile, file, + [FileName, [return,'P',{outdir,?privdir}]]), + PFileName = filename(L++".P", Config), + {ok, Bin} = rpc:call(Node, file, read_file, [PFileName]), + true = test_server:stop_node(Node), + true = file_attr_is_string(binary_to_list(Bin)), + ok. + +file_attr_is_string("-file(\"" ++ _) -> true; +file_attr_is_string([_ | L]) -> + file_attr_is_string(L). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% compile(Config, Tests) -> @@ -1137,9 +1172,11 @@ flat_expr(Expr) -> pp_forms(Bin) -> pp_forms(Bin, none). -pp_forms(Bin, Hook) -> - PP1 = (catch parse_and_pp_forms(binary_to_list(Bin), Hook)), - PP2 = (catch parse_and_pp_forms(PP1, Hook)), +pp_forms(Bin, Options) when is_binary(Bin) -> + pp_forms(to_list(Bin, Options), Options); +pp_forms(List, Options) when is_list(List) -> + PP1 = (catch parse_and_pp_forms(List, Options)), + PP2 = (catch parse_and_pp_forms(PP1, Options)), case PP1 =:= PP2 of % same line numbers true -> test_max_line(PP1); @@ -1147,8 +1184,8 @@ pp_forms(Bin, Hook) -> not_ok end. -parse_and_pp_forms(String, Hook) -> - lists:append(lists:map(fun(AF) -> erl_pp:form(AF, Hook) +parse_and_pp_forms(String, Options) -> + lists:append(lists:map(fun(AF) -> erl_pp:form(AF, Options) end, parse_forms(String))). parse_forms(Chars) -> @@ -1158,7 +1195,7 @@ parse_forms(Chars) -> parse_forms2([], _Cont, _Line, Forms) -> lists:reverse(Forms); parse_forms2(String, Cont0, Line, Forms) -> - case erl_scan:tokens(Cont0, String, Line) of + case erl_scan:tokens(Cont0, String, Line, [unicode]) of {done, {ok, Tokens, EndLine}, Chars} -> {ok, Form} = erl_parse:parse_form(Tokens), parse_forms2(Chars, [], EndLine, [Form | Forms]); @@ -1174,10 +1211,12 @@ pp_expr(Bin) -> pp_expr(Bin, none). %% Final dot is added. -pp_expr(Bin, Hook) -> - PP1 = (catch parse_and_pp_expr(binary_to_list(Bin), 0, Hook)), - PPneg = (catch parse_and_pp_expr(binary_to_list(Bin), -1, Hook)), - PP2 = (catch parse_and_pp_expr(PPneg, 0, Hook)), +pp_expr(Bin, Options) when is_binary(Bin) -> + pp_expr(to_list(Bin, Options), Options); +pp_expr(List, Options) when is_list(List) -> + PP1 = (catch parse_and_pp_expr(List, 0, Options)), + PPneg = (catch parse_and_pp_expr(List, -1, Options)), + PP2 = (catch parse_and_pp_expr(PPneg, 0, Options)), if PP1 =:= PP2 -> % same line numbers case @@ -1192,15 +1231,24 @@ pp_expr(Bin, Hook) -> not_ok end. -parse_and_pp_expr(String, Indent, Hook) -> +parse_and_pp_expr(String, Indent, Options) -> StringDot = lists:flatten(String) ++ ".", - erl_pp:expr(parse_expr(StringDot), Indent, Hook). + erl_pp:expr(parse_expr(StringDot), Indent, Options). parse_expr(Chars) -> - {ok, Tokens, _} = erl_scan:string(Chars), + {ok, Tokens, _} = erl_scan:string(Chars, 1, [unicode]), {ok, [Expr]} = erl_parse:parse_exprs(Tokens), Expr. +to_list(Bin, Options) when is_list(Options) -> + case proplists:get_value(encoding, Options) of + unicode -> unicode:characters_to_list(Bin); + encoding -> binary_to_list(Bin); + undefined -> binary_to_list(Bin) + end; +to_list(Bin, _Hook) -> + binary_to_list(Bin). + test_new_line(String) -> case string:chr(String, $\n) of 0 -> ok; @@ -1228,3 +1276,8 @@ filename(Name, Config) -> fail() -> io:format("failed~n"), ?t:fail(). + +%% +fnu means a peer node has to be started; slave will not do +start_node(Name, Xargs) -> + ?line PA = filename:dirname(code:which(?MODULE)), + test_server:start_node(Name, peer, [{args, "-pa " ++ PA ++ " " ++ Xargs}]). diff --git a/lib/stdlib/test/erl_scan_SUITE.erl b/lib/stdlib/test/erl_scan_SUITE.erl index 4298b2c701..ecd181e87c 100644 --- a/lib/stdlib/test/erl_scan_SUITE.erl +++ b/lib/stdlib/test/erl_scan_SUITE.erl @@ -1,7 +1,8 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2011. All Rights Reserved. +%% Copyright Ericsson AB 1998-2013. 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 @@ -20,7 +21,7 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2]). --export([ error_1/1, error_2/1, iso88591/1, otp_7810/1]). +-export([ error_1/1, error_2/1, iso88591/1, otp_7810/1, otp_10302/1]). -import(lists, [nth/2,flatten/1]). -import(io_lib, [print/1]). @@ -59,7 +60,7 @@ end_per_testcase(_Case, Config) -> suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [{group, error}, iso88591, otp_7810]. + [{group, error}, iso88591, otp_7810, otp_10302]. groups() -> [{error, [], [error_1, error_2]}]. @@ -117,13 +118,13 @@ check(String) -> %%% (This should be useful for all format_error functions.) check_error({error, Info, EndLine}, Module0) -> - ?line {ErrorLine, Module, Desc} = Info, - ?line true = (Module == Module0), - ?line assert_type(EndLine, integer), - ?line assert_type(ErrorLine, integer), - ?line true = (ErrorLine =< EndLine), - ?line String = lists:flatten(Module0:format_error(Desc)), - ?line true = io_lib:printable_list(String). + {ErrorLine, Module, Desc} = Info, + true = (Module == Module0), + assert_type(EndLine, integer), + assert_type(ErrorLine, integer), + true = (ErrorLine =< EndLine), + String = lists:flatten(Module0:format_error(Desc)), + true = io_lib:printable_list(String). iso88591(doc) -> ["Tests the support for ISO-8859-1 i.e Latin-1"]; iso88591(suite) -> []; @@ -131,10 +132,10 @@ iso88591(Config) when is_list(Config) -> ?line ok = case catch begin %% Some atom and variable names - V1s = [$�,$�,$�,$�], - V2s = [$N,$�,$r], - A1s = [$h,$�,$r], - A2s = [$�,$r,$e], + V1s = [$Á,$á,$é,$ë], + V2s = [$N,$ä,$r], + A1s = [$h,$ä,$r], + A2s = [$ö,$r,$e], %% Test parsing atom and variable characters. {ok,Ts1,_} = erl_scan:string(V1s ++ " " ++ V2s ++ "\327" ++ @@ -196,7 +197,7 @@ otp_7810(Config) when is_list(Config) -> reserved_words() -> L = ['after', 'begin', 'case', 'try', 'cond', 'catch', 'andalso', 'orelse', 'end', 'fun', 'if', 'let', 'of', - 'query', 'receive', 'when', 'bnot', 'not', 'div', + 'receive', 'when', 'bnot', 'not', 'div', 'rem', 'band', 'and', 'bor', 'bxor', 'bsl', 'bsr', 'or', 'xor'], [begin @@ -214,8 +215,8 @@ atoms() -> ?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}]), + ?line test_string([39,65,200,39], [{atom,1,'AÈ'}]), + ?line test_string("ärlig östen", [{atom,1,ärlig},{atom,1,östen}]), ?line {ok,[{atom,_,'$a'}],{1,6}} = erl_scan:string("'$\\a'", {1,1}), ?line test("'$\\a'"), @@ -289,7 +290,7 @@ errors() -> ?line {error,{1,erl_scan,{string,$","str"}},1} = %" erl_scan:string("\"str"), %" ?line {error,{1,erl_scan,char},1} = erl_scan:string("$"), - ?line test_string([34,65,200,34], [{string,1,"A�"}]), + ?line test_string([34,65,200,34], [{string,1,"AÈ"}]), ?line test_string("\\", [{'\\',1}]), ?line {'EXIT',_} = (catch {foo, erl_scan:string('$\\a', {1,1})}), % type error @@ -354,7 +355,7 @@ dots() -> {".\n", {ok,[{dot,1}],2}}, {".%", {ok,[{dot,1}],1}}, {".\210",{ok,[{dot,1}],1}}, - {".% �h",{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}}, @@ -369,7 +370,7 @@ dots() -> ?line [{column,1},{length,1},{line,1},{text,"."}] = erl_scan:token_info(T2, [column, length, line, text]), ?line {ok,[{dot,_}=T3],{1,6}} = - erl_scan:string(".% �h", {1,1}, text), + erl_scan:string(".% öh", {1,1}, text), ?line [{column,1},{length,1},{line,1},{text,"."}] = erl_scan:token_info(T3, [column, length, line, text]), ?line {error,{{1,2},erl_scan,char},{1,3}} = @@ -472,11 +473,11 @@ chars() -> variables() -> - ?line test_string(" \237_Aou�eiy��", [{var,1,'_Aou�eiy��'}]), + ?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'}]), + ?line test_string("ABDÀ", [{var,1,'ABDÀ'}]), + ?line test_string("Ärlig Östen", [{var,1,'Ärlig'},{var,1,'Östen'}]), ok. eof() -> @@ -814,62 +815,51 @@ 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,character}},1} = - %% ?line {error,{1,erl_scan,{illegal,atom}},1} = - erl_scan:string("'a"++[1089]++"b'"), - ?line {error,{{1,3},erl_scan,{illegal,character}},{1,4}} = + ?line {error,{1,erl_scan,{illegal,atom}},1} = + erl_scan:string("'a"++[1089]++"b'", 1), + ?line {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} = erl_scan:string([$$,$\\,$^,1089]), + ?line {ok,[{char,1,1}],1} = + erl_scan:string([$$,$\\,$^,1089], 1), - ?line {error,{1,erl_scan,Error},1} = erl_scan:string("\"qa\x{aaa}"), - ?line "unterminated string starting with \"qa\\x{AAA}\"" = + ?line {error,{1,erl_scan,Error},1} = + erl_scan:string("\"qa\x{aaa}", 1), + ?line "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,4},erl_scan,{illegal,character}},{1,11}} = + ?line {error,{{1,1},erl_scan,{illegal,atom}},{1,12}} = erl_scan:string("'qa\\x{aaa}'",{1,1}), - Tags = [category, column, length, line, symbol, text], - - %% Workaround. No character codes greater than 255! To be changed. - %% Note: don't remove these tests, just modify them! - - ?line {ok,[{integer,1,1089}],1} = erl_scan:string([$$,1089]), - ?line {ok,[{integer,1,1089}],1} = erl_scan:string([$$,$\\,1089]), + ?line {ok,[{char,1,1089}],1} = + erl_scan:string([$$,1089], 1), + ?line {ok,[{char,1,1089}],1} = + erl_scan:string([$$,$\\,1089], 1), Qs = "$\\x{aaa}", - ?line {ok,[{integer,1,16#aaa}],1} = erl_scan:string(Qs), - ?line {ok,[Q2],{1,9}} = erl_scan:string("$\\x{aaa}", {1,1}, text), - ?line [{category,integer},{column,1},{length,8}, + ?line {ok,[{char,1,$\x{aaa}}],1} = + erl_scan:string(Qs, 1), + ?line {ok,[Q2],{1,9}} = + erl_scan:string("$\\x{aaa}", {1,1}, [text]), + ?line [{category,char},{column,1},{length,8}, {line,1},{symbol,16#aaa},{text,Qs}] = erl_scan:token_info(Q2), U1 = "\"\\x{aaa}\"", - ?line {ok,[T1,T2,T3],{1,10}} = erl_scan:string(U1, {1,1}, text), - ?line [{category,'['},{column,1},{length,1},{line,1}, - {symbol,'['},{text,"\""}] = erl_scan:token_info(T1, Tags), - ?line [{category,integer},{column,2},{length,7}, - {line,1},{symbol,16#aaa},{text,"\\x{aaa}"}] = - erl_scan:token_info(T2, Tags), - ?line [{category,']'},{column,9},{length,1},{line,1}, - {symbol,']'},{text,"\""}] = erl_scan:token_info(T3, Tags), - ?line {ok,[{'[',1},{integer,1,16#aaa},{']',1}],1} = - erl_scan:string(U1, 1), + {ok, + [{string,[{line,1},{column,1},{text,"\"\\x{aaa}\""}],[2730]}], + {1,10}} = erl_scan:string(U1, {1,1}, [text]), + {ok,[{string,1,[2730]}],1} = erl_scan:string(U1, 1), U2 = "\"\\x41\\x{fff}\\x42\"", - ?line {ok,[{'[',1},{char,1,16#41},{',',1},{integer,1,16#fff}, - {',',1},{char,1,16#42},{']',1}],1} = erl_scan:string(U2, 1), + {ok,[{string,1,[$\x41,$\x{fff},$\x42]}],1} = erl_scan:string(U2, 1), U3 = "\"a\n\\x{fff}\n\"", - ?line {ok,[{'[',1},{char,1,$a},{',',1},{char,1,$\n}, - {',',2},{integer,2,16#fff},{',',2},{char,2,$\n}, - {']',3}],3} = - erl_scan:string(U3, 1), + {ok,[{string,1,[$a,$\n,$\x{fff},$\n]}],3} = erl_scan:string(U3, 1), U4 = "\"\\^\n\\x{aaa}\\^\n\"", - ?line {ok,[{'[',1},{char,1,$\n},{',',2},{integer,2,16#aaa}, - {',',2},{char,2,$\n},{']',3}],3} = erl_scan:string(U4, 1), + {ok,[{string,1,[$\n,$\x{aaa},$\n]}],3} = erl_scan:string(U4, 1), %% Keep these tests: ?line test(Qs), @@ -879,18 +869,15 @@ unicode() -> ?line test(U4), Str1 = "\"ab" ++ [1089] ++ "cd\"", - ?line {ok,[{'[',1},{char,1,$a},{',',1},{char,1,$b},{',',1}, - {integer,1,1089},{',',1},{char,1,$c},{',',1}, - {char,1,$d},{']',1}],1} = erl_scan:string(Str1), - ?line {ok,[{'[',_},{char,_,$a},{',',_},{char,_,$b},{',',_}, - {integer,_,1089},{',',_},{char,_,$c},{',',_}, - {char,_,$d},{']',_}],{1,8}} = erl_scan:string(Str1, {1,1}), + {ok,[{string,1,[$a,$b,1089,$c,$d]}],1} = erl_scan:string(Str1, 1), + {ok,[{string,{1,1},[$a,$b,1089,$c,$d]}],{1,8}} = + erl_scan:string(Str1, {1,1}), ?line test(Str1), Comment = "%% "++[1089], - ?line {ok,[{comment,1,[$%,$%,$\s,1089]}],1} = - erl_scan:string(Comment, 1, return), - ?line {ok,[{comment,_,[$%,$%,$\s,1089]}],{1,5}} = - erl_scan:string(Comment, {1,1}, return), + {ok,[{comment,1,[$%,$%,$\s,1089]}],1} = + erl_scan:string(Comment, 1, [return]), + {ok,[{comment,{1,1},[$%,$%,$\s,1089]}],{1,5}} = + erl_scan:string(Comment, {1,1}, [return]), ok. more_chars() -> @@ -958,6 +945,182 @@ more_chars() -> erl_scan:string("$\\xg", {1,1}), ok. +otp_10302(doc) -> + "OTP-10302. Unicode characters scanner/parser."; +otp_10302(suite) -> + []; +otp_10302(Config) when is_list(Config) -> + %% From unicode(): + {error,{1,erl_scan,{illegal,atom}},1} = + erl_scan:string("'a"++[1089]++"b'", 1), + {error,{{1,1},erl_scan,{illegal,atom}},{1,12}} = + erl_scan:string("'qa\\x{aaa}'",{1,1}), + + {ok,[{char,1,1089}],1} = erl_scan:string([$$,1089], 1), + {ok,[{char,1,1089}],1} = erl_scan:string([$$,$\\,1089],1), + + Qs = "$\\x{aaa}", + {ok,[{char,1,2730}],1} = erl_scan:string(Qs,1), + {ok,[Q2],{1,9}} = erl_scan:string(Qs,{1,1},[text]), + [{category,char},{column,1},{length,8}, + {line,1},{symbol,16#aaa},{text,Qs}] = + erl_scan:token_info(Q2), + + Tags = [category, column, length, line, symbol, text], + + U1 = "\"\\x{aaa}\"", + {ok,[T1],{1,10}} = erl_scan:string(U1, {1,1}, [text]), + [{category,string},{column,1},{length,9},{line,1}, + {symbol,[16#aaa]},{text,U1}] = erl_scan:token_info(T1, Tags), + + U2 = "\"\\x41\\x{fff}\\x42\"", + {ok,[{string,1,[65,4095,66]}],1} = erl_scan:string(U2, 1), + + U3 = "\"a\n\\x{fff}\n\"", + {ok,[{string,1,[97,10,4095,10]}],3} = erl_scan:string(U3, 1), + + U4 = "\"\\^\n\\x{aaa}\\^\n\"", + {ok,[{string,1,[10,2730,10]}],3} = erl_scan:string(U4, 1,[]), + + Str1 = "\"ab" ++ [1089] ++ "cd\"", + {ok,[{string,1,[97,98,1089,99,100]}],1} = + erl_scan:string(Str1,1), + {ok,[{string,{1,1},[97,98,1089,99,100]}],{1,8}} = + erl_scan:string(Str1, {1,1}), + + OK1 = 16#D800-1, + OK2 = 16#DFFF+1, + OK3 = 16#FFFE-1, + OK4 = 16#FFFF+1, + OKL = [OK1,OK2,OK3,OK4], + + Illegal1 = 16#D800, + Illegal2 = 16#DFFF, + Illegal3 = 16#FFFE, + Illegal4 = 16#FFFF, + IllegalL = [Illegal1,Illegal2,Illegal3,Illegal4], + + [{ok,[{comment,1,[$%,$%,$\s,OK]}],1} = + erl_scan:string("%% "++[OK], 1, [return]) || + OK <- OKL], + {ok,[{comment,_,[$%,$%,$\s,OK1]}],{1,5}} = + erl_scan:string("%% "++[OK1], {1,1}, [return]), + [{error,{1,erl_scan,{illegal,character}},1} = + erl_scan:string("%% "++[Illegal], 1, [return]) || + Illegal <- IllegalL], + {error,{{1,1},erl_scan,{illegal,character}},{1,5}} = + erl_scan:string("%% "++[Illegal1], {1,1}, [return]), + + [{ok,[],1} = erl_scan:string("%% "++[OK], 1, []) || + OK <- OKL], + {ok,[],{1,5}} = erl_scan:string("%% "++[OK1], {1,1}, []), + [{error,{1,erl_scan,{illegal,character}},1} = + erl_scan:string("%% "++[Illegal], 1, []) || + Illegal <- IllegalL], + {error,{{1,1},erl_scan,{illegal,character}},{1,5}} = + erl_scan:string("%% "++[Illegal1], {1,1}, []), + + [{ok,[{string,{1,1},[OK]}],{1,4}} = + erl_scan:string("\""++[OK]++"\"",{1,1}) || + OK <- OKL], + [{error,{{1,2},erl_scan,{illegal,character}},{1,3}} = + erl_scan:string("\""++[OK]++"\"",{1,1}) || + OK <- IllegalL], + + [{error,{{1,1},erl_scan,{illegal,character}},{1,2}} = + erl_scan:string([Illegal],{1,1}) || + Illegal <- IllegalL], + + {ok,[{char,{1,1},OK1}],{1,3}} = + erl_scan:string([$$,OK1],{1,1}), + {error,{{1,1},erl_scan,{illegal,character}},{1,2}} = + erl_scan:string([$$,Illegal1],{1,1}), + + {ok,[{char,{1,1},OK1}],{1,4}} = + erl_scan:string([$$,$\\,OK1],{1,1}), + {error,{{1,1},erl_scan,{illegal,character}},{1,4}} = + erl_scan:string([$$,$\\,Illegal1],{1,1}), + + {ok,[{string,{1,1},[55295]}],{1,5}} = + erl_scan:string("\"\\"++[OK1]++"\"",{1,1}), + {error,{{1,2},erl_scan,{illegal,character}},{1,4}} = + erl_scan:string("\"\\"++[Illegal1]++"\"",{1,1}), + + {ok,[{char,{1,1},OK1}],{1,10}} = + erl_scan:string("$\\x{D7FF}",{1,1}), + {error,{{1,1},erl_scan,{illegal,character}},{1,10}} = + erl_scan:string("$\\x{D800}",{1,1}), + + %% Not erl_scan, but erl_parse. + {integer,0,1} = erl_parse:abstract(1), + Float = 3.14, {float,0,Float} = erl_parse:abstract(Float), + {nil,0} = erl_parse:abstract([]), + {bin,0, + [{bin_element,0,{integer,0,1},default,default}, + {bin_element,0,{integer,0,2},default,default}]} = + erl_parse:abstract(<<1,2>>), + {cons,0,{tuple,0,[{atom,0,a}]},{atom,0,b}} = + erl_parse:abstract([{a} | b]), + {string,0,"str"} = erl_parse:abstract("str"), + {cons,0, + {integer,0,$a}, + {cons,0,{integer,0,1024},{string,0,"c"}}} = + erl_parse:abstract("a"++[1024]++"c"), + + Line = 17, + {integer,Line,1} = erl_parse:abstract(1, Line), + Float = 3.14, {float,Line,Float} = erl_parse:abstract(Float, Line), + {nil,Line} = erl_parse:abstract([], Line), + {bin,Line, + [{bin_element,Line,{integer,Line,1},default,default}, + {bin_element,Line,{integer,Line,2},default,default}]} = + erl_parse:abstract(<<1,2>>, Line), + {cons,Line,{tuple,Line,[{atom,Line,a}]},{atom,Line,b}} = + erl_parse:abstract([{a} | b], Line), + {string,Line,"str"} = erl_parse:abstract("str", Line), + {cons,Line, + {integer,Line,$a}, + {cons,Line,{integer,Line,1024},{string,Line,"c"}}} = + erl_parse:abstract("a"++[1024]++"c", Line), + + Opts1 = [{line,17}], + {integer,Line,1} = erl_parse:abstract(1, Opts1), + Float = 3.14, {float,Line,Float} = erl_parse:abstract(Float, Opts1), + {nil,Line} = erl_parse:abstract([], Opts1), + {bin,Line, + [{bin_element,Line,{integer,Line,1},default,default}, + {bin_element,Line,{integer,Line,2},default,default}]} = + erl_parse:abstract(<<1,2>>, Opts1), + {cons,Line,{tuple,Line,[{atom,Line,a}]},{atom,Line,b}} = + erl_parse:abstract([{a} | b], Opts1), + {string,Line,"str"} = erl_parse:abstract("str", Opts1), + {cons,Line, + {integer,Line,$a}, + {cons,Line,{integer,Line,1024},{string,Line,"c"}}} = + erl_parse:abstract("a"++[1024]++"c", Opts1), + + [begin + {integer,Line,1} = erl_parse:abstract(1, Opts2), + Float = 3.14, {float,Line,Float} = erl_parse:abstract(Float, Opts2), + {nil,Line} = erl_parse:abstract([], Opts2), + {bin,Line, + [{bin_element,Line,{integer,Line,1},default,default}, + {bin_element,Line,{integer,Line,2},default,default}]} = + erl_parse:abstract(<<1,2>>, Opts2), + {cons,Line,{tuple,Line,[{atom,Line,a}]},{atom,Line,b}} = + erl_parse:abstract([{a} | b], Opts2), + {string,Line,"str"} = erl_parse:abstract("str", Opts2), + {string,Line,[97,1024,99]} = + erl_parse:abstract("a"++[1024]++"c", Opts2) + end || Opts2 <- [[{encoding,unicode},{line,Line}], + [{encoding,utf8},{line,Line}]]], + + {cons,0, + {integer,0,97}, + {cons,0,{integer,0,1024},{string,0,"c"}}} = + erl_parse:abstract("a"++[1024]++"c", [{encoding,latin1}]), + ok. + test_string(String, Expected) -> {ok, Expected, _End} = erl_scan:string(String), test(String). diff --git a/lib/stdlib/test/escript_SUITE.erl b/lib/stdlib/test/escript_SUITE.erl index 38c085616d..cf5fb12686 100644 --- a/lib/stdlib/test/escript_SUITE.erl +++ b/lib/stdlib/test/escript_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2012. All Rights Reserved. +%% Copyright Ericsson AB 2007-2013. 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 @@ -26,6 +26,7 @@ errors/1, strange_name/1, emulator_flags/1, + emulator_flags_no_shebang/1, module_script/1, beam_script/1, archive_script/1, @@ -34,7 +35,8 @@ create_and_extract/1, foldl/1, overflow/1, - verify_sections/3 + verify_sections/3, + unicode/1 ]). -include_lib("test_server/include/test_server.hrl"). @@ -44,9 +46,10 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [basic, errors, strange_name, emulator_flags, + emulator_flags_no_shebang, module_script, beam_script, archive_script, epp, create_and_extract, foldl, overflow, - archive_script_file_access]. + archive_script_file_access, unicode]. groups() -> []. @@ -64,7 +67,7 @@ end_per_group(_GroupName, Config) -> Config. init_per_testcase(_Case, Config) -> - ?line Dog = ?t:timetrap(?t:minutes(2)), + ?line Dog = ?t:timetrap(?t:minutes(5)), [{watchdog,Dog}|Config]. end_per_testcase(_Case, Config) -> @@ -149,6 +152,21 @@ emulator_flags(Config) when is_list(Config) -> ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +emulator_flags_no_shebang(Config) when is_list(Config) -> + Data = ?config(data_dir, Config), + Dir = filename:absname(Data), %Get rid of trailing slash. + %% Need run_with_opts, to always use "escript" explicitly + ?line run_with_opts(Dir, "", "emulator_flags_no_shebang -arg1 arg2 arg3", + [<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n" + "nostick:[{nostick,[]}]\n" + "mnesia:[{mnesia,[\"dir\",\"a/directory\"]},{mnesia,[\"debug\",\"verbose\"]}]\n" + "ERL_FLAGS=false\n" + "unknown:[]\n" + "ExitCode:0">>]), + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Pick the source code from the emulator_flags script %% Generate a new escript with a module header @@ -618,7 +636,7 @@ compile_files([File | Files], SrcDir, OutDir) -> case filename:extension(File) of ".erl" -> AbsFile = filename:join([SrcDir, File]), - case compile:file(AbsFile, [{outdir, OutDir}]) of + case compile:file(AbsFile, [{outdir, OutDir},report_errors]) of {ok, _Mod} -> compile_files(Files, SrcDir, OutDir); Error -> @@ -810,6 +828,8 @@ normalize_sections(Sections) -> end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + foldl(Config) when is_list(Config) -> {NewFile, _FileInfo, _EmuArg, _Source, @@ -887,6 +907,20 @@ emulate_escript_foldl(Fun, Acc, File) -> {error, Reason} end. +unicode(Config) when is_list(Config) -> + Data = ?config(data_dir, Config), + Dir = filename:absname(Data), %Get rid of trailing slash. + run(Dir, "unicode1", + [<<"escript: exception error: an error occurred when evaluating" + " an arithmetic expression\n in operator '/'/2\n " + "called as <<224,170,170>> / <<224,170,170>>\nExitCode:127">>]), + run(Dir, "unicode2", + [<<"escript: exception error: an error occurred when evaluating" + " an arithmetic expression\n in operator '/'/2\n " + "called as <<\"\xaa\">> / <<\"\xaa\">>\nExitCode:127">>]), + run(Dir, "unicode3", [<<"ExitCode:0">>]), + ok. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% overflow(Config) when is_list(Config) -> diff --git a/lib/stdlib/test/escript_SUITE_data/emulator_flags_no_shebang b/lib/stdlib/test/escript_SUITE_data/emulator_flags_no_shebang new file mode 100644 index 0000000000..47d843ebe1 --- /dev/null +++ b/lib/stdlib/test/escript_SUITE_data/emulator_flags_no_shebang @@ -0,0 +1,10 @@ +%% -*- erlang -*- +%%! -nostick -mnesia dir a/directory -mnesia debug verbose + +main(MainArgs) -> + io:format("main:~p\n",[MainArgs]), + ErlArgs = init:get_arguments(), + io:format("nostick:~p\n",[[E || E <- ErlArgs, element(1, E) =:= nostick]]), + io:format("mnesia:~p\n", [[E || E <- ErlArgs, element(1, E) =:= mnesia]]), + io:format("ERL_FLAGS=~p\n", [os:getenv("ERL_FLAGS")]), + io:format("unknown:~p\n",[[E || E <- ErlArgs, element(1, E) =:= unknown]]). diff --git a/lib/stdlib/test/escript_SUITE_data/unicode1 b/lib/stdlib/test/escript_SUITE_data/unicode1 new file mode 100755 index 0000000000..351bb785e5 --- /dev/null +++ b/lib/stdlib/test/escript_SUITE_data/unicode1 @@ -0,0 +1,14 @@ +#!/usr/bin/env escript +%% -*- erlang -*- + +-export([main/1]). + +main(_) -> + ok = io:setopts([{encoding,unicode}]), + _D = erlang:system_flag(backtrace_depth, 0), + A = <<"\x{aaa}"/utf8>>, + S = lists:flatten(io_lib:format("~p/~p.", [A, A])), + {ok, Ts, _} = erl_scan:string(S, 1, [unicode]), + {ok, Es} = erl_parse:parse_exprs(Ts), + B = erl_eval:new_bindings(), + erl_eval:exprs(Es, B). diff --git a/lib/stdlib/test/escript_SUITE_data/unicode2 b/lib/stdlib/test/escript_SUITE_data/unicode2 new file mode 100755 index 0000000000..495188f6f0 --- /dev/null +++ b/lib/stdlib/test/escript_SUITE_data/unicode2 @@ -0,0 +1,14 @@ +#!/usr/bin/env escript +%% -*- erlang -*- + +-export([main/1]). + +main(_) -> + ok = io:setopts([{encoding,latin1}]), + _D = erlang:system_flag(backtrace_depth, 0), + A = <<"\x{aa}">>, + S = lists:flatten(io_lib:format("~p/~p.", [A, A])), + {ok, Ts, _} = erl_scan:string(S, 1, [unicode]), + {ok, Es} = erl_parse:parse_exprs(Ts), + B = erl_eval:new_bindings(), + erl_eval:exprs(Es, B). diff --git a/lib/stdlib/test/escript_SUITE_data/unicode3 b/lib/stdlib/test/escript_SUITE_data/unicode3 new file mode 100755 index 0000000000..944487dcae --- /dev/null +++ b/lib/stdlib/test/escript_SUITE_data/unicode3 @@ -0,0 +1,13 @@ +#!/usr/bin/env escript +%% -*- erlang; coding: utf-8 -*- + +-export([main/1]). + +main(_) -> + ok = io:setopts([{encoding,unicode}]), + Bin1 = <<"örn_Ѐ שלום-שלום+של 日本語">>, + + L = [246,114,110,95,1024,32,1513,1500,1493,1501,45,1513,1500,1493, + 1501,43,1513,1500,32,26085,26412,35486], + L = unicode:characters_to_list(Bin1, utf8), + ok. diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl index 95f10b1df3..b6c94a6f6d 100644 --- a/lib/stdlib/test/ets_SUITE.erl +++ b/lib/stdlib/test/ets_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2012. All Rights Reserved. +%% Copyright Ericsson AB 1996-2013. 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 @@ -96,7 +96,7 @@ misc1_do/1, safe_fixtable_do/1, info_do/1, dups_do/1, heavy_lookup_do/1, heavy_lookup_element_do/1, member_do/1, otp_5340_do/1, otp_7665_do/1, meta_wb_do/1, do_heavy_concurrent/1, tab2file2_do/2, exit_large_table_owner_do/2, - types_do/1, sleeper/0, rpc_externals/0, memory_do/1, + types_do/1, sleeper/0, memory_do/1, ms_tracee_dummy/1, ms_tracee_dummy/2, ms_tracee_dummy/3, ms_tracee_dummy/4 ]). @@ -2170,20 +2170,29 @@ heir_do(Opts) -> ?line undefined = ets:info(foo), %% When heir dies and pid reused before founder dies - NextPidIx = erts_debug:get_internal_state(next_pid), - {Founder4,MrefF4} = my_spawn_monitor(fun()->heir_founder(Master,"The dying heir",Opts)end), - {Heir4,MrefH4} = my_spawn_monitor(fun()->heir_heir(Founder4)end), - Founder4 ! {go, Heir4}, - ?line {'DOWN', MrefH4, process, Heir4, normal} = receive_any(), - erts_debug:set_internal_state(next_pid, NextPidIx), - {Heir4,MrefH4_B} = spawn_monitor_with_pid(Heir4, - fun()-> ?line die_please = receive_any() end), - Founder4 ! die_please, - ?line {'DOWN', MrefF4, process, Founder4, normal} = receive_any(), - Heir4 ! die_please, - ?line {'DOWN', MrefH4_B, process, Heir4, normal} = receive_any(), - ?line undefined = ets:info(foo), - + repeat_while(fun() -> + NextPidIx = erts_debug:get_internal_state(next_pid), + {Founder4,MrefF4} = my_spawn_monitor(fun()->heir_founder(Master,"The dying heir",Opts)end), + {Heir4,MrefH4} = my_spawn_monitor(fun()->heir_heir(Founder4)end), + Founder4 ! {go, Heir4}, + ?line {'DOWN', MrefH4, process, Heir4, normal} = receive_any(), + erts_debug:set_internal_state(next_pid, NextPidIx), + DoppelGanger = spawn_monitor_with_pid(Heir4, + fun()-> ?line die_please = receive_any() end), + Founder4 ! die_please, + ?line {'DOWN', MrefF4, process, Founder4, normal} = receive_any(), + case DoppelGanger of + {Heir4,MrefH4_B} -> + Heir4 ! die_please, + ?line {'DOWN', MrefH4_B, process, Heir4, normal} = receive_any(), + ?line undefined = ets:info(foo), + false; + failed -> + io:format("Failed to spawn process with pid ~p\n", [Heir4]), + true % try again + end + end), + ?line verify_etsmem(EtsMem). heir_founder(Master, HeirData, Opts) -> @@ -4208,21 +4217,13 @@ heavy_lookup_element(Config) when is_list(Config) -> repeat_for_opts(heavy_lookup_element_do). heavy_lookup_element_do(Opts) -> - ?line EtsMem = etsmem(), - ?line Tab = ets_new(foobar_table, [set, protected, {keypos, 2} | Opts]), - ?line ok = fill_tab2(Tab, 0, 7000), - case os:type() of - vxworks -> - ?line ?t:do_times(5, ?MODULE, do_lookup_element, - [Tab, 6999, 1]); - % lookup ALL elements 5 times. - _ -> - ?line ?t:do_times(50, ?MODULE, do_lookup_element, - [Tab, 6999, 1]) - % lookup ALL elements 50 times. - end, - ?line true = ets:delete(Tab), - ?line verify_etsmem(EtsMem). + EtsMem = etsmem(), + Tab = ets_new(foobar_table, [set, protected, {keypos, 2} | Opts]), + ok = fill_tab2(Tab, 0, 7000), + % lookup ALL elements 50 times + ?t:do_times(50, ?MODULE, do_lookup_element, [Tab, 6999, 1]), + true = ets:delete(Tab), + verify_etsmem(EtsMem). do_lookup_element(_Tab, 0, _) -> ok; do_lookup_element(Tab, N, M) -> @@ -5795,25 +5796,20 @@ receive_any_spinning(Loops, N, Tries) when N>0 -> spawn_monitor_with_pid(Pid, Fun) when is_pid(Pid) -> - spawn_monitor_with_pid(Pid, Fun, 1, 10). + spawn_monitor_with_pid(Pid, Fun, 10). -spawn_monitor_with_pid(Pid, Fun, N, M) when N > M*10 -> - spawn_monitor_with_pid(Pid, Fun, N, M*10); -spawn_monitor_with_pid(Pid, Fun, N, M) -> - ?line false = is_process_alive(Pid), +spawn_monitor_with_pid(_, _, 0) -> + failed; +spawn_monitor_with_pid(Pid, Fun, N) -> case my_spawn(fun()-> case self() of Pid -> Fun(); _ -> die end end) of - Pid -> + Pid -> {Pid, erlang:monitor(process, Pid)}; Other -> - case N rem M of - 0 -> io:format("Failed ~p times to get pid ~p (current = ~p)\n",[N,Pid,Other]); - _ -> ok - end, - spawn_monitor_with_pid(Pid,Fun,N+1,M) + spawn_monitor_with_pid(Pid,Fun,N-1) end. @@ -5993,33 +5989,103 @@ make_ext_ref() -> init_externals() -> case get(externals) of undefined -> - SysDistSz = ets:info(sys_dist,size), - ?line Pa = filename:dirname(code:which(?MODULE)), - ?line {ok, Node} = test_server:start_node(plopp, slave, [{args, " -pa " ++ Pa}]), - ?line Res = case rpc:call(Node, ?MODULE, rpc_externals, []) of - {badrpc, {'EXIT', E}} -> - test_server:fail({rpcresult, E}); - R -> R - end, - ?line test_server:stop_node(Node), - - %% Wait for table 'sys_dist' to stabilize - repeat_while(fun() -> - case ets:info(sys_dist,size) of - SysDistSz -> false; - Sz -> - io:format("Waiting for sys_dist to revert size from ~p to size ~p\n", - [Sz, SysDistSz]), - receive after 1000 -> true end - end - end), + OtherNode = {gurka@sallad, 1}, + Res = {mk_pid(OtherNode, 7645, 8123), + mk_port(OtherNode, 187489773), + mk_ref(OtherNode, [262143, 1293964255, 3291964278])}, put(externals, Res); {_,_,_} -> ok end. -rpc_externals() -> - {self(), make_port(), make_ref()}. +%% +%% Node container constructor functions +%% + +-define(VERSION_MAGIC, 131). +-define(PORT_EXT, 102). +-define(PID_EXT, 103). +-define(NEW_REFERENCE_EXT, 114). + +uint32_be(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 32 -> + [(Uint bsr 24) band 16#ff, + (Uint bsr 16) band 16#ff, + (Uint bsr 8) band 16#ff, + Uint band 16#ff]; +uint32_be(Uint) -> + exit({badarg, uint32_be, [Uint]}). + +uint16_be(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 16 -> + [(Uint bsr 8) band 16#ff, + Uint band 16#ff]; +uint16_be(Uint) -> + exit({badarg, uint16_be, [Uint]}). + +uint8(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 8 -> + Uint band 16#ff; +uint8(Uint) -> + exit({badarg, uint8, [Uint]}). + +mk_pid({NodeName, Creation}, Number, Serial) when is_atom(NodeName) -> + <<?VERSION_MAGIC, NodeNameExt/binary>> = term_to_binary(NodeName), + mk_pid({NodeNameExt, Creation}, Number, Serial); +mk_pid({NodeNameExt, Creation}, Number, Serial) -> + case catch binary_to_term(list_to_binary([?VERSION_MAGIC, + ?PID_EXT, + NodeNameExt, + uint32_be(Number), + uint32_be(Serial), + uint8(Creation)])) of + Pid when is_pid(Pid) -> + Pid; + {'EXIT', {badarg, _}} -> + exit({badarg, mk_pid, [{NodeNameExt, Creation}, Number, Serial]}); + Other -> + exit({unexpected_binary_to_term_result, Other}) + end. + +mk_port({NodeName, Creation}, Number) when is_atom(NodeName) -> + <<?VERSION_MAGIC, NodeNameExt/binary>> = term_to_binary(NodeName), + mk_port({NodeNameExt, Creation}, Number); +mk_port({NodeNameExt, Creation}, Number) -> + case catch binary_to_term(list_to_binary([?VERSION_MAGIC, + ?PORT_EXT, + NodeNameExt, + uint32_be(Number), + uint8(Creation)])) of + Port when is_port(Port) -> + Port; + {'EXIT', {badarg, _}} -> + exit({badarg, mk_port, [{NodeNameExt, Creation}, Number]}); + Other -> + exit({unexpected_binary_to_term_result, Other}) + end. + +mk_ref({NodeName, Creation}, Numbers) when is_atom(NodeName), + is_integer(Creation), + is_list(Numbers) -> + <<?VERSION_MAGIC, NodeNameExt/binary>> = term_to_binary(NodeName), + mk_ref({NodeNameExt, Creation}, Numbers); +mk_ref({NodeNameExt, Creation}, Numbers) when is_binary(NodeNameExt), + is_integer(Creation), + is_list(Numbers) -> + case catch binary_to_term(list_to_binary([?VERSION_MAGIC, + ?NEW_REFERENCE_EXT, + uint16_be(length(Numbers)), + NodeNameExt, + uint8(Creation), + lists:map(fun (N) -> + uint32_be(N) + end, + Numbers)])) of + Ref when is_reference(Ref) -> + Ref; + {'EXIT', {badarg, _}} -> + exit({badarg, mk_ref, [{NodeNameExt, Creation}, Numbers]}); + Other -> + exit({unexpected_binary_to_term_result, Other}) + end. + make_sub_binary(Bin) when is_binary(Bin) -> {_,B} = split_binary(list_to_binary([0,1,3,Bin]), 3), diff --git a/lib/stdlib/test/filelib_SUITE.erl b/lib/stdlib/test/filelib_SUITE.erl index 1de639a166..4a67d68428 100644 --- a/lib/stdlib/test/filelib_SUITE.erl +++ b/lib/stdlib/test/filelib_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2011. All Rights Reserved. +%% Copyright Ericsson AB 2005-2013. 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 @@ -65,19 +65,26 @@ wildcard_one(Config) when is_list(Config) -> ?line {ok,OldCwd} = file:get_cwd(), ?line Dir = filename:join(?config(priv_dir, Config), "wildcard_one"), ?line ok = file:make_dir(Dir), + do_wildcard_1(Dir, + fun(Wc) -> + filelib:wildcard(Wc, Dir, erl_prim_loader) + end), ?line file:set_cwd(Dir), - ?line do_wildcard_1(Dir, fun(Wc) -> filelib:wildcard(Wc) end), + do_wildcard_1(Dir, + fun(Wc) -> + L = filelib:wildcard(Wc), + L = filelib:wildcard(Wc, erl_prim_loader), + L = filelib:wildcard(Wc, "."), + L = filelib:wildcard(Wc, Dir) + end), ?line file:set_cwd(OldCwd), ?line ok = file:del_dir(Dir), ok. wildcard_two(Config) when is_list(Config) -> ?line Dir = filename:join(?config(priv_dir, Config), "wildcard_two"), - ?line DirB = unicode:characters_to_binary(Dir, file:native_name_encoding()), ?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) -> io:format("~p~n",[{Wc,DirB, X = filelib:wildcard(Wc, DirB)}]), - [unicode:characters_to_list(Y,file:native_name_encoding()) || Y <- X] end), ?line do_wildcard_1(Dir, fun(Wc) -> filelib:wildcard(Wc, Dir++"/") end), case os:type() of {win32,_} -> @@ -130,6 +137,9 @@ do_wildcard_2(Dir, Wcf) -> ?line ["abc","abcdef"] = Wcf("a*{def,}"), ?line ["abc","abcdef"] = Wcf("a*{,def}"), + %% Constant wildcard. + ["abcdef"] = Wcf("abcdef"), + %% Negative tests. ?line [] = Wcf("b*"), ?line [] = Wcf("bufflig"), @@ -157,6 +167,8 @@ do_wildcard_4(Dir, Wcf) -> All = ["a-","aA","aB","aC","a[","a]"], ?line Files = mkfiles(lists:reverse(All), Dir), ?line All = Wcf("a[][A-C-]"), + ["a-"] = Wcf("a[-]"), + ["a["] = Wcf("a["), ?line del(Files), do_wildcard_5(Dir, Wcf). @@ -173,11 +185,84 @@ do_wildcard_5(Dir, Wcf) -> ?line ["blurf/nisse"] = Wcf("*/nisse"), ?line [] = Wcf("mountain/*"), ?line [] = Wcf("xa/gurka"), + ["blurf/nisse"] = Wcf("blurf/nisse"), %% Cleanup ?line del(Files), - ?line foreach(fun(D) -> ok = file:del_dir(filename:join(Dir, D)) end, Dirs). + ?line foreach(fun(D) -> ok = file:del_dir(filename:join(Dir, D)) end, Dirs), + do_wildcard_6(Dir, Wcf). + +do_wildcard_6(Dir, Wcf) -> + ok = file:make_dir(filename:join(Dir, "xbin")), + All = ["xbin/a.x","xbin/b.x","xbin/c.x"], + Files = mkfiles(All, Dir), + All = Wcf("xbin/*.x"), + All = Wcf("xbin/*"), + ["xbin"] = Wcf("*"), + All = Wcf("*/*"), + del(Files), + ok = file:del_dir(filename:join(Dir, "xbin")), + do_wildcard_7(Dir, Wcf). + +do_wildcard_7(Dir, Wcf) -> + Dirs = ["blurf","xa","yyy"], + SubDirs = ["blurf/nisse"], + foreach(fun(D) -> + ok = file:make_dir(filename:join(Dir, D)) + end, Dirs ++ SubDirs), + All = ["blurf/nisse/baz","xa/arne","xa/kalle","yyy/arne"], + Files = mkfiles(lists:reverse(All), Dir), + + %% Test. + Listing = Wcf("**"), + ["blurf","blurf/nisse","blurf/nisse/baz", + "xa","xa/arne","xa/kalle","yyy","yyy/arne"] = Listing, + Listing = Wcf("**/*"), + ["xa/arne","yyy/arne"] = Wcf("**/arne"), + ["blurf/nisse"] = Wcf("**/nisse"), + [] = Wcf("mountain/**"), + + %% Cleanup + del(Files), + foreach(fun(D) -> + ok = file:del_dir(filename:join(Dir, D)) + end, SubDirs ++ Dirs), + do_wildcard_8(Dir, Wcf). + +do_wildcard_8(Dir, Wcf) -> + Dirs0 = ["blurf"], + Dirs1 = ["blurf/nisse"], + Dirs2 = ["blurf/nisse/a", "blurf/nisse/b"], + foreach(fun(D) -> + ok = file:make_dir(filename:join(Dir, D)) + end, Dirs0 ++ Dirs1 ++ Dirs2), + All = ["blurf/nisse/a/1.txt", "blurf/nisse/b/2.txt", "blurf/nisse/b/3.txt"], + Files = mkfiles(lists:reverse(All), Dir), + %% Test. + All = Wcf("**/blurf/**/*.txt"), + + %% Cleanup + del(Files), + foreach(fun(D) -> + ok = file:del_dir(filename:join(Dir, D)) + end, Dirs2 ++ Dirs1 ++ Dirs0), + do_wildcard_9(Dir, Wcf). + +do_wildcard_9(Dir, Wcf) -> + Dirs0 = ["lib","lib/app","lib/app/ebin"], + Dirs = [filename:join(Dir, D) || D <- Dirs0], + [ok = file:make_dir(D) || D <- Dirs], + Files0 = [filename:join("lib/app/ebin", F++".bar") || + F <- ["abc","foo","foobar"]], + Files = [filename:join(Dir, F) || F <- Files0], + [ok = file:write_file(F, <<"some content\n">>) || F <- Files], + Files0 = Wcf("lib/app/ebin/*.bar"), + + %% Cleanup. + del(Files), + [ok = file:del_dir(D) || D <- lists:reverse(Dirs)], + ok. fold_files(Config) when is_list(Config) -> diff --git a/lib/stdlib/test/filename_SUITE.erl b/lib/stdlib/test/filename_SUITE.erl index 8817f5a55b..232df6a13f 100644 --- a/lib/stdlib/test/filename_SUITE.erl +++ b/lib/stdlib/test/filename_SUITE.erl @@ -112,19 +112,6 @@ absname(Config) when is_list(Config) -> ?line "/erlang/src" = filename:absname(["/erl",'a','ng',"/",'s',"rc"]), ?line "/erlang/src" = filename:absname("/erlang///src"), ?line "/file_sorter.erl" = filename:absname([file_sorter|'.erl']), - ok; - vxworks -> - Test_dir = ?config(priv_dir, Config), - Test1 = Test_dir ++ "/foo", - Test2 = Test_dir ++ "/ebin", - ?line ok = file:set_cwd(Test_dir), - ?line Test1 = filename:absname(foo), - ?line Test1= filename:absname("foo"), - ?line Test2 = filename:absname("foo/../ebin"), - ?line "/erlang" = filename:absname("/erlang"), - ?line "/erlang/src" = filename:absname("/erlang/src"), - ?line "/erlang/src" = filename:absname(["/erlan",'g/s',"rc"]), - ?line "/erlang/src" = filename:absname("/erlang///src"), ok end. @@ -179,15 +166,6 @@ absname_2(Config) when is_list(Config) -> ?line "/erlang" = filename:absname("/erlang", "/"), ?line "/erlang/src" = filename:absname("/erlang/src", "/"), ?line "/erlang/src" = filename:absname("/erlang///src", "/"), - ok; - vxworks -> - ?line "/usr/foo" = filename:absname(foo, "/usr"), - ?line "/usr/foo" = filename:absname("foo", "/usr"), - ?line "/usr/ebin" = filename:absname("../ebin", "/usr"), - ?line "/usr/ebin" = filename:absname("../ebin", "/usr/src"), - ?line "/erlang" = filename:absname("/erlang", "/usr"), - ?line "/erlang/src" = filename:absname("/erlang/src", "/usr"), - ?line "/erlang/src" = filename:absname("/erlang///src", "/usr"), ok end. @@ -213,11 +191,7 @@ basename_1(Config) when is_list(Config) -> ?line "foo" = filename:basename("A:foo"); {unix, _} -> ?line "strange\\but\\true" = - filename:basename("strange\\but\\true"); - vxworks -> - ?line "foo" = filename:basename(["usr\\foo\\"]), - ?line "foo" = filename:basename("elrond:usr\\foo\\"), - ?line "foo" = filename:basename("disk:/foo") + filename:basename("strange\\but\\true") end, ?line test_server:timetrap_cancel(Dog), ok. @@ -249,15 +223,7 @@ basename_2(Config) when is_list(Config) -> ?line "strange\\but\\true" = filename:basename("strange\\but\\true.erl", ".erl"), ?line "strange\\but\\true" = - filename:basename("strange\\but\\true", ".erl"); - vxworks -> - ?line "foo" = filename:basename("net:foo", ".erl"), - ?line "foo.erl" = filename:basename("net:\\usr\\foo.erl", - ".hrl"), - ?line "foo.erl" = - filename:basename("/disk0:\\usr.hrl\\foo.erl", - ".hrl"), - ?line "foo" = filename:basename("/home\\usr\\foo", ".hrl") + filename:basename("strange\\but\\true", ".erl") end, ?line test_server:timetrap_cancel(Dog), ok. @@ -267,37 +233,25 @@ basename_2(Config) when is_list(Config) -> dirname(Config) when is_list(Config) -> case os:type() of {win32,_} -> - ?line "A:/usr" = filename:dirname("A:/usr/foo.erl"), - ?line "A:usr" = filename:dirname("A:usr/foo.erl"), - ?line "/usr" = filename:dirname("\\usr\\foo.erl"), - ?line "/" = filename:dirname("\\usr"), - ?line "A:" = filename:dirname("A:"); - vxworks -> - ?line "net:/usr" = filename:dirname("net:/usr/foo.erl"), - ?line "/disk0:/usr" = filename:dirname("/disk0:/usr/foo.erl"), - ?line "/usr" = filename:dirname("\\usr\\foo.erl"), - ?line "/usr" = filename:dirname("\\usr"), - ?line "net:" = filename:dirname("net:"); + "A:/usr" = filename:dirname("A:/usr/foo.erl"), + "A:usr" = filename:dirname("A:usr/foo.erl"), + "/usr" = filename:dirname("\\usr\\foo.erl"), + "/" = filename:dirname("\\usr"), + "A:" = filename:dirname("A:"); _ -> true end, - ?line "usr" = filename:dirname("usr///foo.erl"), - ?line "." = filename:dirname("foo.erl"), - ?line "." = filename:dirname("."), - ?line "usr" = filename:dirname('usr/foo.erl'), - ?line "usr" = filename:dirname(['usr','/foo.erl']), - ?line "usr" = filename:dirname(['us','r/foo.erl']), - ?line "usr" = filename:dirname(['usr/','/foo.erl']), - ?line "usr" = filename:dirname(['usr/','foo.erl']), - ?line "usr" = filename:dirname(['usr/'|'foo.erl']), - ?line "usr" = filename:dirname(['usr/f','oo.erl']), - case os:type() of - vxworks -> - ?line "/" = filename:dirname("/"), - ?line "/usr" = filename:dirname("/usr"); - _ -> - ?line "/" = filename:dirname("/"), - ?line "/" = filename:dirname("/usr") - end, + "usr" = filename:dirname("usr///foo.erl"), + "." = filename:dirname("foo.erl"), + "." = filename:dirname("."), + "usr" = filename:dirname('usr/foo.erl'), + "usr" = filename:dirname(['usr','/foo.erl']), + "usr" = filename:dirname(['us','r/foo.erl']), + "usr" = filename:dirname(['usr/','/foo.erl']), + "usr" = filename:dirname(['usr/','foo.erl']), + "usr" = filename:dirname(['usr/'|'foo.erl']), + "usr" = filename:dirname(['usr/f','oo.erl']), + "/" = filename:dirname("/"), + "/" = filename:dirname("/usr"), ok. @@ -319,12 +273,6 @@ extension(Config) when is_list(Config) -> filename:extension("A:/usr.bar/foo.nisse.erl"), ?line "" = filename:extension("A:/usr.bar/foo"), ok; - vxworks -> - ?line "" = filename:extension("/disk0:\\usr\\foo"), - ?line ".erl" = - filename:extension("net:/usr.bar/foo.nisse.erl"), - ?line "" = filename:extension("net:/usr.bar/foo"), - ok; _ -> ok end. @@ -369,25 +317,6 @@ join(Config) when is_list(Config) -> filename:join(["A:","C:usr","foo.erl"]), ?line "d:/foo" = filename:join([$D, $:, $/, []], "foo"), ok; - vxworks -> - ?line "Net:" = filename:join(["Net:/"]), - ?line "net:" = filename:join(["net:\\"]), - ?line "net:/abc" = filename:join(["net:/", "abc"]), - ?line "net:/abc" = filename:join(["net:", "abc"]), - ?line "a/b/c/d/e/f/g" = - filename:join(["a//b\\c//\\/\\d/\\e/f\\g"]), - ?line "net:/usr/foo.erl" = - filename:join(["net:","usr","foo.erl"]), - ?line "/usr/foo.erl" = - filename:join(["net:","/usr","foo.erl"]), - ?line "/target:usr" = filename:join("net:","/target:usr"), - ?line "kernel:/usr" = filename:join("net:", "kernel:/usr"), - ?line "foo:/usr/foo.erl" = - filename:join(["A:","foo:/usr","foo.erl"]), - ?line "/disk0:usr/foo.erl" = - filename:join(["kalle:","/disk0:usr","foo.erl"]), - ?line "D:/foo" = filename:join([$D, $:, $/, []], "foo"), - ok; {unix, _} -> ok end. @@ -406,10 +335,6 @@ pathtype(Config) when is_list(Config) -> {unix, _} -> ?line absolute = filename:pathtype("/"), ?line absolute = filename:pathtype("/usr/local/bin"), - ok; - vxworks -> - ?line absolute = filename:pathtype("/usr/local/bin"), - ?line absolute = filename:pathtype("net:usr/local/bin"), ok end. @@ -424,12 +349,7 @@ rootname(Config) when is_list(Config) -> ok. split(Config) when is_list(Config) -> - case os:type() of - vxworks -> - ?line ["/usr","local","bin"] = filename:split("/usr/local/bin"); - _ -> - ?line ["/","usr","local","bin"] = filename:split("/usr/local/bin") - end, + ?line ["/","usr","local","bin"] = filename:split("/usr/local/bin"), ?line ["foo","bar"]= filename:split("foo/bar"), ?line ["foo", "bar", "hello"]= filename:split("foo////bar//hello"), ?line ["foo", "bar", "hello"]= filename:split(["foo//",'//bar//h',"ello"]), @@ -447,18 +367,6 @@ split(Config) when is_list(Config) -> ?line ["a:","msdev","include"] = filename:split("a:msdev\\include"), ok; - vxworks -> - ?line ["net:","msdev","include"] = - filename:split("net:/msdev/include"), - ?line ["Target:","msdev","include"] = - filename:split("Target:/msdev/include"), - ?line ["msdev","include"] = - filename:split("msdev\\include"), - ?line ["/disk0:","msdev","include"] = - filename:split("/disk0:\\msdev\\include"), - ?line ["a:","msdev","include"] = - filename:split("a:msdev\\include"), - ok; _ -> ok end. @@ -657,56 +565,38 @@ basename_bin_2(Config) when is_list(Config) -> dirname_bin(Config) when is_list(Config) -> case os:type() of {win32,_} -> - ?line <<"A:/usr">> = filename:dirname(<<"A:/usr/foo.erl">>), - ?line <<"A:usr">> = filename:dirname(<<"A:usr/foo.erl">>), - ?line <<"/usr">> = filename:dirname(<<"\\usr\\foo.erl">>), - ?line <<"/">> = filename:dirname(<<"\\usr">>), - ?line <<"A:">> = filename:dirname(<<"A:">>); - vxworks -> - ?line <<"net:/usr">> = filename:dirname(<<"net:/usr/foo.erl">>), - ?line <<"/disk0:/usr">> = filename:dirname(<<"/disk0:/usr/foo.erl">>), - ?line <<"/usr">> = filename:dirname(<<"\\usr\\foo.erl">>), - ?line <<"/usr">> = filename:dirname(<<"\\usr">>), - ?line <<"net:">> = filename:dirname(<<"net:">>); + <<"A:/usr">> = filename:dirname(<<"A:/usr/foo.erl">>), + <<"A:usr">> = filename:dirname(<<"A:usr/foo.erl">>), + <<"/usr">> = filename:dirname(<<"\\usr\\foo.erl">>), + <<"/">> = filename:dirname(<<"\\usr">>), + <<"A:">> = filename:dirname(<<"A:">>); _ -> true end, - ?line <<"usr">> = filename:dirname(<<"usr///foo.erl">>), - ?line <<".">> = filename:dirname(<<"foo.erl">>), - ?line <<".">> = filename:dirname(<<".">>), - case os:type() of - vxworks -> - ?line <<"/">> = filename:dirname(<<"/">>), - ?line <<"/usr">> = filename:dirname(<<"/usr">>); - _ -> - ?line <<"/">> = filename:dirname(<<"/">>), - ?line <<"/">> = filename:dirname(<<"/usr">>) - end, + <<"usr">> = filename:dirname(<<"usr///foo.erl">>), + <<".">> = filename:dirname(<<"foo.erl">>), + <<".">> = filename:dirname(<<".">>), + <<"/">> = filename:dirname(<<"/">>), + <<"/">> = filename:dirname(<<"/usr">>), ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% extension_bin(Config) when is_list(Config) -> - ?line <<".erl">> = filename:extension(<<"A:/usr/foo.erl">>), - ?line <<".erl">> = filename:extension(<<"A:/usr/foo.nisse.erl">>), - ?line <<".erl">> = filename:extension(<<"A:/usr.bar/foo.nisse.erl">>), - ?line <<"">> = filename:extension(<<"A:/usr.bar/foo">>), - ?line <<"">> = filename:extension(<<"A:/usr/foo">>), - ?line case os:type() of - {win32, _} -> - ?line <<"">> = filename:extension(<<"A:\\usr\\foo">>), - ?line <<".erl">> = - filename:extension(<<"A:/usr.bar/foo.nisse.erl">>), - ?line <<"">> = filename:extension(<<"A:/usr.bar/foo">>), - ok; - vxworks -> - ?line <<"">> = filename:extension(<<"/disk0:\\usr\\foo">>), - ?line <<".erl">> = - filename:extension(<<"net:/usr.bar/foo.nisse.erl">>), - ?line <<"">> = filename:extension(<<"net:/usr.bar/foo">>), - ok; - _ -> ok - end. + <<".erl">> = filename:extension(<<"A:/usr/foo.erl">>), + <<".erl">> = filename:extension(<<"A:/usr/foo.nisse.erl">>), + <<".erl">> = filename:extension(<<"A:/usr.bar/foo.nisse.erl">>), + <<"">> = filename:extension(<<"A:/usr.bar/foo">>), + <<"">> = filename:extension(<<"A:/usr/foo">>), + case os:type() of + {win32, _} -> + ?line <<"">> = filename:extension(<<"A:\\usr\\foo">>), + ?line <<".erl">> = + filename:extension(<<"A:/usr.bar/foo.nisse.erl">>), + ?line <<"">> = filename:extension(<<"A:/usr.bar/foo">>), + ok; + _ -> ok + end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -754,50 +644,45 @@ join_bin(Config) when is_list(Config) -> end. pathtype_bin(Config) when is_list(Config) -> - ?line relative = filename:pathtype(<<"..">>), - ?line relative = filename:pathtype(<<"foo">>), - ?line relative = filename:pathtype(<<"foo/bar">>), - ?line relative = filename:pathtype('foo/bar'), + relative = filename:pathtype(<<"..">>), + relative = filename:pathtype(<<"foo">>), + relative = filename:pathtype(<<"foo/bar">>), + relative = filename:pathtype('foo/bar'), case os:type() of {win32, _} -> - ?line volumerelative = filename:pathtype(<<"/usr/local/bin">>), - ?line volumerelative = filename:pathtype(<<"A:usr/local/bin">>), + volumerelative = filename:pathtype(<<"/usr/local/bin">>), + volumerelative = filename:pathtype(<<"A:usr/local/bin">>), ok; {unix, _} -> - ?line absolute = filename:pathtype(<<"/">>), - ?line absolute = filename:pathtype(<<"/usr/local/bin">>), + absolute = filename:pathtype(<<"/">>), + absolute = filename:pathtype(<<"/usr/local/bin">>), ok end. rootname_bin(Config) when is_list(Config) -> - ?line <<"/jam.src/kalle">> = filename:rootname(<<"/jam.src/kalle">>), - ?line <<"/jam.src/foo">> = filename:rootname(<<"/jam.src/foo.erl">>), - ?line <<"/jam.src/foo">> = filename:rootname(<<"/jam.src/foo.erl">>, <<".erl">>), - ?line <<"/jam.src/foo.jam">> = filename:rootname(<<"/jam.src/foo.jam">>, <<".erl">>), - ?line <<"/jam.src/foo.jam">> = filename:rootname(["/jam.sr",'c/foo.j',"am"],<<".erl">>), - ?line <<"/jam.src/foo.jam">> = filename:rootname(["/jam.sr",'c/foo.j'|am],<<".erl">>), + <<"/jam.src/kalle">> = filename:rootname(<<"/jam.src/kalle">>), + <<"/jam.src/foo">> = filename:rootname(<<"/jam.src/foo.erl">>), + <<"/jam.src/foo">> = filename:rootname(<<"/jam.src/foo.erl">>, <<".erl">>), + <<"/jam.src/foo.jam">> = filename:rootname(<<"/jam.src/foo.jam">>, <<".erl">>), + <<"/jam.src/foo.jam">> = filename:rootname(["/jam.sr",'c/foo.j',"am"],<<".erl">>), + <<"/jam.src/foo.jam">> = filename:rootname(["/jam.sr",'c/foo.j'|am],<<".erl">>), ok. split_bin(Config) when is_list(Config) -> - case os:type() of - vxworks -> - ?line [<<"/usr">>,<<"local">>,<<"bin">>] = filename:split(<<"/usr/local/bin">>); - _ -> - ?line [<<"/">>,<<"usr">>,<<"local">>,<<"bin">>] = filename:split(<<"/usr/local/bin">>) - end, - ?line [<<"foo">>,<<"bar">>]= filename:split(<<"foo/bar">>), - ?line [<<"foo">>, <<"bar">>, <<"hello">>]= filename:split(<<"foo////bar//hello">>), + [<<"/">>,<<"usr">>,<<"local">>,<<"bin">>] = filename:split(<<"/usr/local/bin">>), + [<<"foo">>,<<"bar">>]= filename:split(<<"foo/bar">>), + [<<"foo">>, <<"bar">>, <<"hello">>]= filename:split(<<"foo////bar//hello">>), case os:type() of {win32,_} -> - ?line [<<"a:/">>,<<"msdev">>,<<"include">>] = + [<<"a:/">>,<<"msdev">>,<<"include">>] = filename:split(<<"a:/msdev/include">>), - ?line [<<"a:/">>,<<"msdev">>,<<"include">>] = + [<<"a:/">>,<<"msdev">>,<<"include">>] = filename:split(<<"A:/msdev/include">>), - ?line [<<"msdev">>,<<"include">>] = + [<<"msdev">>,<<"include">>] = filename:split(<<"msdev\\include">>), - ?line [<<"a:/">>,<<"msdev">>,<<"include">>] = + [<<"a:/">>,<<"msdev">>,<<"include">>] = filename:split(<<"a:\\msdev\\include">>), - ?line [<<"a:">>,<<"msdev">>,<<"include">>] = + [<<"a:">>,<<"msdev">>,<<"include">>] = filename:split(<<"a:msdev\\include">>), ok; _ -> @@ -814,4 +699,3 @@ t_nativename_bin(Config) when is_list(Config) -> ?line <<"/usr/tmp/arne">> = filename:nativename(<<"/usr/tmp//arne/">>) end. - diff --git a/lib/stdlib/test/gen_fsm_SUITE.erl b/lib/stdlib/test/gen_fsm_SUITE.erl index bdb4ea65b5..a637a8543b 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-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2013. 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 @@ -281,21 +281,12 @@ start12(Config) when is_list(Config) -> %% Check that time outs in calls work abnormal1(suite) -> []; abnormal1(Config) when is_list(Config) -> - ?line {ok, _Pid} = - gen_fsm:start({local, my_fsm}, gen_fsm_SUITE, [], []), + {ok, _Pid} = gen_fsm:start({local, my_fsm}, gen_fsm_SUITE, [], []), %% timeout call. - case os:type() of - vxworks -> - %% timeout call for VxWorks must be in 16ms increments. - ?line delayed = gen_fsm:sync_send_event(my_fsm, {delayed_answer,1}, 17), - ?line {'EXIT',{timeout,_}} = - (catch gen_fsm:sync_send_event(my_fsm, {delayed_answer,17}, 1)); - _ -> - ?line delayed = gen_fsm:sync_send_event(my_fsm, {delayed_answer,1}, 100), - ?line {'EXIT',{timeout,_}} = - (catch gen_fsm:sync_send_event(my_fsm, {delayed_answer,10}, 1)) - end, + delayed = gen_fsm:sync_send_event(my_fsm, {delayed_answer,1}, 100), + {'EXIT',{timeout,_}} = + (catch gen_fsm:sync_send_event(my_fsm, {delayed_answer,10}, 1)), test_server:messages_get(), ok. diff --git a/lib/stdlib/test/gen_server_SUITE.erl b/lib/stdlib/test/gen_server_SUITE.erl index c930d90e1c..dffeadb423 100644 --- a/lib/stdlib/test/gen_server_SUITE.erl +++ b/lib/stdlib/test/gen_server_SUITE.erl @@ -231,14 +231,6 @@ start(Config) when is_list(Config) -> end, test_server:messages_get(), - %% Must wait for all error messages before going to next test. - %% (otherwise it interferes too much with real time characteristics). - case os:type() of - vxworks -> - receive after 5000 -> ok end; - _ -> - ok - end, process_flag(trap_exit, OldFl), ok. @@ -1054,8 +1046,9 @@ call_with_huge_message_queue(Config) when is_list(Config) -> 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 -> + Q when Q < 10; IsCover -> ok; Q -> io:format("Q = ~p", [Q]), diff --git a/lib/stdlib/test/id_transform_SUITE.erl b/lib/stdlib/test/id_transform_SUITE.erl index e1972a100e..ee97ffe7b3 100644 --- a/lib/stdlib/test/id_transform_SUITE.erl +++ b/lib/stdlib/test/id_transform_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2011. All Rights Reserved. +%% Copyright Ericsson AB 2003-2012. 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 @@ -26,7 +26,7 @@ id_transform/1]). -export([check/2,check2/1,g/0,f/1,t/1,t1/1,t2/1,t3/1,t4/1, - t5/1,t6/1,apa/1,new_fun/0]). + t5/1,apa/1,new_fun/0]). % Serves as test... -hej(hopp). @@ -61,7 +61,7 @@ id_transform(Config) when is_list(Config) -> ?line {module,erl_id_trans}=code:load_binary(erl_id_trans,File,Bin), ?line case test_server:purify_is_running() of false -> - Dog = ?t:timetrap(?t:hours(1)), + Dog = ct:timetrap(?t:hours(1)), ?line Res = run_in_test_suite(), ?t:timetrap_cancel(Dog), Res; @@ -388,8 +388,6 @@ t3(A) when is_tuple(A) or is_tuple(A) -> is_tuple; t3(A) when record(A, apa) -> foo; -t3(A) when {erlang,is_record}(A, apa) -> - foo; t3(A) when erlang:is_record(A, apa) -> foo; t3(A) when is_record(A, apa) -> @@ -397,13 +395,10 @@ t3(A) when is_record(A, apa) -> t3(A) when record({apa}, apa) -> {A,foo}. -t4(_) when {erlang,is_record}({apa}, apa) -> - foo. - -t5(A) when erlang:is_record({apa}, apa) -> +t4(A) when erlang:is_record({apa}, apa) -> {A,foo}. -t6(A) when is_record({apa}, apa) -> +t5(A) when is_record({apa}, apa) -> {A,foo}. -record(apa2,{a=a,b=foo:bar()}). diff --git a/lib/stdlib/test/io_SUITE.erl b/lib/stdlib/test/io_SUITE.erl index bb02a879c2..9f828c6d2d 100644 --- a/lib/stdlib/test/io_SUITE.erl +++ b/lib/stdlib/test/io_SUITE.erl @@ -1,7 +1,8 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2011. All Rights Reserved. +%% Copyright Ericsson AB 1999-2013. 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 @@ -27,7 +28,12 @@ otp_6282/1, otp_6354/1, otp_6495/1, otp_6517/1, otp_6502/1, manpage/1, otp_6708/1, otp_7084/1, otp_7421/1, io_lib_collect_line_3_wb/1, cr_whitespace_in_string/1, - io_fread_newlines/1, otp_8989/1, io_lib_fread_literal/1]). + 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]). + +-export([pretty/2]). %-define(debug, true). @@ -62,7 +68,9 @@ all() -> otp_6282, otp_6354, otp_6495, otp_6517, otp_6502, manpage, otp_6708, otp_7084, otp_7421, io_lib_collect_line_3_wb, cr_whitespace_in_string, - io_fread_newlines, otp_8989, io_lib_fread_literal]. + io_fread_newlines, otp_8989, io_lib_fread_literal, + printable_range, + io_lib_print_binary_depth_one, otp_10302, otp_10755, otp_10836]. groups() -> []. @@ -892,7 +900,7 @@ otp_6354(Config) when is_list(Config) -> ?line "\"\\b\\t\\n\\v\\f\\r\\e\250\"" = p([8,9,10,11,12,13,27,168], 1, 40, -1), % ?line "\"\\b\\t\\n\"\n \"\\v\\f\\r\"\n \"\\e\250\"" = - ?line "\"\\b\\t\\n\\v\\f\\r\\e�\"" = + ?line "\"\\b\\t\\n\\v\\f\\r\\e¨\"" = p([8,9,10,11,12,13,27,168], 1, 10, -1), ?line "\"\\b\\t\\n\\v\\f\\r\\e\250\"" = p([8,9,10,11,12,13,27,168], 1, 40, 100), @@ -2021,3 +2029,188 @@ io_lib_fread_literal(Suite) when is_list(Suite) -> ?line {done,{error,{fread,input}},_} = io_lib:fread(C2, eof, " d"), ?line {done,{ok,[]},[]} = io_lib:fread(C2, "d\n", " d"), ok. + + +printable_range(doc) -> + "Check that the printable range set by the user actually works"; +printable_range(Suite) when is_list(Suite) -> + Pa = filename:dirname(code:which(?MODULE)), + {ok, UNode} = test_server:start_node(printable_range_unicode, slave, + [{args, " +pc unicode -pa " ++ Pa}]), + {ok, LNode} = test_server:start_node(printable_range_latin1, slave, + [{args, " +pc latin1 -pa " ++ Pa}]), + {ok, DNode} = test_server:start_node(printable_range_default, slave, + [{args, " -pa " ++ Pa}]), + unicode = rpc:call(UNode,io,printable_range,[]), + latin1 = rpc:call(LNode,io,printable_range,[]), + latin1 = rpc:call(DNode,io,printable_range,[]), + test_server:stop_node(UNode), + test_server:stop_node(LNode), + {ok, UNode} = test_server:start_node(printable_range_unicode, slave, + [{args, " +pcunicode -pa " ++ Pa}]), + {ok, LNode} = test_server:start_node(printable_range_latin1, slave, + [{args, " +pclatin1 -pa " ++ Pa}]), + unicode = rpc:call(UNode,io,printable_range,[]), + latin1 = rpc:call(LNode,io,printable_range,[]), + {error, _} = test_server:start_node(printable_range_unnicode, slave, + [{args, " +pcunnicode -pa " ++ Pa}]), + PrettyOptions = [{column,1}, + {line_length,109}, + {depth,30}, + {max_chars,60}, + {record_print_fun, + fun(_,_) -> no end}, + {encoding,unicode}], + 1025 = lists:max(lists:flatten(rpc:call(UNode,io_lib_pretty,print, + [{hello, [1024,1025]}, + PrettyOptions]))), + 125 = lists:max(lists:flatten(rpc:call(LNode,io_lib_pretty,print, + [{hello, [1024,1025]}, + PrettyOptions]))), + 125 = lists:max(lists:flatten(rpc:call(DNode,io_lib_pretty,print, + [{hello, [1024,1025]}, + PrettyOptions]))), + 1025 = lists:max(lists:flatten(rpc:call(UNode,io_lib_pretty,print, + [{hello, <<1024/utf8,1025/utf8>>}, + PrettyOptions]))), + 125 = lists:max(lists:flatten(rpc:call(LNode,io_lib_pretty,print, + [{hello, <<1024/utf8,1025/utf8>>}, + PrettyOptions]))), + 125 = lists:max(lists:flatten(rpc:call(DNode,io_lib_pretty,print, + [{hello, <<1024/utf8,1025/utf8>>}, + PrettyOptions]))), + + 1025 = lists:max(lists:flatten(rpc:call(UNode,io_lib,format, + ["~tp",[{hello, [1024,1025]}]]))), + 125 = lists:max(lists:flatten(rpc:call(LNode,io_lib,format, + ["~tp",[{hello, [1024,1025]}]]))), + 125 = lists:max(lists:flatten(rpc:call(DNode,io_lib,format, + ["~tp",[{hello, [1024,1025]}]]))), + 1025 = lists:max(lists:flatten(rpc:call(UNode,io_lib,format, + ["~tp", + [{hello, + <<1024/utf8,1025/utf8>>}]]))), + 125 = lists:max(lists:flatten(rpc:call(LNode,io_lib,format, + ["~tp", + [{hello, + <<1024/utf8,1025/utf8>>}]]))), + 125 = lists:max(lists:flatten(rpc:call(DNode,io_lib,format, + ["~tp", + [{hello, + <<1024/utf8,1025/utf8>>}]]))), + test_server:stop_node(UNode), + test_server:stop_node(LNode), + test_server:stop_node(DNode), + ok. + +io_lib_print_binary_depth_one(doc) -> + "Test binaries printed with a depth of one behave correctly"; +io_lib_print_binary_depth_one(Suite) when is_list(Suite) -> + ?line "<<>>" = fmt("~W", [<<>>, 1]), + ?line "<<>>" = fmt("~P", [<<>>, 1]), + ?line "<<...>>" = fmt("~W", [<<1>>, 1]), + ?line "<<...>>" = fmt("~P", [<<1>>, 1]), + ?line "<<...>>" = fmt("~W", [<<1:7>>, 1]), + ?line "<<...>>" = fmt("~P", [<<1:7>>, 1]), + ok. + +otp_10302(doc) -> + "OTP-10302. Unicode"; +otp_10302(Suite) when is_list(Suite) -> + Pa = filename:dirname(code:which(?MODULE)), + {ok, UNode} = test_server:start_node(printable_range_unicode, slave, + [{args, " +pc unicode -pa " ++ Pa}]), + {ok, LNode} = test_server:start_node(printable_range_latin1, slave, + [{args, " +pc latin1 -pa " ++ Pa}]), + "\"\x{400}\"" = rpc:call(UNode,?MODULE,pretty,["\x{400}", -1]), + "<<\"\x{400}\"/utf8>>" = rpc:call(UNode,?MODULE,pretty, + [<<"\x{400}"/utf8>>, -1]), + + "<<\"\x{400}foo\"/utf8>>" = rpc:call(UNode,?MODULE,pretty, + [<<"\x{400}foo"/utf8>>, 2]), + "[1024]" = rpc:call(LNode,?MODULE,pretty,["\x{400}", -1]), + "<<208,128>>" = rpc:call(LNode,?MODULE,pretty,[<<"\x{400}"/utf8>>, -1]), + + "<<208,...>>" = rpc:call(LNode,?MODULE,pretty,[<<"\x{400}foo"/utf8>>, 2]), + test_server:stop_node(UNode), + test_server:stop_node(LNode), + + "<<\"äppl\"/utf8>>" = pretty(<<"äppl"/utf8>>, 2), + "<<\"äppl\"/utf8...>>" = pretty(<<"äpple"/utf8>>, 2), + "<<\"apel\">>" = pretty(<<"apel">>, 2), + "<<\"apel\"...>>" = pretty(<<"apelsin">>, 2), + "<<\"äppl\">>" = fmt("~tp", [<<"äppl">>]), + "<<\"äppl\"...>>" = fmt("~tP", [<<"äpple">>, 2]), + "<<0,0,0,0,0,0,1,0>>" = fmt("~p", [<<256:64/unsigned-integer>>]), + "<<0,0,0,0,0,0,1,0>>" = fmt("~tp", [<<256:64/unsigned-integer>>]), + + Chars = lists:seq(0, 512), % just a few... + [] = [C || C <- Chars, S <- io_lib:write_char_as_latin1(C), + not is_latin1(S)], + L1 = [S || C <- Chars, S <- io_lib:write_char(C), + not is_latin1(S)], + L1 = lists:seq(256, 512), + + [] = [C || C <- Chars, S <- io_lib:write_string_as_latin1([C]), + not is_latin1(S)], + L2 = [S || C <- Chars, S <- io_lib:write_string([C]), + not is_latin1(S)], + L2 = lists:seq(256, 512), + + ok. + +pretty(Term, Depth) when is_integer(Depth) -> + Opts = [{column, 1}, {line_length, 20}, + {depth, Depth}, {max_chars, 60}, + {encoding, unicode}], + pretty(Term, Opts); +pretty(Term, Opts) when is_list(Opts) -> + R = io_lib_pretty:print(Term, Opts), + lists:flatten(io_lib:format("~ts", [R])). + +is_latin1(S) -> + S >= 0 andalso S =< 255. + +otp_10836(doc) -> + "OTP-10836. ~ts extended to latin1"; +otp_10836(Suite) when is_list(Suite) -> + S = io_lib:format("~ts", [[<<"äpple"/utf8>>, <<"äpple">>]]), + "äppleäpple" = lists:flatten(S), + ok. + +otp_10755(doc) -> + "OTP-10755. The 'l' modifier"; +otp_10755(Suite) when is_list(Suite) -> + S = "string", + "\"string\"" = fmt("~p", [S]), + "[115,116,114,105,110,103]" = fmt("~lp", [S]), + "\"string\"" = fmt("~P", [S, 2]), + "[115|...]" = fmt("~lP", [S, 2]), + {'EXIT',{badarg,_}} = (catch fmt("~ltp", [S])), + {'EXIT',{badarg,_}} = (catch fmt("~tlp", [S])), + {'EXIT',{badarg,_}} = (catch fmt("~ltP", [S])), + {'EXIT',{badarg,_}} = (catch fmt("~tlP", [S])), + Text = + "-module(l_mod).\n" + "-export([t/0]).\n" + "t() ->\n" + " S = \"string\",\n" + " io:format(\"~ltp\", [S]),\n" + " io:format(\"~tlp\", [S]),\n" + " io:format(\"~ltP\", [S, 1]),\n" + " io:format(\"~tlP\", [S, 1]).\n", + {ok,l_mod,[{_File,Ws}]} = compile_file("l_mod.erl", Text, Suite), + ["format string invalid (invalid control ~lt)", + "format string invalid (invalid control ~tl)", + "format string invalid (invalid control ~lt)", + "format string invalid (invalid control ~tl)"] = + [lists:flatten(M:format_error(E)) || {_L,M,E} <- Ws], + ok. + +compile_file(File, Text, Config) -> + PrivDir = ?privdir(Config), + Fname = filename:join(PrivDir, File), + ok = file:write_file(Fname, Text), + try compile:file(Fname, [return]) + after ok %file:delete(Fname) + end. diff --git a/lib/stdlib/test/io_proto_SUITE.erl b/lib/stdlib/test/io_proto_SUITE.erl index 17e69f7c1c..4ca8680fc9 100644 --- a/lib/stdlib/test/io_proto_SUITE.erl +++ b/lib/stdlib/test/io_proto_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2012. All Rights Reserved. +%% Copyright Ericsson AB 2009-2013. 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 @@ -716,7 +716,7 @@ binary_options(Config) when is_list(Config) -> {getline, "<<\"hej\\n\">>"}, {putline, "io:get_line('')."}, {putline, binary_to_list(<<"\345\344\366"/utf8>>)}, - {getline, "<<\""++binary_to_list(unicode:characters_to_binary(<<"\345\344\366"/utf8>>,latin1,utf8))++"\\n\">>"} + {getline, "<<\""++binary_to_list(<<"\345\344\366"/utf8>>)++"\\n\"/utf8>>"} ],[]) end, %% And one with oldshell @@ -736,7 +736,7 @@ binary_options(Config) when is_list(Config) -> {getline_re, ".*<<\"hej\\\\n\">>"}, {putline, "io:get_line('')."}, {putline, binary_to_list(<<"\345\344\366"/utf8>>)}, - {getline_re, ".*<<\""++binary_to_list(unicode:characters_to_binary(<<"\345\344\366"/utf8>>,latin1,utf8))++"\\\\n\">>"} + {getline_re, ".*<<\""++binary_to_list(<<"\345\344\366"/utf8>>)++"\\\\n\"/utf8>>"} ],[],[],"-oldshell"), ok. @@ -1784,8 +1784,8 @@ get_default_shell() -> {putline, "whereis(user_drv)."}, {getline, "undefined"}],[]), old - catch E:R -> - ?dbg({E,R}), + catch _E:_R -> + ?dbg({_E,_R}), new end. diff --git a/lib/stdlib/test/proc_lib_SUITE.erl b/lib/stdlib/test/proc_lib_SUITE.erl index c95089117c..8dca69bac4 100644 --- a/lib/stdlib/test/proc_lib_SUITE.erl +++ b/lib/stdlib/test/proc_lib_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 @@ -28,7 +28,7 @@ crash/1, sync_start_nolink/1, sync_start_link/1, spawn_opt/1, sp1/0, sp2/0, sp3/1, sp4/2, sp5/1, hibernate/1]). --export([ otp_6345/1]). +-export([ otp_6345/1, init_dont_hang/1]). -export([hib_loop/1, awaken/1]). @@ -36,7 +36,7 @@ handle_event/2, handle_call/2, handle_info/2, terminate/2]). --export([otp_6345_init/1]). +-export([otp_6345_init/1, init_dont_hang_init/1]). -ifdef(STANDALONE). @@ -52,7 +52,7 @@ all() -> {group, tickets}]. groups() -> - [{tickets, [], [otp_6345]}, + [{tickets, [], [otp_6345, init_dont_hang]}, {sync_start, [], [sync_start_nolink, sync_start_link]}]. init_per_suite(Config) -> @@ -343,6 +343,29 @@ otp_6345_loop() -> otp_6345_loop() end. +%% OTP-9803 +init_dont_hang(suite) -> + []; +init_dont_hang(doc) -> + ["Check that proc_lib:start don't hang if spawned process crashes before proc_lib:init_ack/2"]; +init_dont_hang(Config) when is_list(Config) -> + %% Start should behave as start_link + process_flag(trap_exit, true), + StartLinkRes = proc_lib:start_link(?MODULE, init_dont_hang_init, [self()]), + try + StartLinkRes = proc_lib:start(?MODULE, init_dont_hang_init, [self()], 1000), + StartLinkRes = proc_lib:start(?MODULE, init_dont_hang_init, [self()], 1000, []), + ok + catch _:Error -> + io:format("Error ~p /= ~p ~n",[erlang:get_stacktrace(), StartLinkRes]), + exit(Error) + end. + +init_dont_hang_init(Parent) -> + 1 = 2. + + + %%----------------------------------------------------------------- %% The error_logger handler used. %%----------------------------------------------------------------- diff --git a/lib/stdlib/test/qlc_SUITE.erl b/lib/stdlib/test/qlc_SUITE.erl index 192268f90e..a9ea78a58b 100644 --- a/lib/stdlib/test/qlc_SUITE.erl +++ b/lib/stdlib/test/qlc_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2012. All Rights Reserved. +%% Copyright Ericsson AB 2004-2013. 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 @@ -2969,15 +2969,6 @@ lookup1(Config) when is_list(Config) -> [3] = lookup_keys(Q) end, [{1,a},{3,3}])">>, - {cres, - <<"A = 3, - etsc(fun(E) -> - Q = qlc:q([X || X <- ets:table(E), A =:= {erlang,element}(1, X)]), - [{3,3}] = qlc:e(Q), - [3] = lookup_keys(Q) - end, [{1,a},{3,3}])">>, - {warnings,[{3,erl_lint,deprecated_tuple_fun}]}}, - <<"etsc(fun(E) -> A = 3, Q = qlc:q([X || X <- ets:table(E), @@ -3442,14 +3433,6 @@ lookup2(Config) when is_list(Config) -> [r] = lookup_keys(Q) end, [{keypos,1}], [#r{}])">>, {cres, - <<"etsc(fun(E) -> - Q = qlc:q([element(1, X) || X <- ets:table(E), - {erlang,is_record}(X, r, 2)]), - [r] = qlc:e(Q), - [r] = lookup_keys(Q) - end, [{keypos,1}], [#r{}])">>, - {warnings,[{4,erl_lint,deprecated_tuple_fun}]}}, - {cres, <<"etsc(fun(E) -> Q = qlc:q([element(1, X) || X <- ets:table(E), record(X, r)]), @@ -3468,15 +3451,7 @@ lookup2(Config) when is_list(Config) -> is_record(X, r)]), [r] = qlc:e(Q), [r] = lookup_keys(Q) - end, [{keypos,1}], [#r{}])">>, - {cres, - <<"etsc(fun(E) -> - Q = qlc:q([element(1, X) || X <- ets:table(E), - {erlang,is_record}(X, r)]), - [r] = qlc:e(Q), - [r] = lookup_keys(Q) - end, [{keypos,1}], [#r{}])">>, - {warnings,[{4,erl_lint,deprecated_tuple_fun}]}} + end, [{keypos,1}], [#r{}])">> ], ?line run(Config, <<"-record(r, {a}).\n">>, TsR), @@ -6087,21 +6062,6 @@ otp_6673(Config) when is_list(Config) -> ], ?line run(Config, Ts_RT), - %% Ulf Wiger provided a patch that makes QLC work with packages: - Dir = filename:join(?privdir, "p"), - ?line ok = filelib:ensure_dir(filename:join(Dir, ".")), - File = filename:join(Dir, "p.erl"), - ?line ok = file:write_file(File, - <<"-module(p.p).\n" - "-export([q/0]).\n" - "-include_lib(\"stdlib/include/qlc.hrl\").\n" - "q() ->\n" - " .qlc:q([X || X <- [1,2]]).">>), - ?line {ok, 'p.p'} = compile:file(File, [{outdir,Dir}]), - ?line code:purge('p.p'), - ?line {module, 'p.p'} = code:load_abs(filename:rootname(File), 'p.p'), - ?line [1,2] = qlc:e(p.p:q()), - ok. otp_6964(doc) -> @@ -6703,7 +6663,7 @@ otp_7714(Config) when is_list(Config) -> {A,I1} <- ets:table(E1), {B,I2} <- ets:table(E2), I1 =:= I2],{join,merge}), - [{a,1},{a,2},{a,3}] = qlc:e(Q), + [{a,1},{a,2},{a,3}] = lists:sort(qlc:e(Q)), ets:delete(E1), ets:delete(E2)">>], ?line run(Config, Ts). @@ -6768,7 +6728,7 @@ otp_6674(Config) when is_list(Config) -> [{join,lookup}]}}], []} = qlc:info(Q, {format,debug}), {0,1,0,0} = join_info(Q), - [{1.0,1},{2,2}] = qlc:e(Q), + [{1.0,1},{2,2}] = lists:sort(qlc:e(Q)), ets:delete(E1), ets:delete(E2)">>, <<"E1 = ets:new(join, [ordered_set]), diff --git a/lib/stdlib/test/re_SUITE.erl b/lib/stdlib/test/re_SUITE.erl index a542745e67..500f5fadb9 100644 --- a/lib/stdlib/test/re_SUITE.erl +++ b/lib/stdlib/test/re_SUITE.erl @@ -1,3 +1,4 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% @@ -291,10 +292,10 @@ global_capture(Config) when is_list(Config) -> ?line match = re:run("ABCabcdABCabcdA","a(?<FOO>bcd)",[global,{capture,none,index}]), ?line match = re:run("ABCabcdABCabcdA","a(?<FOO>bcd)",[global,{capture,none,binary}]), ?line match = re:run("ABCabcdABCabcdA","a(?<FOO>bcd)",[global,{capture,none,list}]), - ?line {match,[[<<195,133,98,99,100>>,<<"bcd">>],[<<"abcd">>,<<"bcd">>]]} = re:run("ABC�bcdABCabcdA",".(?<FOO>bcd)",[global,{capture,all,binary},unicode]), - ?line {match,[["�bcd","bcd"],["abcd","bcd"]]} = re:run(<<"ABC",8#303,8#205,"bcdABCabcdA">>,".(?<FOO>bcd)",[global,{capture,all,list},unicode]), - ?line {match,[["�bcd","bcd"],["abcd","bcd"]]} = re:run("ABC�bcdABCabcdA",".(?<FOO>bcd)",[global,{capture,all,list},unicode]), - ?line {match,[[{3,5},{5,3}],[{11,4},{12,3}]]} = re:run("ABC�bcdABCabcdA",".(?<FOO>bcd)",[global,{capture,all,index},unicode]), + ?line {match,[[<<195,133,98,99,100>>,<<"bcd">>],[<<"abcd">>,<<"bcd">>]]} = re:run("ABCÅbcdABCabcdA",".(?<FOO>bcd)",[global,{capture,all,binary},unicode]), + ?line {match,[["Åbcd","bcd"],["abcd","bcd"]]} = re:run(<<"ABC",8#303,8#205,"bcdABCabcdA">>,".(?<FOO>bcd)",[global,{capture,all,list},unicode]), + ?line {match,[["Åbcd","bcd"],["abcd","bcd"]]} = re:run("ABCÅbcdABCabcdA",".(?<FOO>bcd)",[global,{capture,all,list},unicode]), + ?line {match,[[{3,5},{5,3}],[{11,4},{12,3}]]} = re:run("ABCÅbcdABCabcdA",".(?<FOO>bcd)",[global,{capture,all,index},unicode]), ?t:timetrap_cancel(Dog), ok. @@ -314,20 +315,26 @@ replace_return(Config) when is_list(Config) -> Dog = ?t:timetrap(?t:minutes(3)), ?line {'EXIT',{badarg,_}} = (catch re:replace("na","(a","")), ?line <<"nasse">> = re:replace(<<"nisse">>,"i","a",[{return,binary}]), - ?line <<"ABC�XABCXA">> = re:replace("ABC\305abcdABCabcdA","a(?<FOO>bcd)","X",[global,{return,binary}]), + ?line <<"ABCÅXABCXA">> = re:replace("ABC\305abcdABCabcdA","a(?<FOO>bcd)","X",[global,{return,binary}]), - ?line [<<"ABC�">>, + ?line [<<"ABCÅ">>, <<"X">>, <<"ABC">>, <<"X">> | <<"A">> ] = - re:replace("ABC�abcdABCabcdA","a(?<FOO>bcd)","X",[global,{return,iodata}]), - ?line "ABC�XABCXA" = re:replace("ABC�abcdABCabcdA","a(?<FOO>bcd)","X",[global,{return,list},unicode]), - ?line <<65,66,67,195,133,88,65,66,67,88,65>> = re:replace("ABC�abcdABCabcdA","a(?<FOO>bcd)","X",[global,{return,binary},unicode]), - ?line <<65,66,67,195,133,88,65,66,67,97,98,99,100,65>> = re:replace("ABC�abcdABCabcdA","a(?<FOO>bcd)","X",[{return,binary},unicode]), + re:replace("ABCÅabcdABCabcdA","a(?<FOO>bcd)","X",[global,{return,iodata}]), + ?line "ABCÅXABCXA" = re:replace("ABCÅabcdABCabcdA","a(?<FOO>bcd)","X",[global,{return,list},unicode]), + ?line <<65,66,67,195,133,88,65,66,67,88,65>> = re:replace("ABCÅabcdABCabcdA","a(?<FOO>bcd)","X",[global,{return,binary},unicode]), + ?line <<65,66,67,195,133,88,65,66,67,97,98,99,100,65>> = re:replace("ABCÅabcdABCabcdA","a(?<FOO>bcd)","X",[{return,binary},unicode]), ?line <<"iXk">> = re:replace("abcdefghijk","(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)","\\9X",[{return,binary}]), ?line <<"jXk">> = re:replace("abcdefghijk","(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)","\\10X",[{return,binary}]), ?line <<"Xk">> = re:replace("abcdefghijk","(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)","\\11X",[{return,binary}]), + ?line <<"9X1">> = re:replace("12345678901","(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)","\\g9X",[{return,binary}]), + ?line <<"0X1">> = re:replace("12345678901","(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)","\\g10X",[{return,binary}]), + ?line <<"X1">> = re:replace("12345678901","(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)","\\g11X",[{return,binary}]), + ?line <<"971">> = re:replace("12345678901","(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)","\\g{9}7",[{return,binary}]), + ?line <<"071">> = re:replace("12345678901","(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)","\\g{10}7",[{return,binary}]), + ?line <<"71">> = re:replace("12345678901","(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)","\\g{11}7",[{return,binary}]), ?line "a\x{400}bcX" = re:replace("a\x{400}bcd","d","X",[global,{return,list},unicode]), ?line <<"a",208,128,"bcX">> = re:replace("a\x{400}bcd","d","X",[global,{return,binary},unicode]), ?line "a\x{400}bcd" = re:replace("a\x{400}bcd","Z","X",[global,{return,list},unicode]), diff --git a/lib/stdlib/test/re_testoutput1_replacement_test.erl b/lib/stdlib/test/re_testoutput1_replacement_test.erl index 69cb140e0d..8f8d8762ad 100644 --- a/lib/stdlib/test/re_testoutput1_replacement_test.erl +++ b/lib/stdlib/test/re_testoutput1_replacement_test.erl @@ -1,7 +1,8 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2010. All Rights Reserved. +%% Copyright Ericsson AB 2008-2012. 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 @@ -274,10 +275,10 @@ run() -> ?line <<"dthing">> = iolist_to_binary(re:replace("dthing","^[^]cde]","y\\1I&MoqRPG&GQa\\1l",[global])), ?line <<"ething">> = iolist_to_binary(re:replace("ething","^[^]cde]","AsxwUn\\1GqkWNdgRJk",[])), ?line <<"ething">> = iolist_to_binary(re:replace("ething","^[^]cde]","AsxwUn\\1GqkWNdgRJk",[global])), -?line <<"RornKmOnaFr�tWgtW">> = iolist_to_binary(re:replace("�","^\\�","R\\1o\\1r\\1nKmOnaFr&tWgtW",[])), -?line <<"RornKmOnaFr�tWgtW">> = iolist_to_binary(re:replace("�","^\\�","R\\1o\\1r\\1nKmOnaFr&tWgtW",[global])), -?line <<"ufbmbfOYuK�wf�E�dx">> = iolist_to_binary(re:replace("�","^�","ufbmbfOYuK&wf&E&\\1dx",[])), -?line <<"ufbmbfOYuK�wf�E�dx">> = iolist_to_binary(re:replace("�","^�","ufbmbfOYuK&wf&E&\\1dx",[global])), +?line <<"RornKmOnaFrtWgtW">> = iolist_to_binary(re:replace("","^\\","R\\1o\\1r\\1nKmOnaFr&tWgtW",[])), +?line <<"RornKmOnaFrtWgtW">> = iolist_to_binary(re:replace("","^\\","R\\1o\\1r\\1nKmOnaFr&tWgtW",[global])), +?line <<"ufbmbfOYuKÿwfÿEÿdx">> = iolist_to_binary(re:replace("ÿ","^ÿ","ufbmbfOYuK&wf&E&\\1dx",[])), +?line <<"ufbmbfOYuKÿwfÿEÿdx">> = iolist_to_binary(re:replace("ÿ","^ÿ","ufbmbfOYuK&wf&E&\\1dx",[global])), ?line <<"oAdJme0jw">> = iolist_to_binary(re:replace("0","^[0-9]+$","oAdJme\\1&jw",[])), ?line <<"oAdJme0jw">> = iolist_to_binary(re:replace("0","^[0-9]+$","oAdJme\\1&jw",[global])), ?line <<"1aoKN">> = iolist_to_binary(re:replace("1","^[0-9]+$","&aoKN",[])), @@ -14972,10 +14973,10 @@ def">> = iolist_to_binary(re:replace("abc def","abc$","M",[global])), ?line <<"abcWCabcSYXGPjRugTabcVGabcSX">> = iolist_to_binary(re:replace("abcS","(abc)\\123","\\1WC&YXGPjRugT\\1VG&X",[])), ?line <<"abcWCabcSYXGPjRugTabcVGabcSX">> = iolist_to_binary(re:replace("abcS","(abc)\\123","\\1WC&YXGPjRugT\\1VG&X",[global])), -?line <<"fabc�Uabc�UmiqabceCsabcabc�">> = iolist_to_binary(re:replace("abc�","(abc)\\223","f&U&Umiq\\1eCs\\1&",[])), -?line <<"fabc�Uabc�UmiqabceCsabcabc�">> = iolist_to_binary(re:replace("abc�","(abc)\\223","f&U&Umiq\\1eCs\\1&",[global])), -?line <<"JRFabcxnbabc�Vkabc�fWigQMuaY">> = iolist_to_binary(re:replace("abc�","(abc)\\323","JRF\\1xnb&Vk&fWigQMuaY",[])), -?line <<"JRFabcxnbabc�Vkabc�fWigQMuaY">> = iolist_to_binary(re:replace("abc�","(abc)\\323","JRF\\1xnb&Vk&fWigQMuaY",[global])), +?line <<"fabcUabcUmiqabceCsabcabc">> = iolist_to_binary(re:replace("abc","(abc)\\223","f&U&Umiq\\1eCs\\1&",[])), +?line <<"fabcUabcUmiqabceCsabcabc">> = iolist_to_binary(re:replace("abc","(abc)\\223","f&U&Umiq\\1eCs\\1&",[global])), +?line <<"JRFabcxnbabcÓVkabcÓfWigQMuaY">> = iolist_to_binary(re:replace("abcÓ","(abc)\\323","JRF\\1xnb&Vk&fWigQMuaY",[])), +?line <<"JRFabcxnbabcÓVkabcÓfWigQMuaY">> = iolist_to_binary(re:replace("abcÓ","(abc)\\323","JRF\\1xnb&Vk&fWigQMuaY",[global])), ?line <<"vgabc@QQ">> = iolist_to_binary(re:replace("abc@","(abc)\\100","vg&QQ",[])), ?line <<"vgabc@QQ">> = iolist_to_binary(re:replace("abc@","(abc)\\100","vg&QQ",[global])), ?line <<"abc@OkvNytabc@abcabc@a">> = iolist_to_binary(re:replace("abc@","(abc)\\100","&OkvNyt&\\1&a",[])), @@ -18343,24 +18344,24 @@ xb","(?!^)x","\\1tysI\\1v\\1BVwx\\1FOWG\\1&C",[multiline, ?line <<"cY">> = iolist_to_binary(re:replace("M","\\M","cY\\1",[global])), ?line <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(re:replace("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(a+)*b","yWOTIFhIX\\1H",[])), ?line <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(re:replace("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(a+)*b","yWOTIFhIX\\1H",[global])), -?line <<"NREGularsRREGularWEYrVRr">> = iolist_to_binary(re:replace("REGular","(?i)reg(?:ul(?:[a�]|ae)r|ex)","N&\\1sR&WEYrVRr",[])), -?line <<"NREGularsRREGularWEYrVRr">> = iolist_to_binary(re:replace("REGular","(?i)reg(?:ul(?:[a�]|ae)r|ex)","N&\\1sR&WEYrVRr",[global])), -?line <<"G">> = iolist_to_binary(re:replace("regulaer","(?i)reg(?:ul(?:[a�]|ae)r|ex)","G",[])), -?line <<"G">> = iolist_to_binary(re:replace("regulaer","(?i)reg(?:ul(?:[a�]|ae)r|ex)","G",[global])), -?line <<"PSsXtwlmy">> = iolist_to_binary(re:replace("Regex","(?i)reg(?:ul(?:[a�]|ae)r|ex)","PSsXtwlmy",[])), -?line <<"PSsXtwlmy">> = iolist_to_binary(re:replace("Regex","(?i)reg(?:ul(?:[a�]|ae)r|ex)","PSsXtwlmy",[global])), -?line <<"regul�rmiYTi">> = iolist_to_binary(re:replace("regul�r","(?i)reg(?:ul(?:[a�]|ae)r|ex)","&miYTi\\1\\1",[])), -?line <<"regul�rmiYTi">> = iolist_to_binary(re:replace("regul�r","(?i)reg(?:ul(?:[a�]|ae)r|ex)","&miYTi\\1\\1",[global])), -?line <<"W�����rxh�����yUoaLOIegmSA">> = iolist_to_binary(re:replace("�����","����[�-��-�]+","W&rxh&yUoaL\\1OIegmS\\1A",[])), -?line <<"W�����rxh�����yUoaLOIegmSA">> = iolist_to_binary(re:replace("�����","����[�-��-�]+","W&rxh&yUoaL\\1OIegmS\\1A",[global])), -?line <<"F�����gnWPyHeh�����tXTQ">> = iolist_to_binary(re:replace("�����","����[�-��-�]+","F&gnWPyHe\\1h&tXTQ",[])), -?line <<"F�����gnWPyHeh�����tXTQ">> = iolist_to_binary(re:replace("�����","����[�-��-�]+","F&gnWPyHe\\1h&tXTQ",[global])), -?line <<"sHerHnAhAdx">> = iolist_to_binary(re:replace("�����","����[�-��-�]+","sHer\\1HnA\\1h\\1Adx",[])), -?line <<"sHerHnAhAdx">> = iolist_to_binary(re:replace("�����","����[�-��-�]+","sHer\\1HnA\\1h\\1Adx",[global])), -?line <<"trobAQoU�����n">> = iolist_to_binary(re:replace("�����","����[�-��-�]+","tr\\1obAQoU&n",[])), -?line <<"trobAQoU�����n">> = iolist_to_binary(re:replace("�����","����[�-��-�]+","tr\\1obAQoU&n",[global])), -?line <<"�XAZSd">> = iolist_to_binary(re:replace("�XAZXB","(?<=Z)X.","Sd",[])), -?line <<"�XAZSd">> = iolist_to_binary(re:replace("�XAZXB","(?<=Z)X.","Sd",[global])), +?line <<"NREGularsRREGularWEYrVRr">> = iolist_to_binary(re:replace("REGular","(?i)reg(?:ul(?:[aä]|ae)r|ex)","N&\\1sR&WEYrVRr",[])), +?line <<"NREGularsRREGularWEYrVRr">> = iolist_to_binary(re:replace("REGular","(?i)reg(?:ul(?:[aä]|ae)r|ex)","N&\\1sR&WEYrVRr",[global])), +?line <<"G">> = iolist_to_binary(re:replace("regulaer","(?i)reg(?:ul(?:[aä]|ae)r|ex)","G",[])), +?line <<"G">> = iolist_to_binary(re:replace("regulaer","(?i)reg(?:ul(?:[aä]|ae)r|ex)","G",[global])), +?line <<"PSsXtwlmy">> = iolist_to_binary(re:replace("Regex","(?i)reg(?:ul(?:[aä]|ae)r|ex)","PSsXtwlmy",[])), +?line <<"PSsXtwlmy">> = iolist_to_binary(re:replace("Regex","(?i)reg(?:ul(?:[aä]|ae)r|ex)","PSsXtwlmy",[global])), +?line <<"regulärmiYTi">> = iolist_to_binary(re:replace("regulär","(?i)reg(?:ul(?:[aä]|ae)r|ex)","&miYTi\\1\\1",[])), +?line <<"regulärmiYTi">> = iolist_to_binary(re:replace("regulär","(?i)reg(?:ul(?:[aä]|ae)r|ex)","&miYTi\\1\\1",[global])), +?line <<"WÅæåäàrxhÅæåäàyUoaLOIegmSA">> = iolist_to_binary(re:replace("Åæåäà","Åæåä[à-ÿÀ-ß]+","W&rxh&yUoaL\\1OIegmS\\1A",[])), +?line <<"WÅæåäàrxhÅæåäàyUoaLOIegmSA">> = iolist_to_binary(re:replace("Åæåäà","Åæåä[à-ÿÀ-ß]+","W&rxh&yUoaL\\1OIegmS\\1A",[global])), +?line <<"FÅæåäÿgnWPyHehÅæåäÿtXTQ">> = iolist_to_binary(re:replace("Åæåäÿ","Åæåä[à-ÿÀ-ß]+","F&gnWPyHe\\1h&tXTQ",[])), +?line <<"FÅæåäÿgnWPyHehÅæåäÿtXTQ">> = iolist_to_binary(re:replace("Åæåäÿ","Åæåä[à-ÿÀ-ß]+","F&gnWPyHe\\1h&tXTQ",[global])), +?line <<"sHerHnAhAdx">> = iolist_to_binary(re:replace("ÅæåäÀ","Åæåä[à-ÿÀ-ß]+","sHer\\1HnA\\1h\\1Adx",[])), +?line <<"sHerHnAhAdx">> = iolist_to_binary(re:replace("ÅæåäÀ","Åæåä[à-ÿÀ-ß]+","sHer\\1HnA\\1h\\1Adx",[global])), +?line <<"trobAQoUÅæåäßn">> = iolist_to_binary(re:replace("Åæåäß","Åæåä[à-ÿÀ-ß]+","tr\\1obAQoU&n",[])), +?line <<"trobAQoUÅæåäßn">> = iolist_to_binary(re:replace("Åæåäß","Åæåä[à-ÿÀ-ß]+","tr\\1obAQoU&n",[global])), +?line <<"XAZSd">> = iolist_to_binary(re:replace("XAZXB","(?<=Z)X.","Sd",[])), +?line <<"XAZSd">> = iolist_to_binary(re:replace("XAZXB","(?<=Z)X.","Sd",[global])), ?line <<"A">> = iolist_to_binary(re:replace("ab cd defg","ab cd (?x) de fg","\\1A\\1",[])), ?line <<"A">> = iolist_to_binary(re:replace("ab cd defg","ab cd (?x) de fg","\\1A\\1",[global])), ?line <<"fab cddefgLdtKCtPab cddefgxvVUHDah">> = iolist_to_binary(re:replace("ab cddefg","ab cd(?x) de fg","f&LdtKC\\1\\1tP&xvVUHDah",[])), diff --git a/lib/stdlib/test/re_testoutput1_split_test.erl b/lib/stdlib/test/re_testoutput1_split_test.erl index e86a04b008..4fc85b95c0 100644 --- a/lib/stdlib/test/re_testoutput1_split_test.erl +++ b/lib/stdlib/test/re_testoutput1_split_test.erl @@ -1,7 +1,8 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2010. All Rights Reserved. +%% Copyright Ericsson AB 2008-2012. 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 @@ -524,14 +525,14 @@ run() -> ?line <<"ething">> = iolist_to_binary(join(re:split("ething","^[^]cde]",[{parts, 2}]))), ?line <<"ething">> = iolist_to_binary(join(re:split("ething","^[^]cde]",[]))), -?line <<"">> = iolist_to_binary(join(re:split("�","^\\�",[trim]))), -?line <<":">> = iolist_to_binary(join(re:split("�","^\\�",[{parts, +?line <<"">> = iolist_to_binary(join(re:split("","^\\",[trim]))), +?line <<":">> = iolist_to_binary(join(re:split("","^\\",[{parts, 2}]))), -?line <<":">> = iolist_to_binary(join(re:split("�","^\\�",[]))), -?line <<"">> = iolist_to_binary(join(re:split("�","^�",[trim]))), -?line <<":">> = iolist_to_binary(join(re:split("�","^�",[{parts, +?line <<":">> = iolist_to_binary(join(re:split("","^\\",[]))), +?line <<"">> = iolist_to_binary(join(re:split("ÿ","^ÿ",[trim]))), +?line <<":">> = iolist_to_binary(join(re:split("ÿ","^ÿ",[{parts, 2}]))), -?line <<":">> = iolist_to_binary(join(re:split("�","^�",[]))), +?line <<":">> = iolist_to_binary(join(re:split("ÿ","^ÿ",[]))), ?line <<"">> = iolist_to_binary(join(re:split("0","^[0-9]+$",[trim]))), ?line <<":">> = iolist_to_binary(join(re:split("0","^[0-9]+$",[{parts, 2}]))), @@ -22879,14 +22880,14 @@ def","abc$",[]))), ?line <<":abc:">> = iolist_to_binary(join(re:split("abcS","(abc)\\123",[{parts, 2}]))), ?line <<":abc:">> = iolist_to_binary(join(re:split("abcS","(abc)\\123",[]))), -?line <<":abc">> = iolist_to_binary(join(re:split("abc�","(abc)\\223",[trim]))), -?line <<":abc:">> = iolist_to_binary(join(re:split("abc�","(abc)\\223",[{parts, +?line <<":abc">> = iolist_to_binary(join(re:split("abc","(abc)\\223",[trim]))), +?line <<":abc:">> = iolist_to_binary(join(re:split("abc","(abc)\\223",[{parts, 2}]))), -?line <<":abc:">> = iolist_to_binary(join(re:split("abc�","(abc)\\223",[]))), -?line <<":abc">> = iolist_to_binary(join(re:split("abc�","(abc)\\323",[trim]))), -?line <<":abc:">> = iolist_to_binary(join(re:split("abc�","(abc)\\323",[{parts, +?line <<":abc:">> = iolist_to_binary(join(re:split("abc","(abc)\\223",[]))), +?line <<":abc">> = iolist_to_binary(join(re:split("abcÓ","(abc)\\323",[trim]))), +?line <<":abc:">> = iolist_to_binary(join(re:split("abcÓ","(abc)\\323",[{parts, 2}]))), -?line <<":abc:">> = iolist_to_binary(join(re:split("abc�","(abc)\\323",[]))), +?line <<":abc:">> = iolist_to_binary(join(re:split("abcÓ","(abc)\\323",[]))), ?line <<":abc">> = iolist_to_binary(join(re:split("abc@","(abc)\\100",[trim]))), ?line <<":abc:">> = iolist_to_binary(join(re:split("abc@","(abc)\\100",[{parts, 2}]))), @@ -28929,42 +28930,42 @@ xb","(?!^)x",[multiline]))), ?line <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(a+)*b",[{parts, 2}]))), ?line <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(a+)*b",[]))), -?line <<"">> = iolist_to_binary(join(re:split("REGular","(?i)reg(?:ul(?:[a�]|ae)r|ex)",[trim]))), -?line <<":">> = iolist_to_binary(join(re:split("REGular","(?i)reg(?:ul(?:[a�]|ae)r|ex)",[{parts, +?line <<"">> = iolist_to_binary(join(re:split("REGular","(?i)reg(?:ul(?:[aä]|ae)r|ex)",[trim]))), +?line <<":">> = iolist_to_binary(join(re:split("REGular","(?i)reg(?:ul(?:[aä]|ae)r|ex)",[{parts, 2}]))), -?line <<":">> = iolist_to_binary(join(re:split("REGular","(?i)reg(?:ul(?:[a�]|ae)r|ex)",[]))), -?line <<"">> = iolist_to_binary(join(re:split("regulaer","(?i)reg(?:ul(?:[a�]|ae)r|ex)",[trim]))), -?line <<":">> = iolist_to_binary(join(re:split("regulaer","(?i)reg(?:ul(?:[a�]|ae)r|ex)",[{parts, +?line <<":">> = iolist_to_binary(join(re:split("REGular","(?i)reg(?:ul(?:[aä]|ae)r|ex)",[]))), +?line <<"">> = iolist_to_binary(join(re:split("regulaer","(?i)reg(?:ul(?:[aä]|ae)r|ex)",[trim]))), +?line <<":">> = iolist_to_binary(join(re:split("regulaer","(?i)reg(?:ul(?:[aä]|ae)r|ex)",[{parts, 2}]))), -?line <<":">> = iolist_to_binary(join(re:split("regulaer","(?i)reg(?:ul(?:[a�]|ae)r|ex)",[]))), -?line <<"">> = iolist_to_binary(join(re:split("Regex","(?i)reg(?:ul(?:[a�]|ae)r|ex)",[trim]))), -?line <<":">> = iolist_to_binary(join(re:split("Regex","(?i)reg(?:ul(?:[a�]|ae)r|ex)",[{parts, +?line <<":">> = iolist_to_binary(join(re:split("regulaer","(?i)reg(?:ul(?:[aä]|ae)r|ex)",[]))), +?line <<"">> = iolist_to_binary(join(re:split("Regex","(?i)reg(?:ul(?:[aä]|ae)r|ex)",[trim]))), +?line <<":">> = iolist_to_binary(join(re:split("Regex","(?i)reg(?:ul(?:[aä]|ae)r|ex)",[{parts, 2}]))), -?line <<":">> = iolist_to_binary(join(re:split("Regex","(?i)reg(?:ul(?:[a�]|ae)r|ex)",[]))), -?line <<"">> = iolist_to_binary(join(re:split("regul�r","(?i)reg(?:ul(?:[a�]|ae)r|ex)",[trim]))), -?line <<":">> = iolist_to_binary(join(re:split("regul�r","(?i)reg(?:ul(?:[a�]|ae)r|ex)",[{parts, +?line <<":">> = iolist_to_binary(join(re:split("Regex","(?i)reg(?:ul(?:[aä]|ae)r|ex)",[]))), +?line <<"">> = iolist_to_binary(join(re:split("regulär","(?i)reg(?:ul(?:[aä]|ae)r|ex)",[trim]))), +?line <<":">> = iolist_to_binary(join(re:split("regulär","(?i)reg(?:ul(?:[aä]|ae)r|ex)",[{parts, 2}]))), -?line <<":">> = iolist_to_binary(join(re:split("regul�r","(?i)reg(?:ul(?:[a�]|ae)r|ex)",[]))), -?line <<"">> = iolist_to_binary(join(re:split("�����","����[�-��-�]+",[trim]))), -?line <<":">> = iolist_to_binary(join(re:split("�����","����[�-��-�]+",[{parts, +?line <<":">> = iolist_to_binary(join(re:split("regulär","(?i)reg(?:ul(?:[aä]|ae)r|ex)",[]))), +?line <<"">> = iolist_to_binary(join(re:split("Åæåäà","Åæåä[à-ÿÀ-ß]+",[trim]))), +?line <<":">> = iolist_to_binary(join(re:split("Åæåäà","Åæåä[à-ÿÀ-ß]+",[{parts, 2}]))), -?line <<":">> = iolist_to_binary(join(re:split("�����","����[�-��-�]+",[]))), -?line <<"">> = iolist_to_binary(join(re:split("�����","����[�-��-�]+",[trim]))), -?line <<":">> = iolist_to_binary(join(re:split("�����","����[�-��-�]+",[{parts, +?line <<":">> = iolist_to_binary(join(re:split("Åæåäà","Åæåä[à-ÿÀ-ß]+",[]))), +?line <<"">> = iolist_to_binary(join(re:split("Åæåäÿ","Åæåä[à-ÿÀ-ß]+",[trim]))), +?line <<":">> = iolist_to_binary(join(re:split("Åæåäÿ","Åæåä[à-ÿÀ-ß]+",[{parts, 2}]))), -?line <<":">> = iolist_to_binary(join(re:split("�����","����[�-��-�]+",[]))), -?line <<"">> = iolist_to_binary(join(re:split("�����","����[�-��-�]+",[trim]))), -?line <<":">> = iolist_to_binary(join(re:split("�����","����[�-��-�]+",[{parts, +?line <<":">> = iolist_to_binary(join(re:split("Åæåäÿ","Åæåä[à-ÿÀ-ß]+",[]))), +?line <<"">> = iolist_to_binary(join(re:split("ÅæåäÀ","Åæåä[à-ÿÀ-ß]+",[trim]))), +?line <<":">> = iolist_to_binary(join(re:split("ÅæåäÀ","Åæåä[à-ÿÀ-ß]+",[{parts, 2}]))), -?line <<":">> = iolist_to_binary(join(re:split("�����","����[�-��-�]+",[]))), -?line <<"">> = iolist_to_binary(join(re:split("�����","����[�-��-�]+",[trim]))), -?line <<":">> = iolist_to_binary(join(re:split("�����","����[�-��-�]+",[{parts, +?line <<":">> = iolist_to_binary(join(re:split("ÅæåäÀ","Åæåä[à-ÿÀ-ß]+",[]))), +?line <<"">> = iolist_to_binary(join(re:split("Åæåäß","Åæåä[à-ÿÀ-ß]+",[trim]))), +?line <<":">> = iolist_to_binary(join(re:split("Åæåäß","Åæåä[à-ÿÀ-ß]+",[{parts, 2}]))), -?line <<":">> = iolist_to_binary(join(re:split("�����","����[�-��-�]+",[]))), -?line <<"�XAZ">> = iolist_to_binary(join(re:split("�XAZXB","(?<=Z)X.",[trim]))), -?line <<"�XAZ:">> = iolist_to_binary(join(re:split("�XAZXB","(?<=Z)X.",[{parts, +?line <<":">> = iolist_to_binary(join(re:split("Åæåäß","Åæåä[à-ÿÀ-ß]+",[]))), +?line <<"XAZ">> = iolist_to_binary(join(re:split("XAZXB","(?<=Z)X.",[trim]))), +?line <<"XAZ:">> = iolist_to_binary(join(re:split("XAZXB","(?<=Z)X.",[{parts, 2}]))), -?line <<"�XAZ:">> = iolist_to_binary(join(re:split("�XAZXB","(?<=Z)X.",[]))), +?line <<"XAZ:">> = iolist_to_binary(join(re:split("XAZXB","(?<=Z)X.",[]))), ?line <<"">> = iolist_to_binary(join(re:split("ab cd defg","ab cd (?x) de fg",[trim]))), ?line <<":">> = iolist_to_binary(join(re:split("ab cd defg","ab cd (?x) de fg",[{parts, 2}]))), diff --git a/lib/stdlib/test/sets_SUITE.erl b/lib/stdlib/test/sets_SUITE.erl index f284276bd7..c0cf1fc7e8 100644 --- a/lib/stdlib/test/sets_SUITE.erl +++ b/lib/stdlib/test/sets_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2011. All Rights Reserved. +%% Copyright Ericsson AB 2004-2013. 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 @@ -35,7 +35,7 @@ -import(lists, [foldl/3,reverse/1]). init_per_testcase(_Case, Config) -> - ?line Dog = ?t:timetrap(?t:minutes(5)), + Dog = ?t:timetrap(?t:minutes(5)), [{watchdog,Dog}|Config]. end_per_testcase(_Case, Config) -> @@ -70,65 +70,65 @@ create(Config) when is_list(Config) -> test_all(fun create_1/1). create_1(M) -> - ?line S0 = M:empty(), - ?line [] = M:to_list(S0), - ?line 0 = M:size(S0), - ?line true = M:is_empty(S0), + S0 = M(empty, []), + [] = M(to_list, S0), + 0 = M(size, S0), + true = M(is_empty, S0), E = make_ref(), - ?line One = M:singleton(E), - ?line 1 = M:size(One), - ?line false = M:is_empty(One), - [E] = M:to_list(One), + One = M(singleton, E), + 1 = M(size, One), + false = M(is_empty, One), + [E] = M(to_list, One), S0. add_element(Config) when is_list(Config) -> test_all([{0,132},{253,258},{510,514}], fun add_element_1/2). add_element_1(List, M) -> - ?line S = M:from_list(List), - ?line SortedSet = lists:usort(List), - ?line SortedSet = lists:sort(M:to_list(S)), + S = M(from_list, List), + SortedSet = lists:usort(List), + SortedSet = lists:sort(M(to_list, S)), %% Make sure that we get the same result by inserting %% elements one at the time. - ?line S2 = foldl(fun(El, Set) -> M:add_element(El, Set) end, - M:empty(), List), - ?line true = M:equal(S, S2), + S2 = foldl(fun(El, Set) -> M(add_element, {El,Set}) end, + M(empty, []), List), + true = M(equal, {S,S2}), %% Insert elements, randomly delete inserted elements, %% and re-inserted all deleted elements at the end. - ?line S3 = add_element_del(List, M, M:empty(), [], []), - ?line true = M:equal(S2, S3), - ?line true = M:equal(S, S3), + S3 = add_element_del(List, M, M(empty, []), [], []), + true = M(equal, {S2,S3}), + true = M(equal, {S,S3}), S. add_element_del([H|T], M, S, Del, []) -> - add_element_del(T, M, M:add_element(H, S), Del, [H]); + add_element_del(T, M, M(add_element, {H,S}), Del, [H]); add_element_del([H|T], M, S0, Del, Inserted) -> - S1 = M:add_element(H, S0), + S1 = M(add_element, {H,S0}), case random:uniform(3) of 1 -> OldEl = lists:nth(random:uniform(length(Inserted)), Inserted), - S = M:del_element(OldEl, S1), + S = M(del_element, {OldEl,S1}), add_element_del(T, M, S, [OldEl|Del], [H|Inserted]); _ -> add_element_del(T, M, S1, Del, [H|Inserted]) end; add_element_del([], M, S, Del, _) -> - M:union(S, M:from_list(Del)). + M(union, {S,M(from_list, Del)}). del_element(Config) when is_list(Config) -> test_all([{0,132},{253,258},{510,514},{1022,1026}], fun del_element_1/2). del_element_1(List, M) -> - ?line S0 = M:from_list(List), - ?line Empty = foldl(fun(El, Set) -> M:del_element(El, Set) end, S0, List), - ?line Empty = M:empty(), - ?line M:is_empty(Empty), - ?line S1 = foldl(fun(El, Set) -> - M:add_element(El, Set) - end, S0, reverse(List)), - ?line true = M:equal(S0, S1), + S0 = M(from_list, List), + Empty = foldl(fun(El, Set) -> M(del_element, {El,Set}) end, S0, List), + Empty = M(empty, []), + true = M(is_empty, Empty), + S1 = foldl(fun(El, Set) -> + M(add_element, {El,Set}) + end, S0, reverse(List)), + true = M(equal, {S0,S1}), S1. subtract(Config) when is_list(Config) -> @@ -138,23 +138,23 @@ subtract(Config) when is_list(Config) -> test_all([{2,69},{126,130},{253,258},511,512,{1023,1030}], fun subtract_1/2). subtract_empty(M) -> - ?line Empty = M:empty(), - ?line true = M:is_empty(M:subtract(Empty, Empty)), - M:subtract(Empty, Empty). + Empty = M(empty, []), + true = M(is_empty, M(subtract, {Empty,Empty})), + M(subtract, {Empty,Empty}). subtract_1(List, M) -> - ?line S0 = M:from_list(List), - ?line Empty = M:empty(), + S0 = M(from_list, List), + Empty = M(empty, []), %% Trivial cases. - ?line true = M:is_empty(M:subtract(Empty, S0)), - ?line true = M:equal(S0, M:subtract(S0, Empty)), + true = M(is_empty, M(subtract, {Empty,S0})), + true = M(equal, {S0,M(subtract, {S0,Empty})}), %% Not so trivial. - ?line subtract_check(List, mutate_some(remove_some(List, 0.4)), M), - ?line subtract_check(List, rnd_list(length(List) div 2 + 5), M), - ?line subtract_check(List, rnd_list(length(List) div 7 + 9), M), - ?line subtract_check(List, mutate_some(List), M). + subtract_check(List, mutate_some(remove_some(List, 0.4)), M), + subtract_check(List, rnd_list(length(List) div 2 + 5), M), + subtract_check(List, rnd_list(length(List) div 7 + 9), M), + subtract_check(List, mutate_some(List), M). subtract_check(A, B, M) -> one_subtract_check(B, A, M), @@ -163,12 +163,12 @@ subtract_check(A, B, M) -> one_subtract_check(A, B, M) -> ASorted = lists:usort(A), BSorted = lists:usort(B), - ASet = M:from_list(A), - BSet = M:from_list(B), - DiffSet = M:subtract(ASet, BSet), + ASet = M(from_list, A), + BSet = M(from_list, B), + DiffSet = M(subtract, {ASet,BSet}), Diff = ASorted -- BSorted, - true = M:equal(DiffSet, M:from_list(Diff)), - Diff = lists:sort(M:to_list(DiffSet)), + true = M(equal, {DiffSet,M(from_list, Diff)}), + Diff = lists:sort(M(to_list, DiffSet)), DiffSet. intersection(Config) when is_list(Config) -> @@ -176,60 +176,60 @@ intersection(Config) when is_list(Config) -> test_all([{1,65},{126,130},{253,259},{499,513},{1023,1025}], fun intersection_1/2). intersection_1(List, M) -> - ?line S0 = M:from_list(List), + S0 = M(from_list, List), %% Intersection with self. - ?line true = M:equal(S0, M:intersection(S0, S0)), - ?line true = M:equal(S0, M:intersection([S0,S0])), - ?line true = M:equal(S0, M:intersection([S0,S0,S0])), - ?line true = M:equal(S0, M:intersection([S0])), + true = M(equal, {S0,M(intersection, {S0,S0})}), + true = M(equal, {S0,M(intersection, [S0,S0])}), + true = M(equal, {S0,M(intersection, [S0,S0,S0])}), + true = M(equal, {S0,M(intersection, [S0])}), %% Intersection with empty. - ?line Empty = M:empty(), - ?line true = M:equal(Empty, M:intersection(S0, Empty)), - ?line true = M:equal(Empty, M:intersection([S0,Empty,S0,Empty])), + Empty = M(empty, []), + true = M(equal, {Empty,M(intersection, {S0,Empty})}), + true = M(equal, {Empty,M(intersection, [S0,Empty,S0,Empty])}), %% The intersection of no sets is undefined. - ?line {'EXIT',_} = (catch M:intersection([])), + {'EXIT',_} = (catch M(intersection, [])), %% Disjoint sets. - ?line Disjoint = [{El} || El <- List], - ?line DisjointSet = M:from_list(Disjoint), - ?line M:is_empty(M:intersection(S0, DisjointSet)), + Disjoint = [{El} || El <- List], + DisjointSet = M(from_list, Disjoint), + true = M(is_empty, M(intersection, {S0,DisjointSet})), %% Disjoint, different sizes. - ?line M:is_empty(M:intersection(S0, M:from_list(remove_some(Disjoint, 0.3)))), - ?line M:is_empty(M:intersection(S0, M:from_list(remove_some(Disjoint, 0.7)))), - ?line M:is_empty(M:intersection(S0, M:from_list(remove_some(Disjoint, 0.9)))), - ?line M:is_empty(M:intersection(M:from_list(remove_some(List, 0.3)), DisjointSet)), - ?line M:is_empty(M:intersection(M:from_list(remove_some(List, 0.5)), DisjointSet)), - ?line M:is_empty(M:intersection(M:from_list(remove_some(List, 0.9)), DisjointSet)), + [begin + SomeRemoved = M(from_list, remove_some(Disjoint, HowMuch)), + true = M(is_empty, M(intersection, {S0,SomeRemoved})), + MoreRemoved = M(from_list, remove_some(List, HowMuch)), + true = M(is_empty, M(intersection, {MoreRemoved,DisjointSet})) + end || HowMuch <- [0.3,0.5,0.7,0.9]], %% Partial overlap (one or more elements in result set). %% The sets have almost the same size. (Almost because a duplicated %% element in the original list could be mutated and not mutated %% at the same time.) - ?line PartialOverlap = mutate_some(List, []), - ?line IntersectionSet = check_intersection(List, PartialOverlap, M), - ?line false = M:is_empty(IntersectionSet), + PartialOverlap = mutate_some(List, []), + IntersectionSet = check_intersection(List, PartialOverlap, M), + false = M(is_empty, IntersectionSet), %% Partial overlap, different set sizes. (Intersection possibly empty.) - ?line check_intersection(List, remove_some(PartialOverlap, 0.1), M), - ?line check_intersection(List, remove_some(PartialOverlap, 0.3), M), - ?line check_intersection(List, remove_some(PartialOverlap, 0.5), M), - ?line check_intersection(List, remove_some(PartialOverlap, 0.7), M), - ?line check_intersection(List, remove_some(PartialOverlap, 0.9), M), + check_intersection(List, remove_some(PartialOverlap, 0.1), M), + check_intersection(List, remove_some(PartialOverlap, 0.3), M), + check_intersection(List, remove_some(PartialOverlap, 0.5), M), + check_intersection(List, remove_some(PartialOverlap, 0.7), M), + check_intersection(List, remove_some(PartialOverlap, 0.9), M), IntersectionSet. check_intersection(Orig, Mutated, M) -> - OrigSet = M:from_list(Orig), - MutatedSet = M:from_list(Mutated), + OrigSet = M(from_list, Orig), + MutatedSet = M(from_list, Mutated), Intersection = [El || El <- Mutated, not is_tuple(El)], SortedIntersection = lists:usort(Intersection), - IntersectionSet = M:intersection(OrigSet, MutatedSet), - true = M:equal(IntersectionSet, M:from_list(SortedIntersection)), - SortedIntersection = lists:sort(M:to_list(IntersectionSet)), + IntersectionSet = M(intersection, {OrigSet,MutatedSet}), + true = M(equal, {IntersectionSet,M(from_list, SortedIntersection)}), + SortedIntersection = lists:sort(M(to_list, IntersectionSet)), IntersectionSet. @@ -239,63 +239,63 @@ union(Config) when is_list(Config) -> test_all([{1,71},{125,129},{254,259},{510,513},{1023,1025}], fun union_1/2). union_1(List, M) -> - ?line S = M:from_list(List), + S = M(from_list, List), %% Union with self and empty. - ?line Empty = M:empty(), - ?line true = M:equal(S, M:union(S, S)), - ?line true = M:equal(S, M:union([S,S])), - ?line true = M:equal(S, M:union([S,S,Empty])), - ?line true = M:equal(S, M:union([S,Empty,S])), - ?line true = M:equal(S, M:union(S, Empty)), - ?line true = M:equal(S, M:union([S])), - ?line true = M:is_empty(M:union([])), + Empty = M(empty, []), + true = M(equal, {S,M(union, {S,S})}), + true = M(equal, {S,M(union, [S,S])}), + true = M(equal, {S,M(union, [S,S,Empty])}), + true = M(equal, {S,M(union, [S,Empty,S])}), + true = M(equal, {S,M(union, {S,Empty})}), + true = M(equal, {S,M(union, [S])}), + true = M(is_empty, M(union, [])), %% Partial overlap. - ?line check_union(List, remove_some(mutate_some(List), 0.9), M), - ?line check_union(List, remove_some(mutate_some(List), 0.7), M), - ?line check_union(List, remove_some(mutate_some(List), 0.5), M), - ?line check_union(List, remove_some(mutate_some(List), 0.3), M), - ?line check_union(List, remove_some(mutate_some(List), 0.1), M), - - ?line check_union(List, mutate_some(remove_some(List, 0.9)), M), - ?line check_union(List, mutate_some(remove_some(List, 0.7)), M), - ?line check_union(List, mutate_some(remove_some(List, 0.5)), M), - ?line check_union(List, mutate_some(remove_some(List, 0.3)), M), - ?line check_union(List, mutate_some(remove_some(List, 0.1)), M). + check_union(List, remove_some(mutate_some(List), 0.9), M), + check_union(List, remove_some(mutate_some(List), 0.7), M), + check_union(List, remove_some(mutate_some(List), 0.5), M), + check_union(List, remove_some(mutate_some(List), 0.3), M), + check_union(List, remove_some(mutate_some(List), 0.1), M), + + check_union(List, mutate_some(remove_some(List, 0.9)), M), + check_union(List, mutate_some(remove_some(List, 0.7)), M), + check_union(List, mutate_some(remove_some(List, 0.5)), M), + check_union(List, mutate_some(remove_some(List, 0.3)), M), + check_union(List, mutate_some(remove_some(List, 0.1)), M). check_union(Orig, Other, M) -> - OrigSet = M:from_list(Orig), - OtherSet = M:from_list(Other), + OrigSet = M(from_list, Orig), + OtherSet = M(from_list, Other), Union = Orig++Other, SortedUnion = lists:usort(Union), - UnionSet = M:union(OrigSet, OtherSet), - SortedUnion = lists:sort(M:to_list(UnionSet)), - M:equal(UnionSet, M:from_list(Union)), + UnionSet = M(union, {OrigSet,OtherSet}), + SortedUnion = lists:sort(M(to_list, UnionSet)), + M(equal, {UnionSet,M(from_list, Union)}), UnionSet. is_subset(Config) when is_list(Config) -> test_all([{1,132},{253,270},{299,311}], fun is_subset_1/2). is_subset_1(List, M) -> - ?line S = M:from_list(List), - ?line Empty = M:empty(), + S = M(from_list, List), + Empty = M(empty, []), %% Subset of empty and self. - ?line true = M:is_subset(Empty, Empty), - ?line true = M:is_subset(Empty, S), - ?line false = M:is_subset(S, Empty), - ?line true = M:is_subset(S, S), + true = M(is_subset, {Empty,Empty}), + true = M(is_subset, {Empty,S}), + false = M(is_subset, {S,Empty}), + true = M(is_subset, {S,S}), %% Other cases. - Res = [?line false = M:is_subset(M:singleton(make_ref()), S), - ?line true = M:is_subset(M:singleton(hd(List)), S), - ?line true = check_subset(remove_some(List, 0.1), List, M), - ?line true = check_subset(remove_some(List, 0.5), List, M), - ?line true = check_subset(remove_some(List, 0.9), List, M), - ?line check_subset(mutate_some(List), List, M), - ?line check_subset(rnd_list(length(List) div 2 + 5), List, M), - ?line subtract_check(List, rnd_list(length(List) div 7 + 9), M) + Res = [false = M(is_subset, {M(singleton, make_ref()),S}), + true = M(is_subset, {M(singleton, hd(List)),S}), + true = check_subset(remove_some(List, 0.1), List, M), + true = check_subset(remove_some(List, 0.5), List, M), + true = check_subset(remove_some(List, 0.9), List, M), + check_subset(mutate_some(List), List, M), + check_subset(rnd_list(length(List) div 2 + 5), List, M), + subtract_check(List, rnd_list(length(List) div 7 + 9), M) ], res_to_set(Res, M, 0, []). @@ -304,12 +304,12 @@ check_subset(X, Y, M) -> check_one_subset(X, Y, M). check_one_subset(X, Y, M) -> - XSet = M:from_list(X), - YSet = M:from_list(Y), + XSet = M(from_list, X), + YSet = M(from_list, Y), SortedX = lists:usort(X), SortedY = lists:usort(Y), IsSubSet = length(SortedY--SortedX) =:= length(SortedY) - length(SortedX), - IsSubSet = M:is_subset(XSet, YSet), + IsSubSet = M(is_subset, {XSet,YSet}), IsSubSet. %% Encode all test results as a set to return. @@ -317,54 +317,54 @@ res_to_set([true|T], M, I, Acc) -> res_to_set(T, M, I+1, [I|Acc]); res_to_set([_|T], M, I, Acc) -> res_to_set(T, M, I+1, Acc); -res_to_set([], M, _, Acc) -> M:from_list(Acc). +res_to_set([], M, _, Acc) -> M(from_list, Acc). is_set(Config) when is_list(Config) -> %% is_set/1 is tested in the other test cases when its argument %% is a set. Here test some arguments that makes it return false. - ?line false = gb_sets:is_set([a,b]), - ?line false = gb_sets:is_set({a,very,bad,tuple}), + false = gb_sets:is_set([a,b]), + false = gb_sets:is_set({a,very,bad,tuple}), - ?line false = sets:is_set([a,b]), - ?line false = sets:is_set({a,very,bad,tuple}), + false = sets:is_set([a,b]), + false = sets:is_set({a,very,bad,tuple}), - ?line false = ordsets:is_set([b,a]), - ?line false = ordsets:is_set({bad,tuple}), + false = ordsets:is_set([b,a]), + false = ordsets:is_set({bad,tuple}), %% Now test values that are known to be bad for all set representations. test_all(fun is_set_1/1). is_set_1(M) -> - ?line false = M:is_set(self()), - ?line false = M:is_set(blurf), - ?line false = M:is_set(make_ref()), - ?line false = M:is_set(<<1,2,3>>), - ?line false = M:is_set(42), - ?line false = M:is_set(math:pi()), - ?line false = M:is_set({}), - M:empty(). + false = M(is_set, self()), + false = M(is_set, blurf), + false = M(is_set, make_ref()), + false = M(is_set, <<1,2,3>>), + false = M(is_set, 42), + false = M(is_set, math:pi()), + false = M(is_set, {}), + M(empty, []). fold(Config) when is_list(Config) -> test_all([{0,71},{125,129},{254,259},{510,513},{1023,1025},{9999,10001}], fun fold_1/2). fold_1(List, M) -> - ?line S = M:from_list(List), - ?line L = M:fold(fun(E, A) -> [E|A] end, [], S), - ?line true = lists:sort(L) =:= lists:usort(List), - M:empty(). + S = M(from_list, List), + L = M(fold, {fun(E, A) -> [E|A] end,[],S}), + true = lists:sort(L) =:= lists:usort(List), + M(empty, []). filter(Config) when is_list(Config) -> test_all([{0,69},{126,130},{254,259},{510,513},{1023,1025},{7999,8000}], fun filter_1/2). filter_1(List, M) -> - ?line S = M:from_list(List), + S = M(from_list, List), IsNumber = fun(X) -> is_number(X) end, - ?line M:equal(M:from_list(lists:filter(IsNumber, List)), - M:filter(IsNumber, S)), - ?line M:filter(fun(X) -> is_atom(X) end, S). + M(equal, {M(from_list, lists:filter(IsNumber, List)), + M(filter, {IsNumber,S})}), + M(filter, {fun(X) -> is_atom(X) end,S}). %%% %%% Test specifics for gb_sets. @@ -375,26 +375,26 @@ take_smallest(Config) when is_list(Config) -> fun take_smallest_1/2). take_smallest_1(List, M) -> - case M:module() of + case M(module, []) of gb_sets -> take_smallest_2(List, M); _ -> ok end, - M:empty(). + M(empty, []). take_smallest_2(List0, M) -> - ?line List = lists:usort(List0), - ?line S = M:from_list(List0), + List = lists:usort(List0), + S = M(from_list, List0), take_smallest_3(S, List, M). take_smallest_3(S0, List0, M) -> - case M:is_empty(S0) of + case M(is_empty, S0) of true -> ok; false -> - ?line Smallest = hd(List0), - ?line Smallest = gb_sets:smallest(S0), - ?line {Smallest,S} = gb_sets:take_smallest(S0), - ?line List = tl(List0), - ?line true = gb_sets:to_list(S) =:= List, + Smallest = hd(List0), + Smallest = gb_sets:smallest(S0), + {Smallest,S} = gb_sets:take_smallest(S0), + List = tl(List0), + true = gb_sets:to_list(S) =:= List, take_smallest_3(S, List, M) end. @@ -403,26 +403,26 @@ take_largest(Config) when is_list(Config) -> fun take_largest_1/2). take_largest_1(List, M) -> - case M:module() of + case M(module, []) of gb_sets -> take_largest_2(List, M); _ -> ok end, - M:empty(). + M(empty, []). take_largest_2(List0, M) -> - ?line List = reverse(lists:usort(List0)), - ?line S = M:from_list(List0), + List = reverse(lists:usort(List0)), + S = M(from_list, List0), take_largest_3(S, List, M). take_largest_3(S0, List0, M) -> - case M:is_empty(S0) of + case M(is_empty, S0) of true -> ok; false -> - ?line Largest = hd(List0), - ?line Largest = gb_sets:largest(S0), - ?line {Largest,S} = gb_sets:take_largest(S0), - ?line List = tl(List0), - ?line true = gb_sets:to_list(S) =:= reverse(List), + Largest = hd(List0), + Largest = gb_sets:largest(S0), + {Largest,S} = gb_sets:take_largest(S0), + List = tl(List0), + true = gb_sets:to_list(S) =:= reverse(List), take_largest_3(S, List, M) end. @@ -441,23 +441,23 @@ sets_mods() -> [Ordsets,Sets,Gb]. test_all(Tester) -> - ?line Res = [begin - random:seed(1, 2, 42), - S = Tester(M), - {M:size(S),lists:sort(M:to_list(S))} - end || M <- sets_mods()], - ?line all_same(Res). + Res = [begin + random:seed(1, 2, 42), + S = Tester(M), + {M(size, S),lists:sort(M(to_list, S))} + end || M <- sets_mods()], + all_same(Res). test_all([{Low,High}|T], Tester) -> test_all(lists:seq(Low, High)++T, Tester); test_all([Sz|T], Tester) when is_integer(Sz) -> List = rnd_list(Sz), - ?line Res = [begin + Res = [begin random:seed(19, 2, Sz), S = Tester(List, M), - {M:size(S),lists:sort(M:to_list(S))} + {M(size, S),lists:sort(M(to_list, S))} end || M <- sets_mods()], - ?line all_same(Res), + all_same(Res), test_all(T, Tester); test_all([], _) -> ok. diff --git a/lib/stdlib/test/sets_test_lib.erl b/lib/stdlib/test/sets_test_lib.erl index bdfb0d59d2..86f009a8f9 100644 --- a/lib/stdlib/test/sets_test_lib.erl +++ b/lib/stdlib/test/sets_test_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2010. All Rights Reserved. +%% Copyright Ericsson AB 2004-2013. 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 @@ -17,91 +17,89 @@ %% %CopyrightEnd% %% --module(sets_test_lib, [Mod,Equal]). - --export([module/0,equal/2,empty/0,from_list/1,to_list/1,singleton/1, - add_element/2,del_element/2,size/1,is_empty/1,is_set/1, - intersection/1,intersection/2,subtract/2, - union/1,union/2,is_subset/2,fold/3,filter/2]). - -module() -> - Mod. - -equal(X, Y) -> - Equal(X, Y). - -empty() -> - Mod:new(). - -from_list(L) -> - Mod:from_list(L). - -to_list(S) -> - Mod:to_list(S). +-module(sets_test_lib). + +-export([new/2]). + +new(Mod, Eq) -> + fun (add_element, {El,S}) -> add_element(Mod, El, S); + (del_element, {El,S}) -> del_element(Mod, El, S); + (empty, []) -> Mod:new(); + (equal, {S1,S2}) -> Eq(S1, S2); + (filter, {F,S}) -> filter(Mod, F, S); + (fold, {F,A,S}) -> fold(Mod, F, A, S); + (from_list, L) -> Mod:from_list(L); + (intersection, {S1,S2}) -> intersection(Mod, Eq, S1, S2); + (intersection, Ss) -> intersection(Mod, Eq, Ss); + (is_empty, S) -> is_empty(Mod, S); + (is_set, S) -> Mod:is_set(S); + (is_subset, {S,Set}) -> is_subset(Mod, Eq, S, Set); + (module, []) -> Mod; + (singleton, E) -> singleton(Mod, E); + (size, S) -> Mod:size(S); + (subtract, {S1,S2}) -> subtract(Mod, S1, S2); + (to_list, S) -> Mod:to_list(S); + (union, {S1,S2}) -> union(Mod, Eq, S1, S2); + (union, Ss) -> union(Mod, Eq, Ss) + end. -singleton(E) -> +singleton(Mod, E) -> case erlang:function_exported(Mod, singleton, 1) of true -> Mod:singleton(E); - false -> from_list([E]) + false -> Mod:from_list([E]) end. -add_element(El, S0) -> +add_element(Mod, El, S0) -> S = Mod:add_element(El, S0), true = Mod:is_element(El, S), - false = is_empty(S), + false = is_empty(Mod, S), true = Mod:is_set(S), S. -del_element(El, S0) -> +del_element(Mod, El, S0) -> S = Mod:del_element(El, S0), false = Mod:is_element(El, S), true = Mod:is_set(S), S. -size(S) -> - Mod:size(S). - -is_empty(S) -> +is_empty(Mod, S) -> true = Mod:is_set(S), case erlang:function_exported(Mod, is_empty, 1) of true -> Mod:is_empty(S); false -> Mod:size(S) == 0 end. -is_set(S) -> - Mod:is_set(S). - -intersection(S1, S2) -> +intersection(Mod, Equal, S1, S2) -> S = Mod:intersection(S1, S2), true = Equal(S, Mod:intersection(S2, S1)), - Disjoint = is_empty(S), + Disjoint = is_empty(Mod, S), Disjoint = Mod:is_disjoint(S1, S2), Disjoint = Mod:is_disjoint(S2, S1), S. -intersection(Ss) -> +intersection(Mod, Equal, Ss) -> S = Mod:intersection(Ss), true = Equal(S, Mod:intersection(lists:reverse(Ss))), S. -subtract(S1, S2) -> +subtract(Mod, S1, S2) -> S = Mod:subtract(S1, S2), true = Mod:is_set(S), true = Mod:size(S) =< Mod:size(S1), S. -union(S1, S2) -> +union(Mod, Equal, S1, S2) -> S = Mod:union(S1, S2), true = Equal(S, Mod:union(S2, S1)), true = Mod:is_set(S), S. -union(Ss) -> +union(Mod, Equal, Ss) -> S = Mod:union(Ss), true = Equal(S, Mod:union(lists:reverse(Ss))), S. -is_subset(S, Set) -> +is_subset(Mod, Equal, S, Set) -> case Mod:is_subset(S, Set) of false -> false; true -> @@ -115,10 +113,10 @@ is_subset(S, Set) -> true end. -fold(F, A, S) -> +fold(Mod, F, A, S) -> true = Mod:is_set(S), Mod:fold(F, A, S). -filter(F, S) -> +filter(Mod, F, S) -> true = Mod:is_set(S), Mod:filter(F, S). diff --git a/lib/stdlib/test/shell_SUITE.erl b/lib/stdlib/test/shell_SUITE.erl index 4b83e42ee0..990b1f5eb2 100644 --- a/lib/stdlib/test/shell_SUITE.erl +++ b/lib/stdlib/test/shell_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2012. All Rights Reserved. +%% Copyright Ericsson AB 2004-2013. 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 @@ -28,8 +28,8 @@ refman_bit_syntax/1, progex_bit_syntax/1, progex_records/1, progex_lc/1, progex_funs/1, - otp_5990/1, otp_6166/1, otp_6554/1, otp_6785/1, - otp_7184/1, otp_7232/1, otp_8393/1]). + otp_5990/1, otp_6166/1, otp_6554/1, + otp_7184/1, otp_7232/1, otp_8393/1, otp_10302/1]). -export([ start_restricted_from_shell/1, start_restricted_on_command_line/1,restricted_local/1]). @@ -92,8 +92,8 @@ groups() -> [progex_bit_syntax, progex_records, progex_lc, progex_funs]}, {tickets, [], - [otp_5990, otp_6166, otp_6554, otp_6785, otp_7184, - otp_7232, otp_8393]}]. + [otp_5990, otp_6166, otp_6554, otp_7184, + otp_7232, otp_8393, otp_10302]}]. init_per_suite(Config) -> Config. @@ -108,7 +108,7 @@ end_per_group(_GroupName, Config) -> Config. --record(state, {bin, reply, leader}). +-record(state, {bin, reply, leader, unic = latin1}). start_restricted_from_shell(doc) -> @@ -374,15 +374,18 @@ records(Config) when is_list(Config) -> MS = ?MODULE_STRING, RR1 = "rr(" ++ MS ++ "). #state{}.", ?line "[state]\n" - "#state{bin = undefined,reply = undefined,leader = undefined}.\n" = + "#state{bin = undefined,reply = undefined,leader = undefined,\n" + " unic = latin1}.\n" = t(RR1), RR2 = "rr(" ++ MS ++ ",[state]). #state{}.", ?line "[state]\n" - "#state{bin = undefined,reply = undefined,leader = undefined}.\n" = + "#state{bin = undefined,reply = undefined,leader = undefined,\n" + " unic = latin1}.\n" = t(RR2), RR3 = "rr(" ++ MS ++ ",'_'). #state{}.", ?line "[state]\n" - "#state{bin = undefined,reply = undefined,leader = undefined}.\n" = + "#state{bin = undefined,reply = undefined,leader = undefined,\n" + " unic = latin1}.\n" = t(RR3), RR4 = "rr(" ++ MS ++ ", '_', {d,test1}).", ?line [[state]] = scan(RR4), @@ -817,9 +820,6 @@ otp_5916(Config) when is_list(Config) -> true = if is_record(#r1{},r1,3) -> true; true -> false end, false = if is_record(#r2{},r1,3) -> true; true -> false end, - true = if {erlang,is_record}(#r1{},r1,3) -> true; true -> false end, - false = if {erlang,is_record}(#r2{},r1,3) -> true; true -> false end, - ok.">>, [ok] = scan(C), ok. @@ -2282,12 +2282,6 @@ otp_5990(doc) -> otp_5990(suite) -> []; otp_5990(Config) when is_list(Config) -> ?line [true] = - scan(<<"rd(foo,{bar}), {erlang,is_record}(#foo{}, foo).">>), - ?line [3] = - scan(<<"rd(foo,{bar}), A = #foo{}, " - "{if {erlang,is_record}(A, foo) -> erlang; " - "true -> not_a_module end, length}([1,2,3]).">>), - ?line [true] = scan(<<"rd('OrdSet', {orddata = {},ordtype = type}), " "S = #'OrdSet'{ordtype = {}}, " "if tuple(S#'OrdSet'.ordtype) -> true; true -> false end.">>), @@ -2549,19 +2543,6 @@ otp_6554(Config) when is_list(Config) -> ok. -otp_6785(doc) -> - "OTP-6785. Parameterized modules."; -otp_6785(suite) -> []; -otp_6785(Config) when is_list(Config) -> - MFile = filename:join(?config(priv_dir, Config), "parameterized.erl"), - Contents = <<"-module(parameterized, [A]). " - "-export([test/0]). " - "test() -> A. ">>, - ?line ok = compile_file(Config, MFile, Contents, []), - ?line (parameterized:new(adsf)):test(), - file:delete(MFile), - ok. - otp_7184(doc) -> "OTP-7184. Propagate exit signals from dying evaluator process."; otp_7184(suite) -> []; @@ -2757,6 +2738,149 @@ prompt_err(B) -> S = string:strip(S2, both, $"), string:strip(S, right, $.). +otp_10302(doc) -> + "OTP-10302. Unicode."; +otp_10302(suite) -> []; +otp_10302(Config) when is_list(Config) -> + {ok,Node} = start_node(shell_suite_helper_2, + "-pa "++?config(priv_dir,Config)++ + " +pc unicode"), + Test1 = + <<"begin + io:setopts([{encoding,utf8}]), + [1024] = \"\\x{400}\", + rd(rec, {a = \"\\x{400}\"}), + ok = rl(rec) + end.">>, + "-record(rec,{a = \"\x{400}\"}).\nok.\n" = t({Node,Test1}), + + Test3 = + <<"io:setopts([{encoding,utf8}]). + rd(rec, {a = \"\\x{400}\"}). + ok = rp(#rec{}).">>, + "ok.\nrec\n#rec{a = \"\x{400}\"}.\nok.\n" = t({Node,Test3}), + + Test4 = + <<"io:setopts([{encoding,utf8}]). + A = [1024] = \"\\x{400}\". + b(). + h().">>, + + "ok.\n\"\x{400}\"\nA = \"\x{400}\".\nok.\n" + "1: io:setopts([{encoding,utf8}])\n-> ok.\n" + "2: A = [1024] = \"\x{400}\"\n-> \"\x{400}\"\n" + "3: b()\n-> ok.\nok.\n" = t({Node,Test4}), + + Test5 = + <<"begin + io:setopts([{encoding,utf8}]), + results(0), + A = [1024] = \"\\x{400}\", + b(), + h() + end.">>, + "A = \"\x{400}\".\nok.\n" = t({Node,Test5}), + + %% One $" is "lost": + true = + "\x{400}\": command not found" =:= + prompt_err({Node, + <<"io:setopts([{encoding,utf8}]). v(\"\x{400}\")."/utf8>>, + unicode}), + + "ok.\ndefault\n* Bad prompt function: \"\x{400}\".\n" = + t({Node,<<"io:setopts([{encoding,utf8}]). " + "shell:prompt_func(\"\x{400}\")."/utf8>>, + unicode}), + rpc:call(Node,shell, prompt_func, [default]), + _ = shell:prompt_func(default), + + %% Test lib:format_exception() (cf. OTP-6554) + Test6 = + <<"begin + A = <<\"\\xaa\">>, + S = lists:flatten(io_lib:format(\"~p/~p.\", [A, A])), + {ok, Ts, _} = erl_scan:string(S, 1, [unicode]), + {ok, Es} = erl_parse:parse_exprs(Ts), + B = erl_eval:new_bindings(), + erl_eval:exprs(Es, B) + end.">>, + + "** exception error: an error occurred when evaluating" + " an arithmetic expression\n in operator '/'/2\n" + " called as <<\"\xaa\">> / <<\"\xaa\">>.\n" = t(Test6), + Test7 = + <<"io:setopts([{encoding,utf8}]). + A = <<\"\\xaa\">>, + S = lists:flatten(io_lib:format(\"~p/~p.\", [A, A])), + {ok, Ts, _} = erl_scan:string(S, 1, [unicode]), + {ok, Es} = erl_parse:parse_exprs(Ts), + B = erl_eval:new_bindings(), + erl_eval:exprs(Es, B).">>, + + "ok.\n** exception error: an error occurred when evaluating" + " an arithmetic expression\n in operator '/'/2\n" + " called as <<\"�\">> / <<\"�\">>.\n" = t({Node,Test7}), + Test8 = + <<"begin + A = [1089], + S = lists:flatten(io_lib:format(\"~tp/~tp.\", [A, A])), + {ok, Ts, _} = erl_scan:string(S, 1, [unicode]), + {ok, Es} = erl_parse:parse_exprs(Ts), + B = erl_eval:new_bindings(), + erl_eval:exprs(Es, B) + end.">>, + "** exception error: an error occurred when evaluating" + " an arithmetic expression\n in operator '/'/2\n" + " called as [1089] / [1089].\n" = t(Test8), + Test9 = + <<"io:setopts([{encoding,utf8}]). + A = [1089], + S = lists:flatten(io_lib:format(\"~tp/~tp.\", [A, A])), + {ok, Ts, _} = erl_scan:string(S, 1, [unicode]), + {ok, Es} = erl_parse:parse_exprs(Ts), + B = erl_eval:new_bindings(), + erl_eval:exprs(Es, B).">>, + + "ok.\n** exception error: an error occurred when evaluating" + " an arithmetic expression\n in operator '/'/2\n" + " called as \"\x{441}\" / \"\x{441}\".\n" = t({Node,Test9}), + Test10 = + <<"A = {\"1\\xaa\", + $\\xaa, + << <<\"hi\">>/binary >>, + <<\"1\xaa\">>}, + fun(a) -> true end(A).">>, + "** exception error: no function clause matching \n" + " erl_eval:'-inside-an-interpreted-fun-'" + "({\"1\xc2\xaa\",170,<<\"hi\">>,\n " + " <<\"1\xc2\xaa\">>}) .\n" = t(Test10), + Test11 = + <<"io:setopts([{encoding,utf8}]). + A = {\"1\\xaa\", + $\\xaa, + << <<\"hi\">>/binary >>, + <<\"1\xaa\">>}, + fun(a) -> true end(A).">>, + + "ok.\n** exception error: no function clause matching \n" + " erl_eval:'-inside-an-interpreted-fun-'" + "({\"1\xaa\",170,<<\"hi\">>,\n " + " <<\"1\xaa\"/utf8>>}) .\n" = t({Node,Test11}), + Test12 = <<"fun(a, b) -> false end(65, [1089]).">>, + "** exception error: no function clause matching \n" + " erl_eval:'-inside-an-interpreted-fun-'(65,[1089])" + " .\n" = t(Test12), + Test13 = + <<"io:setopts([{encoding,utf8}]). + fun(a, b) -> false end(65, [1089]).">>, + "ok.\n** exception error: no function clause matching \n" + " erl_eval:'-inside-an-interpreted-fun-'(65,\"\x{441}\")" + " .\n" = t({Node,Test13}), + + test_server:stop_node(Node), + ok. + scan(B) -> F = fun(Ts) -> case erl_parse:parse_term(Ts) of @@ -2770,37 +2894,46 @@ scan(B) -> scan(t(B), F). scan(S0, F) -> - case erl_scan:tokens([], S0, 1) of + case erl_scan:tokens([], S0, 1, [unicode]) of {done,{ok,Ts,_},S} -> [F(Ts) | scan(S, F)]; _Else -> [] end. +t({Node,Bin,Enc}) when is_atom(Node),is_binary(Bin), is_atom(Enc) -> + t0({Bin,Enc}, fun() -> start_new_shell(Node) end); t({Node,Bin}) when is_atom(Node),is_binary(Bin) -> - t0(Bin, fun() -> start_new_shell(Node) end); + t0({Bin,latin1}, fun() -> start_new_shell(Node) end); t(Bin) when is_binary(Bin) -> - t0(Bin, fun() -> start_new_shell() end); + t0({Bin,latin1}, fun() -> start_new_shell() end); +t({Bin,Enc}) when is_binary(Bin), is_atom(Enc) -> + t0({Bin,Enc}, fun() -> start_new_shell() end); t(L) -> t(list_to_binary(L)). -t0(Bin, F) -> +t0({Bin,Enc}, F) -> %% Spawn a process so that io_request messages do not interfer. P = self(), - C = spawn(fun() -> t1(P, Bin, F) end), + C = spawn(fun() -> t1(P, {Bin, Enc}, F) end), receive {C, R} -> R end. -t1(Parent, Bin, F) -> - %% io:format("*** Testing ~s~n", [binary_to_list(Bin)]), - S = #state{bin = Bin, reply = [], leader = group_leader()}, +t1(Parent, {Bin,Enc}, F) -> + io:format("*** Testing ~s~n", [binary_to_list(Bin)]), + S = #state{bin = Bin, unic = Enc, reply = [], leader = group_leader()}, group_leader(self(), self()), _Shell = F(), try server_loop(S) catch exit:R -> Parent ! {self(), R}; - throw:{?MODULE,LoopReply} -> + throw:{?MODULE,LoopReply,latin1} -> L0 = binary_to_list(list_to_binary(LoopReply)), [$\n | L1] = lists:dropwhile(fun(X) -> X =/= $\n end, L0), + Parent ! {self(), dotify(L1)}; + throw:{?MODULE,LoopReply,_Uni} -> + Tmp = unicode:characters_to_binary(LoopReply), + L0 = unicode:characters_to_list(Tmp), + [$\n | L1] = lists:dropwhile(fun(X) -> X =/= $\n end, L0), Parent ! {self(), dotify(L1)} after group_leader(S#state.leader, self()) end. @@ -2844,7 +2977,7 @@ do_io_request(Req, From, S, ReplyAs) -> case io_requests([Req], [], S) of {_Status,{eof,_},S1} -> io_reply(From, ReplyAs, {error,terminated}), - throw({?MODULE,S1#state.reply}); + throw({?MODULE,S1#state.reply,S1#state.unic}); {_Status,Reply,S1} -> io_reply(From, ReplyAs, Reply), S1 @@ -2867,13 +3000,34 @@ io_requests([], [Rs|Cont], S) -> io_requests([], [], S) -> {ok,ok,S}. +io_request({setopts, Opts}, S) -> + #state{unic = OldEnc, bin = Bin} = S, + NewEnc = case proplists:get_value(encoding, Opts) of + undefined -> OldEnc; + utf8 -> unicode; + New -> New + end, + NewBin = case {OldEnc, NewEnc} of + {E, E} -> Bin; + {latin1, _} -> + unicode:characters_to_binary(Bin, latin1, unicode); + {_, latin1} -> + unicode:characters_to_binary(Bin, unicode, latin1); + {_, _} -> Bin + end, + {ok, ok, S#state{unic = NewEnc, bin = NewBin}}; +io_request(getopts, S) -> + {ok,[{encoding,S#state.unic}],S}; io_request({get_geometry,columns}, S) -> {ok,80,S}; io_request({get_geometry,rows}, S) -> {ok,24,S}; io_request({put_chars,Chars}, S) -> {ok,ok,S#state{reply = [S#state.reply | Chars]}}; -io_request({put_chars,_,Chars}, S) -> +io_request({put_chars,latin1,Chars}, S) -> + {ok,ok,S#state{reply = [S#state.reply | Chars]}}; +io_request({put_chars,unicode,Chars0}, S) -> + Chars = unicode:characters_to_list(Chars0), {ok,ok,S#state{reply = [S#state.reply | Chars]}}; io_request({put_chars,Mod,Func,Args}, S) -> case catch apply(Mod, Func, Args) of @@ -2899,9 +3053,12 @@ get_until_loop(M, F, As, S, {more,Cont}, Enc) -> 0 -> get_until_loop(M, F, As, S, catch apply(M, F, [Cont,eof|As]), Enc); + _ when S#state.unic =:= latin1 -> + get_until_loop(M, F, As, S#state{bin = <<>>}, + catch apply(M, F, [Cont,binary_to_list(Bin)|As]), Enc); _ -> get_until_loop(M, F, As, S#state{bin = <<>>}, - catch apply(M, F, [Cont,binary_to_list(Bin)|As]), Enc) + catch apply(M, F, [Cont,unicode:characters_to_list(Bin)|As]), Enc) end; get_until_loop(_M, _F, _As, S, {done,Res,Buf}, Enc) -> {ok,Res,S#state{bin = buf2bin(Buf, Enc)}}; @@ -2912,6 +3069,8 @@ buf2bin(eof,_) -> <<>>; buf2bin(Buf,latin1) -> list_to_binary(Buf); +buf2bin(Buf,utf8) -> + unicode:characters_to_binary(Buf,unicode,unicode); buf2bin(Buf,unicode) -> unicode:characters_to_binary(Buf,unicode,unicode). diff --git a/lib/stdlib/test/stdlib.cover b/lib/stdlib/test/stdlib.cover index 61f4f064b9..e71be880cb 100644 --- a/lib/stdlib/test/stdlib.cover +++ b/lib/stdlib/test/stdlib.cover @@ -1,17 +1,2 @@ %% -*- erlang -*- {incl_app,stdlib,details}. - -{excl_mods,stdlib, - [erl_parse, - erl_eval, - ets, - filename, - gen_event, - gen_server, - gen, - lists, - io, - io_lib, - io_lib_format, - io_lib_pretty, - proc_lib]}. diff --git a/lib/stdlib/test/stdlib.spec.vxworks b/lib/stdlib/test/stdlib.spec.vxworks deleted file mode 100644 index ddc804b831..0000000000 --- a/lib/stdlib/test/stdlib.spec.vxworks +++ /dev/null @@ -1,8 +0,0 @@ -{topcase, {dir, "../stdlib_test"}}. -{skip,{dets_SUITE,"Not runnable VxWorks/NFS"}}. -{skip,{slave_SUITE,"VxWorks: slave nodes are not supported"}}. -{skip,{tar_SUITE,errors,"VxWorks filesystem too primitive"}}. -{skip,{tar_SUITE,create_long_names,"VxWorks names too short"}}. -{skip,{epp_SUITE,"Test not adopted to VxWorks"}}. -{skip,{select_SUITE,"Test too memory consuming for VxWorks"}}. -{skip,{beam_lib_SUITE,error,"All sections not present in stripped beam files"}}. diff --git a/lib/stdlib/test/string_SUITE.erl b/lib/stdlib/test/string_SUITE.erl index 6969c095a0..96e653985f 100644 --- a/lib/stdlib/test/string_SUITE.erl +++ b/lib/stdlib/test/string_SUITE.erl @@ -1,7 +1,8 @@ +%% -*- coding: utf-8 -*- %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2011. All Rights Reserved. +%% Copyright Ericsson AB 2004-2012. 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 @@ -473,8 +474,8 @@ to_upper_to_lower(suite) -> to_upper_to_lower(doc) -> []; to_upper_to_lower(Config) when is_list(Config) -> - ?line "1234ABCDEF���=" = string:to_upper("1234abcdef���="), - ?line "����������abc()" = string:to_lower("����������abc()"), + ?line "1234ABCDEFÅÄÖ=" = string:to_upper("1234abcdefåäö="), + ?line "éèíúùòóåäöabc()" = string:to_lower("ÉÈÍÚÙÒÓÅÄÖabc()"), ?line All = lists:seq(0, 255), ?line UC = string:to_upper(All), diff --git a/lib/stdlib/test/supervisor_SUITE.erl b/lib/stdlib/test/supervisor_SUITE.erl index 767ae3d62c..569c66959e 100644 --- a/lib/stdlib/test/supervisor_SUITE.erl +++ b/lib/stdlib/test/supervisor_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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,6 +46,7 @@ temporary_normal/1, permanent_shutdown/1, transient_shutdown/1, temporary_shutdown/1, + faulty_application_shutdown/1, permanent_abnormal/1, transient_abnormal/1, temporary_abnormal/1, temporary_bystander/1]). @@ -98,7 +99,8 @@ groups() -> {normal_termination, [], [permanent_normal, transient_normal, temporary_normal]}, {shutdown_termination, [], - [permanent_shutdown, transient_shutdown, temporary_shutdown]}, + [permanent_shutdown, transient_shutdown, temporary_shutdown, + faulty_application_shutdown]}, {abnormal_termination, [], [permanent_abnormal, transient_abnormal, temporary_abnormal]}, @@ -659,6 +661,39 @@ temporary_shutdown(Config) when is_list(Config) -> [0,0,0,0] = get_child_counts(sup_test). %%------------------------------------------------------------------------- +%% Faulty application should shutdown and pass on errors +faulty_application_shutdown(Config) when is_list(Config) -> + + %% Set some paths + AppDir = filename:join(?config(data_dir, Config), "app_faulty"), + EbinDir = filename:join(AppDir, "ebin"), + + %% Start faulty app + code:add_patha(EbinDir), + + %% {error, + %% {{shutdown, + %% {failed_to_start_child, + %% app_faulty, + %% {undef, + %% [{an_undefined_module_with,an_undefined_function,[argument1,argument2], + %% []}, + %% {app_faulty_server,init,1, + %% [{file,"app_faulty/src/app_faulty_server.erl"},{line,16}]}, + %% {gen_server,init_it,6, + %% [{file,"gen_server.erl"},{line,304}]}, + %% {proc_lib,init_p_do_apply,3, + %% [{file,"proc_lib.erl"},{line,227}]}]}}}, + %% {app_faulty,start,[normal,[]]}}} + + {error, Error} = application:start(app_faulty), + {{shutdown, {failed_to_start_child,app_faulty,{undef, CallStack}}}, + {app_faulty,start,_}} = Error, + [{an_undefined_module_with,an_undefined_function,_,_}|_] = CallStack, + ok = application:unload(app_faulty), + ok. + +%%------------------------------------------------------------------------- %% A permanent child should always be restarted. permanent_abnormal(Config) when is_list(Config) -> {ok, SupPid} = start_link({ok, {{one_for_one, 2, 3600}, []}}), diff --git a/lib/stdlib/test/supervisor_SUITE_data/Makefile.src b/lib/stdlib/test/supervisor_SUITE_data/Makefile.src new file mode 100644 index 0000000000..dbc5729f47 --- /dev/null +++ b/lib/stdlib/test/supervisor_SUITE_data/Makefile.src @@ -0,0 +1,15 @@ +EFLAGS=+debug_info + +APP_FAULTY= \ + app_faulty/ebin/app_faulty_sup.@EMULATOR@ \ + app_faulty/ebin/app_faulty_server.@EMULATOR@ \ + app_faulty/ebin/app_faulty.@EMULATOR@ \ + +all: $(APP_FAULTY) + +app_faulty/ebin/app_faulty_server.@EMULATOR@: app_faulty/src/app_faulty_server.erl + erlc $(EFLAGS) -oapp_faulty/ebin app_faulty/src/app_faulty_server.erl +app_faulty/ebin/app_faulty_sup.@EMULATOR@: app_faulty/src/app_faulty_sup.erl + erlc $(EFLAGS) -oapp_faulty/ebin app_faulty/src/app_faulty_sup.erl +app_faulty/ebin/app_faulty.@EMULATOR@: app_faulty/src/app_faulty.erl + erlc $(EFLAGS) -oapp_faulty/ebin app_faulty/src/app_faulty.erl diff --git a/lib/stdlib/test/supervisor_SUITE_data/app_faulty/ebin/app_faulty.app b/lib/stdlib/test/supervisor_SUITE_data/app_faulty/ebin/app_faulty.app new file mode 100644 index 0000000000..d4ab07e485 --- /dev/null +++ b/lib/stdlib/test/supervisor_SUITE_data/app_faulty/ebin/app_faulty.app @@ -0,0 +1,10 @@ +{application, app_faulty, + [{description, "very simple example faulty application"}, + {id, "app_faulty"}, + {vsn, "1.0"}, + {modules, [app_faulty, app_faulty_sup, app_faulty_server]}, + {registered, [app_faulty]}, + {applications, [kernel, stdlib]}, + {env, [{var,val1}]}, + {mod, {app_faulty, []}} + ]}. diff --git a/lib/stdlib/test/supervisor_SUITE_data/app_faulty/src/app_faulty.erl b/lib/stdlib/test/supervisor_SUITE_data/app_faulty/src/app_faulty.erl new file mode 100644 index 0000000000..c65b411cd6 --- /dev/null +++ b/lib/stdlib/test/supervisor_SUITE_data/app_faulty/src/app_faulty.erl @@ -0,0 +1,17 @@ +-module(app_faulty). + +-behaviour(application). + +%% Application callbacks +-export([start/2, stop/1]). + +start(_Type, _StartArgs) -> + case app_faulty_sup:start_link() of + {ok, Pid} -> + {ok, Pid}; + Error -> + Error + end. + +stop(_State) -> + ok. diff --git a/lib/stdlib/test/supervisor_SUITE_data/app_faulty/src/app_faulty_server.erl b/lib/stdlib/test/supervisor_SUITE_data/app_faulty/src/app_faulty_server.erl new file mode 100644 index 0000000000..6628f92210 --- /dev/null +++ b/lib/stdlib/test/supervisor_SUITE_data/app_faulty/src/app_faulty_server.erl @@ -0,0 +1,32 @@ +-module(app_faulty_server). + +-behaviour(gen_server). + +%% API +-export([start_link/0]). + +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). + +start_link() -> + gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). + +init([]) -> + an_undefined_module_with:an_undefined_function(argument1, argument2), + {ok, []}. + +handle_call(_Request, _From, State) -> + {reply, ok, State}. + +handle_cast(_Msg, State) -> + {noreply, State}. + +handle_info(_Info, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. diff --git a/lib/stdlib/test/supervisor_SUITE_data/app_faulty/src/app_faulty_sup.erl b/lib/stdlib/test/supervisor_SUITE_data/app_faulty/src/app_faulty_sup.erl new file mode 100644 index 0000000000..8115a88809 --- /dev/null +++ b/lib/stdlib/test/supervisor_SUITE_data/app_faulty/src/app_faulty_sup.erl @@ -0,0 +1,17 @@ +-module(app_faulty_sup). + +-behaviour(supervisor). + +%% API +-export([start_link/0]). + +%% Supervisor callbacks +-export([init/1]). + +start_link() -> + supervisor:start_link(?MODULE, []). + +init([]) -> + AChild = {app_faulty,{app_faulty_server,start_link,[]}, + permanent,2000,worker,[app_faulty_server]}, + {ok,{{one_for_all,0,1}, [AChild]}}. diff --git a/lib/stdlib/test/sys_SUITE.erl b/lib/stdlib/test/sys_SUITE.erl index fe039e8bcc..c06ba545e7 100644 --- a/lib/stdlib/test/sys_SUITE.erl +++ b/lib/stdlib/test/sys_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2013. 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 @@ -56,70 +56,60 @@ end_per_group(_GroupName, Config) -> log(suite) -> []; log(Config) when is_list(Config) -> - ?line {ok,_Server} = start(), - ?line ok = sys:log(?server,true), - ?line {ok,-44} = public_call(44), - ?line ok = sys:log(?server,false), - ?line ok = sys:log(?server,print), - ?line stop(), + {ok,_Server} = start(), + ok = sys:log(?server,true), + {ok,-44} = public_call(44), + ok = sys:log(?server,false), + ok = sys:log(?server,print), + stop(), ok. log_to_file(suite) -> []; log_to_file(Config) when is_list(Config) -> TempName = test_server:temp_name(?config(priv_dir,Config) ++ "sys."), - ?line {ok,_Server} = start(), - ?line ok = sys:log_to_file(?server,TempName), - ?line {ok,-44} = public_call(44), - ?line ok = sys:log_to_file(?server,false), - ?line {ok,Fd} = file:open(TempName,[read]), - ?line Msg1 = io:get_line(Fd,''), - ?line Msg2 = io:get_line(Fd,''), - ?line file:close(Fd), - ?line lists:prefix("*DBG* sys_SUITE_server got call {req,44} from ",Msg1), - ?line lists:prefix("*DBG* sys_SUITE_server sent {ok,-44} to ",Msg2), - ?line stop(), + {ok,_Server} = start(), + ok = sys:log_to_file(?server,TempName), + {ok,-44} = public_call(44), + ok = sys:log_to_file(?server,false), + {ok,Fd} = file:open(TempName,[read]), + Msg1 = io:get_line(Fd,''), + Msg2 = io:get_line(Fd,''), + file:close(Fd), + lists:prefix("*DBG* sys_SUITE_server got call {req,44} from ",Msg1), + lists:prefix("*DBG* sys_SUITE_server sent {ok,-44} to ",Msg2), + stop(), ok. stats(suite) -> []; stats(Config) when is_list(Config) -> - ?line Self = self(), - ?line {ok,_Server} = start(), - ?line ok = sys:statistics(?server,true), - ?line {ok,-44} = public_call(44), - ?line {ok,Stats} = sys:statistics(?server,get), - ?line lists:member({messages_in,1},Stats), - ?line lists:member({messages_out,1},Stats), - ?line ok = sys:statistics(?server,false), - ?line {status,_Pid,{module,_Mod},[_PDict,running,Self,_,_]} = + Self = self(), + {ok,_Server} = start(), + ok = sys:statistics(?server,true), + {ok,-44} = public_call(44), + {ok,Stats} = sys:statistics(?server,get), + lists:member({messages_in,1},Stats), + lists:member({messages_out,1},Stats), + ok = sys:statistics(?server,false), + {status,_Pid,{module,_Mod},[_PDict,running,Self,_,_]} = sys:get_status(?server), - ?line {ok,no_statistics} = sys:statistics(?server,get), - ?line stop(), + {ok,no_statistics} = sys:statistics(?server,get), + stop(), ok. trace(suite) -> []; trace(Config) when is_list(Config) -> - ?line {ok,_Server} = start(), - case os:type() of - vxworks -> - ?line test_server:sleep(20000); - _ -> - ?line test_server:sleep(2000) - end, - ?line test_server:capture_start(), - ?line sys:trace(?server,true), - ?line {ok,-44} = public_call(44), + {ok,_Server} = start(), + test_server:sleep(2000), + test_server:capture_start(), + sys:trace(?server,true), + {ok,-44} = public_call(44), %% ho, hum, allow for the io to reach us.. - case os:type() of - vxworks -> - ?line test_server:sleep(10000); - _ -> - ?line test_server:sleep(1000) - end, - ?line test_server:capture_stop(), - ?line [Msg1,Msg2] = test_server:capture_get(), - ?line lists:prefix("*DBG* sys_SUITE_server got call {req,44} from ",Msg1), - ?line lists:prefix("*DBG* sys_SUITE_server sent {ok,-44} to ",Msg2), - ?line stop(), + test_server:sleep(1000), + test_server:capture_stop(), + [Msg1,Msg2] = test_server:capture_get(), + lists:prefix("*DBG* sys_SUITE_server got call {req,44} from ",Msg1), + lists:prefix("*DBG* sys_SUITE_server sent {ok,-44} to ",Msg2), + stop(), ok. suspend(suite) -> []; diff --git a/lib/stdlib/test/timer_SUITE.erl b/lib/stdlib/test/timer_SUITE.erl index f84c72b0f8..bea2b3fb2a 100644 --- a/lib/stdlib/test/timer_SUITE.erl +++ b/lib/stdlib/test/timer_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2013. 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,6 @@ %% functions I guess. But I don't have time for that now. %% %% Expect it to run for at least 5-10 minutes! -%% Except for VxWorks of course, where a couple of hours is more apropriate... %% The main test case in this module is "do_big_test", which @@ -77,12 +76,7 @@ end_per_group(_GroupName, Config) -> do_big_test(TConfig) when is_list(TConfig) -> Dog = ?t:timetrap(?t:minutes(20)), Save = process_flag(trap_exit, true), - Result = case os:type() of - vxworks -> - big_test(10); - _ -> - big_test(200) - end, + Result = big_test(200), process_flag(trap_exit, Save), ?t:timetrap_cancel(Dog), report_result(Result). diff --git a/lib/stdlib/vsn.mk b/lib/stdlib/vsn.mk index 6524d83689..c1467697e3 100644 --- a/lib/stdlib/vsn.mk +++ b/lib/stdlib/vsn.mk @@ -1 +1 @@ -STDLIB_VSN = 1.18.3 +STDLIB_VSN = 1.19.1 |