diff options
Diffstat (limited to 'lib/stdlib')
214 files changed, 21950 insertions, 11742 deletions
diff --git a/lib/stdlib/doc/specs/.gitignore b/lib/stdlib/doc/specs/.gitignore new file mode 100644 index 0000000000..322eebcb06 --- /dev/null +++ b/lib/stdlib/doc/specs/.gitignore @@ -0,0 +1 @@ +specs_*.xml diff --git a/lib/stdlib/doc/src/Makefile b/lib/stdlib/doc/src/Makefile index 13b9b2ff18..16e0a86e3b 100644 --- a/lib/stdlib/doc/src/Makefile +++ b/lib/stdlib/doc/src/Makefile @@ -1,19 +1,19 @@ # # %CopyrightBegin% -# -# Copyright Ericsson AB 1997-2009. All Rights Reserved. -# +# +# Copyright Ericsson AB 1997-2011. 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% # include $(ERL_TOP)/make/target.mk @@ -40,6 +40,7 @@ XML_REF3_FILES = \ array.xml \ base64.xml \ beam_lib.xml \ + binary.xml \ c.xml \ calendar.xml \ dets.xml \ @@ -124,18 +125,24 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf +SPECS_FILES = $(XML_REF3_FILES:%.xml=$(SPECDIR)/specs_%.xml) + +TOP_SPECS_FILE = specs.xml + # ---------------------------------------------------- # FLAGS # ---------------------------------------------------- XML_FLAGS += +SPECS_FLAGS = -I../../include -I../../../kernel/include + # ---------------------------------------------------- # Targets # ---------------------------------------------------- $(HTMLDIR)/%.gif: %.gif $(INSTALL_DATA) $< $@ -docs: pdf html man +docs: man pdf html $(TOP_PDF_FILE): $(XML_FILES) @@ -154,8 +161,13 @@ clean clean_docs: rm -f $(MAN3DIR)/* rm -f $(MAN6DIR)/* rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo) + rm -f $(SPECDIR)/* rm -f errs core *~ +$(SPECDIR)/specs_erl_id_trans.xml: + escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \ + -o$(dir $@) -module erl_id_trans + # ---------------------------------------------------- # Release Target # ---------------------------------------------------- diff --git a/lib/stdlib/doc/src/array.xml b/lib/stdlib/doc/src/array.xml index 5c3ac6a2a9..a79fcd487e 100644 --- a/lib/stdlib/doc/src/array.xml +++ b/lib/stdlib/doc/src/array.xml @@ -3,7 +3,7 @@ <erlref> <header> <copyright> - <year>2007</year><year>2009</year> + <year>2007</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -82,19 +82,35 @@ the default value cannot be confused with the values of set entries.</p> %% allow accesses beyond the last set entry {'EXIT',{badarg,_}} = (catch array:set(18, true, A3)). {'EXIT',{badarg,_}} = (catch array:get(18, A3)).</pre></description> -<section><title>DATA TYPES</title><marker id="types"/> - -<taglist> -<tag><c>array()</c></tag> -<item><marker id="type-array"/> -<p>A functional, extendible array. The representation is - not documented and is subject to change without notice. Note that - arrays cannot be directly compared for equality.</p> -</item> -</taglist></section> +<datatypes> + <datatype> + <name><marker id="type-array">array()</marker></name> + <desc> + <p>A functional, extendible array. The representation is + not documented and is subject to change without notice. Note that + arrays cannot be directly compared for equality.</p> + </desc> + </datatype> + <datatype> + <name name="array_indx"/> + </datatype> + <datatype> + <name name="array_opts"/> + </datatype> + <datatype> + <name name="array_opt"/> + </datatype> + <datatype> + <name name="indx_pairs"/> + </datatype> + <datatype> + <name name="indx_pair"/> + </datatype> +</datatypes> + <funcs> <func> -<name>default(Array::array()) -> term()</name> +<name name="default" arity="1"/> <fsummary>Get the value used for uninitialized entries.</fsummary> <desc><marker id="default-1"/> @@ -104,7 +120,7 @@ the default value cannot be confused with the values of set entries.</p> <p><em>See also:</em> <seealso marker="#new-2">new/2</seealso>.</p> </desc></func> <func> -<name>fix(Array::array()) -> array()</name> +<name name="fix" arity="1"/> <fsummary>Fix the size of the array.</fsummary> <desc><marker id="fix-1"/> @@ -114,105 +130,100 @@ the default value cannot be confused with the values of set entries.</p> <p><em>See also:</em> <seealso marker="#relax-1">relax/1</seealso>.</p> </desc></func> <func> -<name>foldl(Function, InitialAcc::term(), Array::array()) -> term()</name> +<name name="foldl" arity="3"/> <fsummary>Fold the elements of the array using the given function and initial accumulator value.</fsummary> -<type> -<v>Function = (Index::integer(), Value::term(), Acc::term()) -> term()</v></type> <desc><marker id="foldl-3"/> - <p>Fold the elements of the array using the given function and initial accumulator value. The elements are visited in order from the - lowest index to the highest. If <c>Function</c> is not a function, the + lowest index to the highest. If <c><anno>Function</anno></c> is not a function, the call fails with reason <c>badarg</c>. </p> <p><em>See also:</em> <seealso marker="#foldr-3">foldr/3</seealso>, <seealso marker="#map-2">map/2</seealso>, <seealso marker="#sparse_foldl-3">sparse_foldl/3</seealso>.</p> </desc></func> <func> -<name>foldr(Function, InitialAcc::term(), Array::array()) -> term()</name> +<name name="foldr" arity="3"/> <fsummary>Fold the elements of the array right-to-left using the given function and initial accumulator value.</fsummary> -<type> -<v>Function = (Index::integer(), Value::term(), Acc::term()) -> term()</v></type> <desc><marker id="foldr-3"/> <p>Fold the elements of the array right-to-left using the given function and initial accumulator value. The elements are visited in - order from the highest index to the lowest. If <c>Function</c> is not a + order from the highest index to the lowest. If <c><anno>Function</anno></c> is not a function, the call fails with reason <c>badarg</c>. </p> <p><em>See also:</em> <seealso marker="#foldl-3">foldl/3</seealso>, <seealso marker="#map-2">map/2</seealso>.</p> </desc></func> <func> -<name>from_list(List::list()) -> array()</name> +<name name="from_list" arity="1"/> <fsummary>Equivalent to from_list(List, undefined). </fsummary> <desc><marker id="from_list-1"/> -<p>Equivalent to <seealso marker="#from_list-2">from_list(List, undefined)</seealso>.</p> +<p>Equivalent to <seealso marker="#from_list-2">from_list(<anno>List</anno>, undefined)</seealso>.</p> </desc></func> <func> -<name>from_list(List::list(), Default::term()) -> array()</name> +<name name="from_list" arity="2"/> <fsummary>Convert a list to an extendible array.</fsummary> <desc><marker id="from_list-2"/> -<p>Convert a list to an extendible array. <c>Default</c> is used as the value - for uninitialized entries of the array. If <c>List</c> is not a proper list, +<p>Convert a list to an extendible array. <c><anno>Default</anno></c> is used as the value + for uninitialized entries of the array. If <c><anno>List</anno></c> is not a proper list, the call fails with reason <c>badarg</c>. </p> <p><em>See also:</em> <seealso marker="#new-2">new/2</seealso>, <seealso marker="#to_list-1">to_list/1</seealso>.</p> </desc></func> <func> -<name>from_orddict(Orddict::list()) -> array()</name> +<name name="from_orddict" arity="1"/> <fsummary>Equivalent to from_orddict(Orddict, undefined). </fsummary> <desc><marker id="from_orddict-1"/> -<p>Equivalent to <seealso marker="#from_orddict-2">from_orddict(Orddict, undefined)</seealso>.</p> +<p>Equivalent to <seealso marker="#from_orddict-2">from_orddict(<anno>Orddict</anno>, undefined)</seealso>.</p> </desc></func> <func> -<name>from_orddict(List::list(), Default::term()) -> array()</name> +<name name="from_orddict" arity="2"/> <fsummary>Convert an ordered list of pairs {Index, Value} to a corresponding extendible array.</fsummary> <desc><marker id="from_orddict-2"/> <p>Convert an ordered list of pairs <c>{Index, Value}</c> to a - corresponding extendible array. <c>Default</c> is used as the value for - uninitialized entries of the array. If <c>List</c> is not a proper, + corresponding extendible array. <c><anno>Default</anno></c> is used as the value for + uninitialized entries of the array. If <c><anno>Orddict</anno></c> is not a proper, ordered list of pairs whose first elements are nonnegative integers, the call fails with reason <c>badarg</c>. </p> <p><em>See also:</em> <seealso marker="#new-2">new/2</seealso>, <seealso marker="#to_orddict-1">to_orddict/1</seealso>.</p> </desc></func> <func> -<name>get(I::integer(), Array::array()) -> term()</name> +<name name="get" arity="2"/> <fsummary>Get the value of entry I.</fsummary> <desc><marker id="get-2"/> -<p>Get the value of entry <c>I</c>. If <c>I</c> is not a nonnegative - integer, or if the array has fixed size and <c>I</c> is larger than the +<p>Get the value of entry <c><anno>I</anno></c>. If <c><anno>I</anno></c> is not a nonnegative + integer, or if the array has fixed size and <c><anno>I</anno></c> is larger than the maximum index, the call fails with reason <c>badarg</c>.</p> <p>If the array does not have fixed size, this function will return the - default value for any index <c>I</c> greater than <c>size(Array)-1</c>.</p> + default value for any index <c><anno>I</anno></c> greater than <c>size(<anno>Array</anno>)-1</c>.</p> <p><em>See also:</em> <seealso marker="#set-3">set/3</seealso>.</p> </desc></func> <func> -<name>is_array(X::term()) -> bool()</name> +<name name="is_array" arity="1"/> <fsummary>Returns true if X appears to be an array, otherwise false.</fsummary> <desc><marker id="is_array-1"/> -<p>Returns <c>true</c> if <c>X</c> appears to be an array, otherwise <c>false</c>. - Note that the check is only shallow; there is no guarantee that <c>X</c> +<p>Returns <c>true</c> if <c><anno>X</anno></c> appears to be an array, otherwise <c>false</c>. + Note that the check is only shallow; there is no guarantee that <c><anno>X</anno></c> is a well-formed array representation even if this function returns <c>true</c>.</p> </desc></func> <func> -<name>is_fix(Array::array()) -> bool()</name> +<name name="is_fix" arity="1"/> <fsummary>Check if the array has fixed size.</fsummary> <desc><marker id="is_fix-1"/> @@ -222,20 +233,18 @@ the default value cannot be confused with the values of set entries.</p> <p><em>See also:</em> <seealso marker="#fix-1">fix/1</seealso>.</p> </desc></func> <func> -<name>map(Function, Array::array()) -> array()</name> +<name name="map" arity="2"/> <fsummary>Map the given function onto each element of the array.</fsummary> -<type> -<v>Function = (Index::integer(), Value::term()) -> term()</v></type> <desc><marker id="map-2"/> <p>Map the given function onto each element of the array. The elements are visited in order from the lowest index to the highest. - If <c>Function</c> is not a function, the call fails with reason <c>badarg</c>. + If <c><anno>Function</anno></c> is not a function, the call fails with reason <c>badarg</c>. </p> <p><em>See also:</em> <seealso marker="#foldl-3">foldl/3</seealso>, <seealso marker="#foldr-3">foldr/3</seealso>, <seealso marker="#sparse_map-2">sparse_map/2</seealso>.</p> </desc></func> <func> -<name>new() -> array()</name> +<name name="new" arity="0"/> <fsummary>Create a new, extendible array with initial size zero.</fsummary> <desc><marker id="new-0"/> @@ -244,7 +253,7 @@ the default value cannot be confused with the values of set entries.</p> <p><em>See also:</em> <seealso marker="#new-1">new/1</seealso>, <seealso marker="#new-2">new/2</seealso>.</p> </desc></func> <func> -<name>new(Options::term()) -> array()</name> +<name name="new" arity="1"/> <fsummary>Create a new array according to the given options.</fsummary> <desc><marker id="new-1"/> @@ -253,10 +262,10 @@ the default value cannot be confused with the values of set entries.</p> the array is extendible and has initial size zero. Array indices start at 0.</p> - <p><c>Options</c> is a single term or a list of terms, selected from the + <p><c><anno>Options</anno></c> is a single term or a list of terms, selected from the following: </p><taglist> - <tag><c>N::integer()</c> or <c>{size, N::integer()}</c></tag> + <tag><c>N::integer() >= 0</c> or <c>{size, N::integer() >= 0}</c></tag> <item><p>Specifies the initial size of the array; this also implies <c>{fixed, true}</c>. If <c>N</c> is not a nonnegative integer, the call fails with reason <c>badarg</c>.</p></item> @@ -283,19 +292,19 @@ cannot be changed once the array has been created.</p> <p><em>See also:</em> <seealso marker="#fix-1">fix/1</seealso>, <seealso marker="#from_list-2">from_list/2</seealso>, <seealso marker="#get-2">get/2</seealso>, <seealso marker="#new-0">new/0</seealso>, <seealso marker="#new-2">new/2</seealso>, <seealso marker="#set-3">set/3</seealso>.</p> </desc></func> <func> -<name>new(Size::integer(), Options::term()) -> array()</name> +<name name="new" arity="2"/> <fsummary>Create a new array according to the given size and options.</fsummary> <desc><marker id="new-2"/> <p>Create a new array according to the given size and options. If - <c>Size</c> is not a nonnegative integer, the call fails with reason + <c><anno>Size</anno></c> is not a nonnegative integer, the call fails with reason <c>badarg</c>. By default, the array has fixed size. Note that any size - specifications in <c>Options</c> will override the <c>Size</c> parameter.</p> + specifications in <c><anno>Options</anno></c> will override the <c><anno>Size</anno></c> parameter.</p> - <p>If <c>Options</c> is a list, this is simply equivalent to <c>new([{size, - Size} | Options]</c>, otherwise it is equivalent to <c>new([{size, Size} | - [Options]]</c>. However, using this function directly is more efficient.</p> + <p>If <c><anno>Options</anno></c> is a list, this is simply equivalent to <c>new([{size, + <anno>Size</anno>} | <anno>Options</anno>]</c>, otherwise it is equivalent to <c>new([{size, <anno>Size</anno>} | + [<anno>Options</anno>]]</c>. However, using this function directly is more efficient.</p> <p>Example: </p><pre> array:new(100, {default,0})</pre><p> creates a fixed-size array of size @@ -304,7 +313,7 @@ cannot be changed once the array has been created.</p> <p><em>See also:</em> <seealso marker="#new-1">new/1</seealso>.</p> </desc></func> <func> -<name>relax(Array::array()) -> array()</name> +<name name="relax" arity="1"/> <fsummary>Make the array resizable.</fsummary> <desc><marker id="relax-1"/> @@ -313,24 +322,24 @@ cannot be changed once the array has been created.</p> <p><em>See also:</em> <seealso marker="#fix-1">fix/1</seealso>.</p> </desc></func> <func> -<name>reset(I::integer(), Array::array()) -> array()</name> +<name name="reset" arity="2"/> <fsummary>Reset entry I to the default value for the array.</fsummary> <desc><marker id="reset-2"/> -<p>Reset entry <c>I</c> to the default value for the array. - If the value of entry <c>I</c> is the default value the array will be +<p>Reset entry <c><anno>I</anno></c> to the default value for the array. + If the value of entry <c><anno>I</anno></c> is the default value the array will be returned unchanged. Reset will never change size of the array. Shrinking can be done explicitly by calling <seealso marker="#resize-2">resize/2</seealso>.</p> - <p>If <c>I</c> is not a nonnegative integer, or if the array has fixed size - and <c>I</c> is larger than the maximum index, the call fails with reason + <p>If <c><anno>I</anno></c> is not a nonnegative integer, or if the array has fixed size + and <c><anno>I</anno></c> is larger than the maximum index, the call fails with reason <c>badarg</c>; cf. <seealso marker="#set-3">set/3</seealso> </p> <p><em>See also:</em> <seealso marker="#new-2">new/2</seealso>, <seealso marker="#set-3">set/3</seealso>.</p> </desc></func> <func> -<name>resize(Array::array()) -> array()</name> +<name name="resize" arity="1"/> <fsummary>Change the size of the array to that reported by sparse_size/1.</fsummary> <desc><marker id="resize-1"/> @@ -340,90 +349,84 @@ cannot be changed once the array has been created.</p> <p><em>See also:</em> <seealso marker="#resize-2">resize/2</seealso>, <seealso marker="#sparse_size-1">sparse_size/1</seealso>.</p> </desc></func> <func> -<name>resize(Size::integer(), Array::array()) -> array()</name> +<name name="resize" arity="2"/> <fsummary>Change the size of the array.</fsummary> <desc><marker id="resize-2"/> -<p>Change the size of the array. If <c>Size</c> is not a nonnegative +<p>Change the size of the array. If <c><anno>Size</anno></c> is not a nonnegative integer, the call fails with reason <c>badarg</c>. If the given array has fixed size, the resulting array will also have fixed size.</p> </desc></func> <func> -<name>set(I::integer(), Value::term(), Array::array()) -> array()</name> +<name name="set" arity="3"/> <fsummary>Set entry I of the array to Value.</fsummary> <desc><marker id="set-3"/> -<p>Set entry <c>I</c> of the array to <c>Value</c>. If <c>I</c> is not a - nonnegative integer, or if the array has fixed size and <c>I</c> is larger +<p>Set entry <c><anno>I</anno></c> of the array to <c><anno>Value</anno></c>. If <c><anno>I</anno></c> is not a + nonnegative integer, or if the array has fixed size and <c><anno>I</anno></c> is larger than the maximum index, the call fails with reason <c>badarg</c>.</p> - <p>If the array does not have fixed size, and <c>I</c> is greater than - <c>size(Array)-1</c>, the array will grow to size <c>I+1</c>. + <p>If the array does not have fixed size, and <c><anno>I</anno></c> is greater than + <c>size(<anno>Array</anno>)-1</c>, the array will grow to size <c><anno>I</anno>+1</c>. </p> <p><em>See also:</em> <seealso marker="#get-2">get/2</seealso>, <seealso marker="#reset-2">reset/2</seealso>.</p> </desc></func> <func> -<name>size(Array::array()) -> integer()</name> +<name name="size" arity="1"/> <fsummary>Get the number of entries in the array.</fsummary> <desc><marker id="size-1"/> <p>Get the number of entries in the array. Entries are numbered - from 0 to <c>size(Array)-1</c>; hence, this is also the index of the first + from 0 to <c>size(<anno>Array</anno>)-1</c>; hence, this is also the index of the first entry that is guaranteed to not have been previously set.</p> <p><em>See also:</em> <seealso marker="#set-3">set/3</seealso>, <seealso marker="#sparse_size-1">sparse_size/1</seealso>.</p> </desc></func> <func> -<name>sparse_foldl(Function, InitialAcc::term(), Array::array()) -> term()</name> +<name name="sparse_foldl" arity="3"/> <fsummary>Fold the elements of the array using the given function and initial accumulator value, skipping default-valued entries.</fsummary> -<type> -<v>Function = (Index::integer(), Value::term(), Acc::term()) -> term()</v></type> <desc><marker id="sparse_foldl-3"/> <p>Fold the elements of the array using the given function and initial accumulator value, skipping default-valued entries. The elements are visited in order from the lowest index to the highest. - If <c>Function</c> is not a function, the call fails with reason <c>badarg</c>. + If <c><anno>Function</anno></c> is not a function, the call fails with reason <c>badarg</c>. </p> <p><em>See also:</em> <seealso marker="#foldl-3">foldl/3</seealso>, <seealso marker="#sparse_foldr-3">sparse_foldr/3</seealso>.</p> </desc></func> <func> -<name>sparse_foldr(Function, InitialAcc::term(), Array::array()) -> term()</name> +<name name="sparse_foldr" arity="3"/> <fsummary>Fold the elements of the array right-to-left using the given function and initial accumulator value, skipping default-valued entries.</fsummary> -<type> -<v>Function = (Index::integer(), Value::term(), Acc::term()) -> term()</v></type> <desc><marker id="sparse_foldr-3"/> <p>Fold the elements of the array right-to-left using the given function and initial accumulator value, skipping default-valued entries. The elements are visited in order from the highest index to - the lowest. If <c>Function</c> is not a function, the call fails with + the lowest. If <c><anno>Function</anno></c> is not a function, the call fails with reason <c>badarg</c>. </p> <p><em>See also:</em> <seealso marker="#foldr-3">foldr/3</seealso>, <seealso marker="#sparse_foldl-3">sparse_foldl/3</seealso>.</p> </desc></func> <func> -<name>sparse_map(Function, Array::array()) -> array()</name> +<name name="sparse_map" arity="2"/> <fsummary>Map the given function onto each element of the array, skipping default-valued entries.</fsummary> -<type> -<v>Function = (Index::integer(), Value::term()) -> term()</v></type> <desc><marker id="sparse_map-2"/> <p>Map the given function onto each element of the array, skipping default-valued entries. The elements are visited in order from the - lowest index to the highest. If <c>Function</c> is not a function, the + lowest index to the highest. If <c><anno>Function</anno></c> is not a function, the call fails with reason <c>badarg</c>. </p> <p><em>See also:</em> <seealso marker="#map-2">map/2</seealso>.</p> </desc></func> <func> -<name>sparse_size(A::array()) -> integer()</name> +<name name="sparse_size" arity="1"/> <fsummary>Get the number of entries in the array up until the last non-default valued entry.</fsummary> @@ -436,7 +439,7 @@ cannot be changed once the array has been created.</p> <p><em>See also:</em> <seealso marker="#resize-1">resize/1</seealso>, <seealso marker="#size-1">size/1</seealso>.</p> </desc></func> <func> -<name>sparse_to_list(Array::array()) -> list()</name> +<name name="sparse_to_list" arity="1"/> <fsummary>Converts the array to a list, skipping default-valued entries.</fsummary> <desc><marker id="sparse_to_list-1"/> @@ -446,7 +449,7 @@ cannot be changed once the array has been created.</p> <p><em>See also:</em> <seealso marker="#to_list-1">to_list/1</seealso>.</p> </desc></func> <func> -<name>sparse_to_orddict(Array::array()) -> [{Index::integer(), Value::term()}]</name> +<name name="sparse_to_orddict" arity="1"/> <fsummary>Convert the array to an ordered list of pairs {Index, Value}, skipping default-valued entries.</fsummary> @@ -458,7 +461,7 @@ cannot be changed once the array has been created.</p> <p><em>See also:</em> <seealso marker="#to_orddict-1">to_orddict/1</seealso>.</p> </desc></func> <func> -<name>to_list(Array::array()) -> list()</name> +<name name="to_list" arity="1"/> <fsummary>Converts the array to a list.</fsummary> <desc><marker id="to_list-1"/> @@ -468,7 +471,7 @@ cannot be changed once the array has been created.</p> <p><em>See also:</em> <seealso marker="#from_list-2">from_list/2</seealso>, <seealso marker="#sparse_to_list-1">sparse_to_list/1</seealso>.</p> </desc></func> <func> -<name>to_orddict(Array::array()) -> [{Index::integer(), Value::term()}]</name> +<name name="to_orddict" arity="1"/> <fsummary>Convert the array to an ordered list of pairs {Index, Value}.</fsummary> <desc><marker id="to_orddict-1"/> diff --git a/lib/stdlib/doc/src/base64.xml b/lib/stdlib/doc/src/base64.xml index d3fd7a843b..bfe8494a73 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>2009</year> + <year>2007</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -33,32 +33,33 @@ <description> <p>Implements base 64 encode and decode, see RFC2045. </p> </description> + <datatypes> + <datatype> + <name name="ascii_string"/> + </datatype> + </datatypes> <funcs> <func> - <name>encode(Data) -> Base64 </name> - <name>encode_to_string(Data) -> Base64String</name> + <name name="encode" arity="1"/> + <name name="encode_to_string" arity="1"/> <fsummary>Encodes data into base64. </fsummary> - <type> - <v>Data = string() | binary()</v> - <v>Base64 = binary()</v> - <v>Base64String = string()</v> - </type> + <type variable="Data"/> + <type variable="Base64" name_i="1"/> + <type variable="Base64String"/> <desc> <p>Encodes a plain ASCII string into base64. The result will be 33% larger than the data.</p> </desc> </func> <func> - <name>decode(Base64) -> Data</name> - <name>decode_to_string(Base64) -> DataString</name> - <name>mime_decode(Base64) -> Data</name> - <name>mime_decode_to_string(Base64) -> DataString</name> + <name name="decode" arity="1"/> + <name name="decode_to_string" arity="1"/> + <name name="mime_decode" arity="1"/> + <name name="mime_decode_to_string" arity="1"/> <fsummary>Decodes a base64 encoded string to data. </fsummary> - <type> - <v>Base64 = string() | binary()</v> - <v>Data = binary()</v> - <v>DataString = string()</v> - </type> + <type variable="Base64" name_i="1"/> + <type variable="Data" name_i="1"/> + <type variable="DataString" name_i="2"/> <desc> <p>Decodes a base64 encoded string to plain ASCII. See RFC4648. <c>mime_decode/1</c> and <c>mime_decode_to_string/1</c> diff --git a/lib/stdlib/doc/src/beam_lib.xml b/lib/stdlib/doc/src/beam_lib.xml index b9286f1402..013e94c393 100644 --- a/lib/stdlib/doc/src/beam_lib.xml +++ b/lib/stdlib/doc/src/beam_lib.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2000</year><year>2010</year> + <year>2000</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -154,70 +154,78 @@ </section> </section> - <section> - <title>DATA TYPES</title> - <code type="none"> -beam() -> Module | Filename | binary() - Module = atom() - Filename = string() | atom()</code> - <p>Each of the functions described below accept either the module - name, the filename, or a binary containing the beam module.</p> - <code type="none"> -chunkdata() = {ChunkId, DataB} | {ChunkName, DataT} - ChunkId = chunkid() - DataB = binary() - {ChunkName, DataT} = - {abstract_code, AbstractCode} - | {attributes, [{Attribute, [AttributeValue]}]} - | {compile_info, [{InfoKey, [InfoValue]}]} - | {exports, [{Function, Arity}]} - | {labeled_exports, [{Function, Arity, Label}]} - | {imports, [{Module, Function, Arity}]} - | {indexed_imports, [{Index, Module, Function, Arity}]} - | {locals, [{Function, Arity}]}]} - | {labeled_locals, [{Function, Arity, Label}]}]} - | {atoms, [{integer(), atom()}]} - AbstractCode = {AbstVersion, Forms} | no_abstract_code - AbstVersion = atom() - Attribute = atom() - AttributeValue = term() - Module = Function = atom() - Arity = int() - Label = int()</code> - <p>It is not checked that the forms conform to the abstract format - indicated by <c>AbstVersion</c>. <c>no_abstract_code</c> means - that the <c>"Abst"</c> chunk is present, but empty.</p> - <p>The list of attributes is sorted on <c>Attribute</c>, and each - attribute name occurs once in the list. The attribute values - occur in the same order as in the file. The lists of functions - are also sorted.</p> - <code type="none"> -chunkid() = "Abst" | "Attr" | "CInf" - | "ExpT" | "ImpT" | "LocT" - | "Atom" + <datatypes> + <datatype> + <name name="beam"/> + <desc> + <p>Each of the functions described below accept either the + module name, the filename, or a binary containing the beam + module.</p> + </desc> + </datatype> + <datatype> + <name name="chunkdata"/> + <desc> + <p>The list of attributes is sorted on <c>Attribute</c> + (in attrib_entry()), and each + attribute name occurs once in the list. The attribute values + occur in the same order as in the file. The lists of functions + are also sorted.</p> + </desc> + </datatype> + <datatype> + <name name="chunkid"/> + <desc> + <p>"Abst" | "Attr" | "CInf" | "ExpT" | "ImpT" | "LocT" | "Atom"</p> + </desc> + </datatype> + <datatype> + <name name="dataB"/> + </datatype> + <datatype> + <name name="abst_code"/> + <desc> + <p>It is not checked that the forms conform to the abstract format + indicated by <c><anno>AbstVersion</anno></c>. <c>no_abstract_code</c> means + that the <c>"Abst"</c> chunk is present, but empty.</p> + </desc> + </datatype> + <datatype> + <name name="forms"/> + </datatype> + <datatype> + <name name="compinfo_entry"/> + </datatype> + <datatype> + <name name="attrib_entry"/> + </datatype> + <datatype> + <name name="labeled_entry"/> + </datatype> + <datatype> + <name name="index"/> + </datatype> + <datatype> + <name name="label"/> + </datatype> + <datatype> + <name name="chunkref"/> + </datatype> + <datatype> + <name name="chunkname"/> + </datatype> + <datatype> + <name name="chnk_rsn"/> + </datatype> + <datatype> + <name name="info_rsn"/> + </datatype> + </datatypes> -chunkname() = abstract_code | attributes | compile_info - | exports | labeled_exports - | imports | indexed_imports - | locals | labeled_locals - | atoms - -chunkref() = chunkname() | chunkid()</code> - </section> <funcs> <func> - <name>chunks(Beam, [ChunkRef]) -> {ok, {Module, [ChunkData]}} | {error, beam_lib, Reason}</name> + <name name="chunks" arity="2"/> <fsummary>Read selected chunks from a BEAM file or binary</fsummary> - <type> - <v>Beam = beam()</v> - <v>ChunkRef = chunkref()</v> - <v>Module = atom()</v> - <v>ChunkData = chunkdata()</v> - <v>Reason = {unknown_chunk, Filename, atom()}</v> - <v> | {key_missing_or_invalid, Filename, abstract_code}</v> - <v> | Reason1 -- see info/1</v> - <v> Filename = string()</v> - </type> <desc> <p>Reads chunk data for selected chunks refs. The order of the returned list of chunk data is determined by the order @@ -225,43 +233,26 @@ chunkref() = chunkname() | chunkid()</code> </desc> </func> <func> - <name>chunks(Beam, [ChunkRef], [Option]) -> {ok, {Module, [ChunkResult]}} | {error, beam_lib, Reason}</name> + <name name="chunks" arity="3"/> <fsummary>Read selected chunks from a BEAM file or binary</fsummary> - <type> - <v>Beam = beam()</v> - <v>ChunkRef = chunkref()</v> - <v>Module = atom()</v> - <v>Option = allow_missing_chunks</v> - <v>ChunkResult = {chunkref(), ChunkContents} | {chunkref(), missing_chunk}</v> - <v>Reason = {missing_chunk, Filename, atom()}</v> - <v> | {key_missing_or_invalid, Filename, abstract_code}</v> - <v> | Reason1 -- see info/1</v> - <v> Filename = string()</v> - </type> <desc> <p>Reads chunk data for selected chunks refs. The order of the returned list of chunk data is determined by the order of the list of chunks references.</p> - <p>By default, if any requested chunk is missing in <c>Beam</c>, + <p>By default, if any requested chunk is missing in <c><anno>Beam</anno></c>, an <c>error</c> tuple is returned. However, if the option <c>allow_missing_chunks</c> has been given, a result will be returned even if chunks are missing. In the result list, any missing chunks will be represented as - <c>{ChunkRef,missing_chunk}</c>. + <c>{<anno>ChunkRef</anno>,missing_chunk}</c>. Note, however, that if the <c>"Atom"</c> chunk if missing, that is considered a fatal error and the return value will be an <c>error</c> tuple.</p> </desc> </func> <func> - <name>version(Beam) -> {ok, {Module, [Version]}} | {error, beam_lib, Reason}</name> + <name name="version" arity="1"/> <fsummary>Read the BEAM file's module version</fsummary> - <type> - <v>Beam = beam()</v> - <v>Module = atom()</v> - <v>Version = term()</v> - <v>Reason -- see chunks/2</v> - </type> <desc> <p>Returns the module version(s). A version is defined by the module attribute <c>-vsn(Vsn)</c>. If this attribute is @@ -282,51 +273,30 @@ chunkref() = chunkname() | chunkid()</code> </desc> </func> <func> - <name>md5(Beam) -> {ok, {Module, MD5}} | {error, beam_lib, Reason}</name> + <name name="md5" arity="1"/> <fsummary>Read the BEAM file's module version</fsummary> - <type> - <v>Beam = beam()</v> - <v>Module = atom()</v> - <v>MD5 = binary()</v> - <v>Reason -- see chunks/2</v> - </type> <desc> <p>Calculates an MD5 redundancy check for the code of the module (compilation date and other attributes are not included).</p> </desc> </func> <func> - <name>info(Beam) -> [{Item, Info}] | {error, beam_lib, Reason1}</name> + <name name="info" arity="1"/> <fsummary>Information about a BEAM file</fsummary> - <type> - <v>Beam = beam()</v> - <v>Item, Info -- see below</v> - <v>Reason1 = {chunk_too_big, Filename, ChunkId, ChunkSize, FileSize}</v> - <v> | {invalid_beam_file, Filename, Pos}</v> - <v> | {invalid_chunk, Filename, ChunkId}</v> - <v> | {missing_chunk, Filename, ChunkId}</v> - <v> | {not_a_beam_file, Filename}</v> - <v> | {file_error, Filename, Posix}</v> - <v> Filename = string()</v> - <v> ChunkId = chunkid()</v> - <v> ChunkSize = FileSize = int()</v> - <v> Pos = int()</v> - <v> Posix = posix() -- see file(3)</v> - </type> <desc> <p>Returns a list containing some information about a BEAM file as tuples <c>{Item, Info}</c>:</p> <taglist> - <tag><c>{file, Filename} | {binary, Binary}</c></tag> + <tag><c>{file, <anno>Filename</anno>} | {binary, <anno>Binary</anno>}</c></tag> <item> <p>The name (string) of the BEAM file, or the binary from which the information was extracted.</p> </item> - <tag><c>{module, Module}</c></tag> + <tag><c>{module, <anno>Module</anno>}</c></tag> <item> <p>The name (atom) of the module.</p> </item> - <tag><c>{chunks, [{ChunkId, Pos, Size}]}</c></tag> + <tag><c>{chunks, [{<anno>ChunkId</anno>, <anno>Pos</anno>, <anno>Size</anno>}]}</c></tag> <item> <p>For each chunk, the identifier (string) and the position and size of the chunk data, in bytes.</p> @@ -335,53 +305,36 @@ chunkref() = chunkname() | chunkid()</code> </desc> </func> <func> - <name>cmp(Beam1, Beam2) -> ok | {error, beam_lib, Reason}</name> + <name name="cmp" arity="2"/> <fsummary>Compare two BEAM files</fsummary> - <type> - <v>Beam1 = Beam2 = beam()</v> - <v>Reason = {modules_different, Module1, Module2}</v> - <v> | {chunks_different, ChunkId}</v> - <v> | Reason1 -- see info/1</v> - <v> Module1 = Module2 = atom()</v> - <v> ChunkId = chunkid()</v> - </type> + <type name="cmp_rsn"/> <desc> <p>Compares the contents of two BEAM files. If the module names - are the same, and the chunks with the identifiers - <c>"Code"</c>, <c>"ExpT"</c>, <c>"ImpT"</c>, <c>"StrT"</c>, - and <c>"Atom"</c> have the same contents in both files, + are the same, and all chunks except for the <c>"CInf"</c> chunk + (the chunk containing the compilation information which is + returned by <c>Module:module_info(compile)</c>) + have the same contents in both files, <c>ok</c> is returned. Otherwise an error message is returned.</p> </desc> </func> <func> - <name>cmp_dirs(Dir1, Dir2) -> {Only1, Only2, Different} | {error, beam_lib, Reason1}</name> + <name name="cmp_dirs" arity="2"/> <fsummary>Compare the BEAM files in two directories</fsummary> - <type> - <v>Dir1 = Dir2 = string() | atom()</v> - <v>Different = [{Filename1, Filename2}]</v> - <v>Only1 = Only2 = [Filename]</v> - <v>Filename = Filename1 = Filename2 = string()</v> - <v>Reason1 = {not_a_directory, term()} | -- see info/1</v> - </type> <desc> <p>The <c>cmp_dirs/2</c> function compares the BEAM files in two directories. Only files with extension <c>".beam"</c> are - compared. BEAM files that exist in directory <c>Dir1</c> - (<c>Dir2</c>) only are returned in <c>Only1</c> - (<c>Only2</c>). BEAM files that exist on both directories but + compared. BEAM files that exist in directory <c><anno>Dir1</anno></c> + (<c><anno>Dir2</anno></c>) only are returned in <c><anno>Only1</anno></c> + (<c><anno>Only2</anno></c>). BEAM files that exist on both directories but are considered different by <c>cmp/2</c> are returned as - pairs {<c>Filename1</c>, <c>Filename2</c>} where - <c>Filename1</c> (<c>Filename2</c>) exists in directory - <c>Dir1</c> (<c>Dir2</c>).</p> + pairs {<c><anno>Filename1</anno></c>, <c><anno>Filename2</anno></c>} where + <c><anno>Filename1</anno></c> (<c><anno>Filename2</anno></c>) exists in directory + <c><anno>Dir1</anno></c> (<c><anno>Dir2</anno></c>).</p> </desc> </func> <func> - <name>diff_dirs(Dir1, Dir2) -> ok | {error, beam_lib, Reason1}</name> + <name name="diff_dirs" arity="2"/> <fsummary>Compare the BEAM files in two directories</fsummary> - <type> - <v>Dir1 = Dir2 = string() | atom()</v> - <v>Reason1 = {not_a_directory, term()} | -- see info/1</v> - </type> <desc> <p>The <c>diff_dirs/2</c> function compares the BEAM files in two directories the way <c>cmp_dirs/2</c> does, but names of @@ -390,13 +343,8 @@ chunkref() = chunkname() | chunkid()</code> </desc> </func> <func> - <name>strip(Beam1) -> {ok, {Module, Beam2}} | {error, beam_lib, Reason1}</name> + <name name="strip" arity="1"/> <fsummary>Removes chunks not needed by the loader from a BEAM file</fsummary> - <type> - <v>Beam1 = Beam2 = beam()</v> - <v>Module = atom()</v> - <v>Reason1 -- see info/1</v> - </type> <desc> <p>The <c>strip/1</c> function removes all chunks from a BEAM file except those needed by the loader. In particular, @@ -404,15 +352,8 @@ chunkref() = chunkname() | chunkid()</code> </desc> </func> <func> - <name>strip_files(Files) -> {ok, [{Module, Beam2}]} | {error, beam_lib, Reason1}</name> + <name name="strip_files" arity="1"/> <fsummary>Removes chunks not needed by the loader from BEAM files</fsummary> - <type> - <v>Files = [Beam1]</v> - <v> Beam1 = beam()</v> - <v>Module = atom()</v> - <v>Beam2 = beam()</v> - <v>Reason1 -- see info/1</v> - </type> <desc> <p>The <c>strip_files/1</c> function removes all chunks except those needed by the loader from BEAM files. In particular, @@ -422,30 +363,20 @@ chunkref() = chunkname() | chunkid()</code> </desc> </func> <func> - <name>strip_release(Dir) -> {ok, [{Module, Filename]}} | {error, beam_lib, Reason1}</name> + <name name="strip_release" arity="1"/> <fsummary>Removes chunks not needed by the loader from all BEAM files of a release</fsummary> - <type> - <v>Dir = string() | atom()</v> - <v>Module = atom()</v> - <v>Filename = string()</v> - <v>Reason1 = {not_a_directory, term()} | -- see info/1</v> - </type> <desc> <p>The <c>strip_release/1</c> function removes all chunks except those needed by the loader from the BEAM files of a - release. <c>Dir</c> should be the installation root + release. <c><anno>Dir</anno></c> should be the installation root directory. For example, the current OTP release can be stripped with the call <c>beam_lib:strip_release(code:root_dir())</c>.</p> </desc> </func> <func> - <name>format_error(Reason) -> Chars</name> + <name name="format_error" arity="1"/> <fsummary>Return an English description of a BEAM read error reply</fsummary> - <type> - <v>Reason -- see other functions</v> - <v>Chars = [char() | Chars]</v> - </type> <desc> <p>Given the error returned by any function in this module, the function <c>format_error</c> returns a descriptive string @@ -454,12 +385,11 @@ chunkref() = chunkname() | chunkid()</code> </desc> </func> <func> - <name>crypto_key_fun(CryptoKeyFun) -> ok | {error, Reason}</name> + <name name="crypto_key_fun" arity="1"/> <fsummary>Register a fun that provides a crypto key</fsummary> - <type> - <v>CryptoKeyFun = fun() -- see below</v> - <v>Reason = badfun | exists | term()</v> - </type> + <type name="crypto_fun"/> + <type name="crypto_fun_arg"/> + <type name="mode"/> <desc> <p>The <c>crypto_key_fun/1</c> function registers a unary fun that will be called if <c>beam_lib</c> needs to read an @@ -493,11 +423,8 @@ chunkref() = chunkname() | chunkid()</code> </desc> </func> <func> - <name>clear_crypto_key_fun() -> {ok, Result}</name> + <name name="clear_crypto_key_fun" arity="0"/> <fsummary>Unregister the current crypto key fun</fsummary> - <type> - <v>Result = undefined | term()</v> - </type> <desc> <p>Unregisters the crypto key fun and terminates the process holding it, started by <c>crypto_key_fun/1</c>.</p> diff --git a/lib/stdlib/doc/src/binary.xml b/lib/stdlib/doc/src/binary.xml new file mode 100644 index 0000000000..88ce77e0d0 --- /dev/null +++ b/lib/stdlib/doc/src/binary.xml @@ -0,0 +1,701 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE erlref SYSTEM "erlref.dtd"> + +<erlref> + <header> + <copyright> + <year>2009</year> + <year>2011</year> + <holder>Ericsson AB, All Rights Reserved</holder> + </copyright> + <legalnotice> + 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 on line 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. + + The Initial Developer of the Original Code is Ericsson AB. + </legalnotice> + + <title>binary</title> + <prepared>Patrik Nyblom</prepared> + <responsible>Kenneth Lundin</responsible> + <docno>1</docno> + <approved></approved> + <checked></checked> + <date>2010-05-05</date> + <rev>A</rev> + <file>binary.xml</file> + </header> + <module>binary</module> + <modulesummary>Library for handling binary data</modulesummary> + <description> + + <p>This module contains functions for manipulating byte-oriented + binaries. Although the majority of functions could be implemented + using bit-syntax, the functions in this library are highly + optimized and are expected to either execute faster or consume + less memory (or both) than a counterpart written in pure Erlang.</p> + + <p>The module is implemented according to the EEP (Erlang Enhancement Proposal) 31.</p> + + <note> + <p> + The library handles byte-oriented data. Bitstrings that are not + binaries (does not contain whole octets of bits) will result in a <c>badarg</c> + exception being thrown from any of the functions in this + module. + </p> + </note> + </description> + <datatypes> + <datatype> + <name name="cp"/> + <desc><p>Opaque data-type representing a compiled + search-pattern. Guaranteed to be a tuple() to allow programs to + distinguish it from non precompiled search patterns.</p> + </desc> + </datatype> + <datatype> + <name name="part"/> + <desc><p>A representaion of a part (or range) in a binary. Start is a + zero-based offset into a binary() and Length is the length of + that part. As input to functions in this module, a reverse + part specification is allowed, constructed with a negative + Length, so that the part of the binary begins at Start + + Length and is -Length long. This is useful for referencing the + last N bytes of a binary as {size(Binary), -N}. The functions + in this module always return part()'s with positive Length.</p> + </desc> + </datatype> + </datatypes> + <funcs> + <func> + <name>at(Subject, Pos) -> byte()</name> + <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>, + a <c>badarg</c> + exception is raised.</p> + + </desc> + </func> + <func> + <name>bin_to_list(Subject) -> [byte()]</name> + <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> + </desc> + </func> + <func> + <name>bin_to_list(Subject, PosLen) -> [byte()]</name> + <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 + the value of one byte. The <c>part()</c> denotes which part of the + <c>binary()</c> to convert. Example:</p> + +<code> +1> binary:bin_to_list(<<"erlang">>,{1,3}). +"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> + </desc> + </func> + <func> + <name>bin_to_list(Subject, Pos, Len) -> [byte()]</name> + <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> + </desc> + </func> + <func> + <name>compile_pattern(Pattern) -> cp()</name> + <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 + search-pattern, later to be used in the <seealso marker="#match-3">match/3</seealso>, + <seealso marker="#matches-3">matches/3</seealso>, + <seealso marker="#split-3">split/3</seealso> or + <seealso marker="#replace-4">replace/4</seealso> + functions. The <c>cp()</c> returned is guaranteed to be a + <c>tuple()</c> to allow programs to distinguish it from non + pre-compiled search patterns</p> + + <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 + 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 + only one element. The order of alternatives in a pattern is not significant.</p> + + <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, + a <c>badarg</c> exception will be raised.</p> + + </desc> + </func> + <func> + <name>copy(Subject) -> binary()</name> + <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> + </desc> + </func> + <func> + <name>copy(Subject,N) -> binary()</name> + <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>This function will always create a new binary, even if <c>N = + 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> + + <note> + <p>By deliberately copying a single binary to avoid referencing + a larger binary, one might, instead of freeing up the larger + binary for later garbage collection, create much more binary + data than needed. Sharing binary data is usually good. Only in + special cases, when small parts reference large binaries and the + 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> + </desc> + </func> + <func> + <name>decode_unsigned(Subject) -> Unsigned</name> + <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> + </desc> + </func> + <func> + <name>decode_unsigned(Subject, Endianess) -> Unsigned</name> + <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> + + <p>Example:</p> + + <code> +1> binary:decode_unsigned(<<169,138,199>>,big). +11111111 + </code> + </desc> + </func> + <func> + <name>encode_unsigned(Unsigned) -> binary()</name> + <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> + </desc> + </func> + <func> + <name>encode_unsigned(Unsigned,Endianess) -> binary()</name> + <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 + representation in a binary digit representation, either big + or little endian.</p> + + <p>Example:</p> + + <code> +1> binary:encode_unsigned(11111111,big). +<<169,138,199>> + </code> + </desc> + </func> + <func> + <name>first(Subject) -> byte()</name> + <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> + + </desc> + </func> + <func> + <name>last(Subject) -> byte()</name> + <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> + + </desc> + </func> + <func> + <name>list_to_bin(ByteList) -> binary()</name> + <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> + <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> + +<code> +1> binary:longest_common_prefix([<<"erlang">>,<<"ergonomy">>]). +2 +2> binary:longest_common_prefix([<<"erlang">>,<<"perl">>]). +0 +</code> + + <p>If <c>Binaries</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> + <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> + +<code> +1> binary:longest_common_suffix([<<"erlang">>,<<"fang">>]). +3 +2> binary:longest_common_suffix([<<"erlang">>,<<"perl">>]). +0 +</code> + + <p>If <c>Binaries</c> is not a flat list of binaries, a <c>badarg</c> exception is raised.</p> + + </desc> + </func> + <func> + <name>match(Subject, Pattern) -> Found | <c>nomatch</c></name> + <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> + </desc> + </func> + <func> + <name>match(Subject,Pattern,Options) -> Found | <c>nomatch</c></name> + <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 + 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> + +<code> +1> binary:match(<<"abcde">>, [<<"bcde">>,<<"cd">>],[]). +{1,4} +</code> + + <p>Even though <c><<"cd">></c> ends before + <c><<"bcde">></c>, <c><<"bcde">></c> + begins first and is therefore the first match. If two + overlapping matches begin at the same position, the longest is + returned.</p> + + <p>Summary of the options:</p> + + <taglist> + <tag>{scope, {Start, Length}}</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> + </taglist> + + <p>If none of the strings in + <c>Pattern</c> is found, the atom <c>nomatch</c> is returned.</p> + + <p>For a description of <c>Pattern</c>, see + <seealso marker="#compile_pattern-1">compile_pattern/1</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> + + </desc> + </func> + <func> + <name>matches(Subject, Pattern) -> Found</name> + <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> + </desc> + </func> + <func> + <name>matches(Subject,Pattern,Options) -> Found</name> + <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 + exhausted and a list of all non-overlapping parts matching + <c>Pattern</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> + +<code> +1> binary:matches(<<"abcde">>, + [<<"bcde">>,<<"bc">>>,<<"de">>],[]). +[{1,4}] +</code> + + <p>The result shows that <<"bcde">> is selected instead of the + shorter match <<"bc">> (which would have given raise to one + more match,<<"de">>). This corresponds to the behavior of posix + regular expressions (and programs like awk), but is not + consistent with alternative matches in re (and Perl), where + instead lexical ordering in the search pattern selects which + string matches.</p> + + <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 + 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> + + </desc> + </func> + <func> + <name>part(Subject, PosLen) -> binary()</name> + <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>Negative length can be used to extract bytes at the end of a binary:</p> + +<code> +1> Bin = <<1,2,3,4,5,6,7,8,9,10>>. +2> binary:part(Bin,{byte_size(Bin), -5}). +<<6,7,8,9,10>> +</code> + + <note> + <p><seealso marker="#part-2">part/2</seealso>and <seealso + marker="#part-3">part/3</seealso> are also available in the + <c>erlang</c> module under the names <c>binary_part/2</c> and + <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 + is raised.</p> + + </desc> + </func> + <func> + <name>part(Subject, Pos, Len) -> binary()</name> + <fsummary>Extracts a part of a binary</fsummary> + <type> + <v>Subject = binary()</v> + <v>Pos = integer() >= 0</v> + <v>Len = integer() >= 0</v> + </type> + <desc> + <p>The same as <c>part(Subject, {Pos, Len})</c>.</p> + </desc> + </func> + <func> + <name>referenced_byte_size(binary()) -> integer() >= 0</name> + <fsummary>Determines the size of the actual binary pointed out by a sub-binary</fsummary> + <desc> + + <p>If a binary references a larger binary (often described as + being a sub-binary), it can be useful to get the size of the + actual referenced binary. This function can be used in a program + to trigger the use of <c>copy/1</c>. By copying a binary, one might + dereference the original, possibly large, binary which a smaller + binary is a reference to.</p> + + <p>Example:</p> + + <code> +store(Binary, GBSet) -> + NewBin = + case binary:referenced_byte_size(Binary) of + Large when Large > 2 * byte_size(Binary) -> + binary:copy(Binary); + _ -> + Binary + end, + gb_sets:insert(NewBin,GBSet). + </code> + + <p>In this example, we chose to copy the binary content before + inserting it in the <c>gb_set()</c> if it references a binary more than + twice the size of the data we're going to keep. Of course + different rules for when copying will apply to different + programs.</p> + + <p>Binary sharing will occur whenever binaries are taken apart, + this is the fundamental reason why binaries are fast, + decomposition can always be done with O(1) complexity. In rare + circumstances this data sharing is however undesirable, why this + function together with <c>copy/1</c> might be useful when optimizing + for memory use.</p> + + <p>Example of binary sharing:</p> + + <code> +1> A = binary:copy(<<1>>,100). +<<1,1,1,1,1 ... +2> byte_size(A). +100 +3> binary:referenced_byte_size(A) +100 +4> <<_:10/binary,B:10/binary,_/binary>> = A. +<<1,1,1,1,1 ... +5> byte_size(B). +10 +6> binary:referenced_byte_size(B) +100 + </code> + + <note> + <p>Binary data is shared among processes. If another process + still references the larger binary, copying the part this + process uses only consumes more memory and will not free up the + larger binary for garbage collection. Use this kind of intrusive + functions with extreme care, and only if a real problem is + detected.</p> + </note> + + </desc> + </func> + <func> + <name name="replace" arity="3"/> + <fsummary>Replaces bytes in a binary according to a pattern</fsummary> + <desc> + <p>The same as <c>replace(<anno>Subject</anno>,<anno>Pattern</anno>,<anno>Replacement</anno>,[])</c>.</p> + </desc> + </func> + <func> + <name name="replace" arity="4"/> + <fsummary>Replaces bytes in a binary according to a pattern</fsummary> + <type_desc variable="OnePos">An integer() =< byte_size(<anno>Replacement</anno>) + </type_desc> + <desc> + + <p>Constructs a new binary by replacing the parts in + <c><anno>Subject</anno></c> matching <c><anno>Pattern</anno></c> with the content of + <c><anno>Replacement</anno></c>.</p> + + <p>If the matching sub-part of <c><anno>Subject</anno></c> giving raise to the + replacement is to be inserted in the result, the option + <c>{insert_replaced, <anno>InsPos</anno>}</c> will insert the matching part into + <c><anno>Replacement</anno></c> at the given position (or positions) before actually + inserting <c><anno>Replacement</anno></c> into the <c><anno>Subject</anno></c>. Example:</p> + +<code> +1> binary:replace(<<"abcde">>,<<"b">>,<<"[]">>,[{insert_replaced,1}]). +<<"a[b]cde">> +2> binary:replace(<<"abcde">>,[<<"b">>,<<"d">>],<<"[]">>, + [global,{insert_replaced,1}]). +<<"a[b]c[d]e">> +3> binary:replace(<<"abcde">>,[<<"b">>,<<"d">>],<<"[]">>, + [global,{insert_replaced,[1,1]}]). +<<"a[bb]c[dd]e">> +4> binary:replace(<<"abcde">>,[<<"b">>,<<"d">>],<<"[-]">>, + [global,{insert_replaced,[1,2]}]). +<<"a[b-b]c[d-d]e">> +</code> + + <p>If any position given in <c><anno>InsPos</anno></c> is greater than the size of the replacement binary, a <c>badarg</c> exception is raised.</p> + + <p>The options <c>global</c> and <c>{scope, part()}</c> work as for <seealso marker="#split-3">split/3</seealso>. The return type is always a <c>binary()</c>.</p> + + <p>For a description of <c><anno>Pattern</anno></c>, see <seealso marker="#compile_pattern-1">compile_pattern/1</seealso>.</p> + </desc> + </func> + <func> + <name name="split" arity="2"/> + <fsummary>Splits a binary according to a pattern</fsummary> + <desc> + <p>The same as <c>split(<anno>Subject</anno>, <anno>Pattern</anno>, [])</c>.</p> + </desc> + </func> + <func> + <name name="split" arity="3"/> + <fsummary>Splits a binary according to a pattern</fsummary> + <desc> + + <p>Splits <c><anno>Subject</anno></c> into a list of binaries based on <c><anno>Pattern</anno></c>. If + the option global is not given, only the first occurrence of + <c><anno>Pattern</anno></c> in <c><anno>Subject</anno></c> will give rise to a split.</p> + + <p>The parts of <c><anno>Pattern</anno></c> actually found in <c><anno>Subject</anno></c> are not included in the result.</p> + + <p>Example:</p> + +<code> +1> binary:split(<<1,255,4,0,0,0,2,3>>, [<<0,0,0>>,<<2>>],[]). +[<<1,255,4>>, <<2,3>>] +2> binary:split(<<0,1,0,0,4,255,255,9>>, [<<0,0>>, <<255,255>>],[global]). +[<<0,1>>,<<4>>,<<9>>] +</code> + + <p>Summary of options:</p> + <taglist> + + <tag>{scope, part()}</tag> + + <item><p>Works as in <seealso marker="#match-3">match/3</seealso> and + <seealso marker="#matches-3">matches/3</seealso>. Note that + this only defines the scope of the search for matching strings, + it does not cut the binary before splitting. The bytes before + and after the scope will be kept in the result. See example + below.</p></item> + + <tag>trim</tag> + + <item><p>Removes trailing empty parts of the result (as does trim in <c>re:split/3</c>)</p></item> + + <tag>global</tag> + + <item><p>Repeats the split until the <c><anno>Subject</anno></c> is + exhausted. Conceptually the global option makes split work on + the positions returned by <seealso marker="#matches-3">matches/3</seealso>, + while it normally + works on the position returned by + <seealso marker="#match-3">match/3</seealso>.</p></item> + + </taglist> + + <p>Example of the difference between a scope and taking the + binary apart before splitting:</p> + +<code> +1> binary:split(<<"banana">>,[<<"a">>],[{scope,{2,3}}]). +[<<"ban">>,<<"na">>] +2> binary:split(binary:part(<<"banana">>,{2,3}),[<<"a">>],[]). +[<<"n">>,<<"n">>] +</code> + + <p>The return type is always a list of binaries that are all + referencing <c><anno>Subject</anno></c>. This means that the data in <c><anno>Subject</anno></c> is not + actually copied to new binaries and that <c><anno>Subject</anno></c> cannot be + garbage collected until the results of the split are no longer + referenced.</p> + + <p>For a description of <c><anno>Pattern</anno></c>, see <seealso marker="#compile_pattern-1">compile_pattern/1</seealso>.</p> + + </desc> + </func> + </funcs> +</erlref> diff --git a/lib/stdlib/doc/src/c.xml b/lib/stdlib/doc/src/c.xml index 19e3ac1f08..ddae388a1b 100644 --- a/lib/stdlib/doc/src/c.xml +++ b/lib/stdlib/doc/src/c.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2009</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -39,43 +39,33 @@ </description> <funcs> <func> - <name>bt(Pid) -> void()</name> + <name name="bt" arity="1"/> <fsummary>Stack backtrace for a process</fsummary> - <type> - <v>Pid = pid()</v> - </type> <desc> <p>Stack backtrace for a process. Equivalent to - <c>erlang:process_display(Pid, backtrace)</c>.</p> + <c>erlang:process_display(<anno>Pid</anno>, backtrace)</c>.</p> </desc> </func> <func> - <name>c(File) -> {ok, Module} | error</name> - <name>c(File, Options) -> {ok, Module} | error</name> + <name name="c" arity="1"/> + <name name="c" arity="2"/> <fsummary>Compile and load code in a file</fsummary> - <type> - <v>File = name() -- see filename(3)</v> - <v>Options = [Opt] -- see compile:file/2</v> - </type> <desc> <p><c>c/1,2</c> compiles and then purges and loads the code for - a file. <c>Options</c> defaults to []. Compilation is + a file. <c><anno>Options</anno></c> defaults to []. Compilation is equivalent to:</p> <code type="none"> -compile:file(File, Options ++ [report_errors, report_warnings])</code> +compile:file(<anno>File</anno>, <anno>Options</anno> ++ [report_errors, report_warnings])</code> <p>Note that purging the code means that any processes lingering in old code for the module are killed without warning. See <c>code/3</c> for more information.</p> </desc> </func> <func> - <name>cd(Dir) -> void()</name> + <name name="cd" arity="1"/> <fsummary>Change working directory</fsummary> - <type> - <v>Dir = name() -- see filename(3)</v> - </type> <desc> - <p>Changes working directory to <c>Dir</c>, which may be a + <p>Changes working directory to <c><anno>Dir</anno></c>, which may be a relative name, and then prints the name of the new working directory.</p> <pre> @@ -84,14 +74,14 @@ compile:file(File, Options ++ [report_errors, report_warnings])</code> </desc> </func> <func> - <name>flush() -> void()</name> + <name name="flush" arity="0"/> <fsummary>Flush any messages sent to the shell</fsummary> <desc> <p>Flushes any messages sent to the shell.</p> </desc> </func> <func> - <name>help() -> void()</name> + <name name="help" arity="0"/> <fsummary>Help information</fsummary> <desc> <p>Displays help information: all valid shell internal commands, @@ -99,8 +89,8 @@ compile:file(File, Options ++ [report_errors, report_warnings])</code> </desc> </func> <func> - <name>i() -> void()</name> - <name>ni() -> void()</name> + <name name="i" arity="0"/> + <name name="ni" arity="0"/> <fsummary>Information about the system</fsummary> <desc> <p><c>i/0</c> displays information about the system, listing @@ -109,26 +99,20 @@ compile:file(File, Options ++ [report_errors, report_warnings])</code> </desc> </func> <func> - <name>i(X, Y, Z) -> void()</name> + <name name="i" arity="3"/> <fsummary>Information about pid <X.Y.Z></fsummary> - <type> - <v>X = Y = Z = int()</v> - </type> <desc> <p>Displays information about a process, Equivalent to - <c>process_info(pid(X, Y, Z))</c>, but location transparent.</p> + <c>process_info(pid(<anno>X</anno>, <anno>Y</anno>, <anno>Z</anno>))</c>, but location transparent.</p> </desc> </func> <func> - <name>l(Module) -> void()</name> + <name name="l" arity="1"/> <fsummary>Load or reload module</fsummary> - <type> - <v>Module = atom()</v> - </type> <desc> <p>Purges and loads, or reloads, a module by calling - <c>code:purge(Module)</c> followed by - <c>code:load_file(Module)</c>.</p> + <c>code:purge(<anno>Module</anno>)</c> followed by + <c>code:load_file(<anno>Module</anno>)</c>.</p> <p>Note that purging the code means that any processes lingering in old code for the module are killed without warning. See <c>code/3</c> for more information.</p> @@ -136,35 +120,33 @@ compile:file(File, Options ++ [report_errors, report_warnings])</code> </func> <func> <name>lc(Files) -> ok</name> - <fsummary>Compile a list of files</fsummary> <type> <v>Files = [File]</v> - <v> File = name() -- see filename(3)</v> + <v>File = <seealso marker="file#type-filename">file:filename() + </seealso></v> </type> + <fsummary>Compile a list of files</fsummary> <desc> <p>Compiles a list of files by calling <c>compile:file(File, [report_errors, report_warnings])</c> for each <c>File</c> in <c>Files</c>.</p> </desc> </func> <func> - <name>ls() -> void()</name> + <name name="ls" arity="0"/> <fsummary>List files in the current directory</fsummary> <desc> <p>Lists files in the current directory.</p> </desc> </func> <func> - <name>ls(Dir) -> void()</name> + <name name="ls" arity="1"/> <fsummary>List files in a directory</fsummary> - <type> - <v>Dir = name() -- see filename(3)</v> - </type> <desc> - <p>Lists files in directory <c>Dir</c>.</p> + <p>Lists files in directory <c><anno>Dir</anno></c>.</p> </desc> </func> <func> - <name>m() -> void()</name> + <name name="m" arity="0"/> <fsummary>Which modules are loaded</fsummary> <desc> <p>Displays information about the loaded modules, including @@ -172,84 +154,67 @@ compile:file(File, Options ++ [report_errors, report_warnings])</code> </desc> </func> <func> - <name>m(Module) -> void()</name> + <name name="m" arity="1"/> <fsummary>Information about a module</fsummary> - <type> - <v>Module = atom()</v> - </type> <desc> - <p>Displays information about <c>Module</c>.</p> + <p>Displays information about <c><anno>Module</anno></c>.</p> </desc> </func> <func> - <name>memory() -> [{Type, Size}]</name> + <name name="memory" arity="0"/> <fsummary>Memory allocation information</fsummary> - <type> - <v>Type, Size -- see erlang:memory/0</v> - </type> <desc> <p>Memory allocation information. Equivalent to - <c>erlang:memory/0</c>.</p> + <seealso marker="erts:erlang#memory/0"><c>erlang:memory/0</c> + </seealso>.</p> </desc> </func> <func> - <name>memory(Type) -> Size</name> - <name>memory([Type]) -> [{Type, Size}]</name> + <name name="memory" arity="1" clause_i="1"/> + <name name="memory" arity="1" clause_i="2"/> <fsummary>Memory allocation information</fsummary> - <type> - <v>Type, Size -- see erlang:memory/0</v> - </type> <desc> <p>Memory allocation information. Equivalent to - <c>erlang:memory/1</c>.</p> + <seealso marker="erts:erlang#memory/1"><c>erlang:memory/1</c> + </seealso>.</p> </desc> </func> <func> - <name>nc(File) -> {ok, Module} | error</name> - <name>nc(File, Options) -> {ok, Module} | error</name> + <name name="nc" arity="1"/> + <name name="nc" arity="2"/> <fsummary>Compile and load code in a file on all nodes</fsummary> - <type> - <v>File = name() -- see filename(3)</v> - <v>Options = [Opt] -- see compile:file/2</v> - </type> <desc> <p>Compiles and then loads the code for a file on all nodes. - <c>Options</c> defaults to []. Compilation is equivalent to:</p> + <c><anno>Options</anno></c> defaults to []. Compilation is equivalent to:</p> <code type="none"> -compile:file(File, Opts ++ [report_errors, report_warnings])</code> +compile:file(<anno>File</anno>, <anno>Options</anno> ++ [report_errors, report_warnings])</code> </desc> </func> <func> - <name>nl(Module) -> void()</name> + <name name="nl" arity="1"/> <fsummary>Load module on all nodes</fsummary> - <type> - <v>Module = atom()</v> - </type> <desc> - <p>Loads <c>Module</c> on all nodes.</p> + <p>Loads <c><anno>Module</anno></c> on all nodes.</p> </desc> </func> <func> - <name>pid(X, Y, Z) -> pid()</name> + <name name="pid" arity="3"/> <fsummary>Convert X,Y,Z to a pid</fsummary> - <type> - <v>X = Y = Z = int()</v> - </type> <desc> - <p>Converts <c>X</c>, <c>Y</c>, <c>Z</c> to the pid + <p>Converts <c><anno>X</anno></c>, <c><anno>Y</anno></c>, <c><anno>Z</anno></c> to the pid <c><![CDATA[<X.Y.Z>]]></c>. This function should only be used when debugging.</p> </desc> </func> <func> - <name>pwd() -> void()</name> + <name name="pwd" arity="0"/> <fsummary>Print working directory</fsummary> <desc> <p>Prints the name of the working directory.</p> </desc> </func> <func> - <name>q() -> void()</name> + <name name="q" arity="0"/> <fsummary>Quit - shorthand for <c>init:stop()</c></fsummary> <desc> <p>This function is shorthand for <c>init:stop()</c>, that is, @@ -257,8 +222,8 @@ compile:file(File, Opts ++ [report_errors, report_warnings])</code> </desc> </func> <func> - <name>regs() -> void()</name> - <name>nregs() -> void()</name> + <name name="regs" arity="0"/> + <name name="nregs" arity="0"/> <fsummary>Information about registered processes</fsummary> <desc> <p><c>regs/0</c> displays information about all registered diff --git a/lib/stdlib/doc/src/calendar.xml b/lib/stdlib/doc/src/calendar.xml index 36f0c03162..4876b37127 100644 --- a/lib/stdlib/doc/src/calendar.xml +++ b/lib/stdlib/doc/src/calendar.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2009</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -63,121 +63,170 @@ given as local time, they must be converted to universal time, in order to get the correct value of the elapsed time between epochs. Use of the function <c>time_difference/2</c> is discouraged.</p> + <p>There exists different definitions for the week of the year. + The calendar module contains a week of the year implementation + which conforms to the ISO 8601 standard. Since the week number for + a given date can fall on the previous, the current or on the next + year it is important to provide the information which year is it + together with the week number. The function <c>iso_week_number/0</c> + and <c>iso_week_number/1</c> returns a tuple of the year and the + week number.</p> </description> - <section> - <title>DATA TYPES</title> - <code type="none"> -date() = {Year, Month, Day} - Year = int() - Month = 1..12 - Day = 1..31 -Year cannot be abbreviated. Example: 93 denotes year 93, not 1993. -Valid range depends on the underlying OS. -The date tuple must denote a valid date. + <datatypes> + <datatype> + <name name="t_datetime"/> + </datatype> + <datatype> + <name name="t_datetime1970"/> + </datatype> + <datatype> + <name name="t_date"/> + </datatype> + <datatype> + <name name="year"/> + <desc><p>Year cannot be abbreviated. Example: 93 denotes year + 93, not 1993. Valid range depends on the underlying OS. The + date tuple must denote a valid date.</p> + </desc> + </datatype> + <datatype> + <name name="year1970"/> + </datatype> + <datatype> + <name name="month"/> + </datatype> + <datatype> + <name name="day"/> + </datatype> + <datatype> + <name name="t_time"/> + </datatype> + <datatype> + <name name="hour"/> + </datatype> + <datatype> + <name name="minute"/> + </datatype> + <datatype> + <name name="second"/> + </datatype> + <datatype> + <name name="daynum"/> + </datatype> + <datatype> + <name name="ldom"/> + </datatype> + <datatype> + <name name="t_now"/> + <desc><p>See <seealso marker="erts:erlang#now/0">erlang:now/0</seealso>.</p> + </desc> + </datatype> + <datatype> + <name name="t_yearweeknum"/> + </datatype> + <datatype> + <name name="weeknum"/> + </datatype> + </datatypes> -time() = {Hour, Minute, Second} - Hour = 0..23 - Minute = Second = 0..59</code> - </section> <funcs> <func> - <name>date_to_gregorian_days(Date) -> Days</name> - <name>date_to_gregorian_days(Year, Month, Day) -> Days</name> + <name name="date_to_gregorian_days" arity="1"/> + <name name="date_to_gregorian_days" arity="3"/> + <type variable="Date" name_i="1"/> + <type variable="Year"/> + <type variable="Month"/> + <type variable="Day"/> <fsummary>Compute the number of days from year 0 up to the given date</fsummary> - <type> - <v>Date = date()</v> - <v>Days = int()</v> - </type> <desc> <p>This function computes the number of gregorian days starting with year 0 and ending at the given date.</p> </desc> </func> <func> - <name>datetime_to_gregorian_seconds({Date, Time}) -> Seconds</name> + <name name="datetime_to_gregorian_seconds" arity="1"/> <fsummary>Compute the number of seconds from year 0 up to the given date and time</fsummary> - <type> - <v>Date = date()</v> - <v>Time = time()</v> - <v>Seconds = int()</v> - </type> <desc> <p>This function computes the number of gregorian seconds starting with year 0 and ending at the given date and time.</p> </desc> </func> <func> - <name>day_of_the_week(Date) -> DayNumber</name> - <name>day_of_the_week(Year, Month, Day) -> DayNumber</name> + <name name="day_of_the_week" arity="1"/> + <name name="day_of_the_week" arity="3"/> <fsummary>Compute the day of the week</fsummary> - <type> - <v>Date = date()</v> - <v>DayNumber = 1..7</v> - </type> + <type variable="Date" name_i="1"/> + <type variable="Year"/> + <type variable="Month"/> + <type variable="Day"/> <desc> - <p>This function computes the day of the week given <c>Year</c>, - <c>Month</c> and <c>Day</c>. The return value denotes the day + <p>This function computes the day of the week given <c><anno>Year</anno></c>, + <c><anno>Month</anno></c> and <c><anno>Day</anno></c>. The return value denotes the day of the week as <c>1</c>: Monday, <c>2</c>: Tuesday, and so on.</p> </desc> </func> <func> - <name>gregorian_days_to_date(Days) -> Date</name> + <name name="gregorian_days_to_date" arity="1"/> <fsummary>Compute the date given the number of gregorian days</fsummary> - <type> - <v>Days = int()</v> - <v>Date = date()</v> - </type> <desc> <p>This function computes the date given the number of gregorian days.</p> </desc> </func> <func> - <name>gregorian_seconds_to_datetime(Seconds) -> {Date, Time}</name> + <name name="gregorian_seconds_to_datetime" arity="1"/> <fsummary>Compute the date given the number of gregorian days</fsummary> - <type> - <v>Seconds = int()</v> - <v>Date = date()</v> - <v>Time = time()</v> - </type> <desc> <p>This function computes the date and time from the given number of gregorian seconds.</p> </desc> </func> <func> - <name>is_leap_year(Year) -> bool()</name> + <name name="is_leap_year" arity="1"/> <fsummary>Check if a year is a leap year</fsummary> <desc> <p>This function checks if a year is a leap year.</p> </desc> </func> <func> - <name>last_day_of_the_month(Year, Month) -> int()</name> + <name name="iso_week_number" arity="0"/> + <fsummary>Compute the iso week number for the actual date</fsummary> + <desc> + <p>This function returns the tuple {Year, WeekNum} representing + the iso week number for the actual date. For determining the + actual date, the function <c>local_time/0</c> is used.</p> + </desc> + </func> + <func> + <name name="iso_week_number" arity="1"/> + <fsummary>Compute the iso week number for the given date</fsummary> + <desc> + <p>This function returns the tuple {Year, WeekNum} representing + the iso week number for the given date.</p> + </desc> + </func> + <func> + <name name="last_day_of_the_month" arity="2"/> <fsummary>Compute the number of days in a month</fsummary> <desc> <p>This function computes the number of days in a month.</p> </desc> </func> <func> - <name>local_time() -> {Date, Time}</name> + <name name="local_time" arity="0"/> <fsummary>Compute local time</fsummary> - <type> - <v>Date = date()</v> - <v>Time = time()</v> - </type> <desc> <p>This function returns the local time reported by the underlying operating system.</p> </desc> </func> <func> - <name>local_time_to_universal_time({Date1, Time1}) -> {Date2, Time2}</name> + <name name="local_time_to_universal_time" arity="1"/> <fsummary>Convert from local time to universal time (deprecated)</fsummary> <desc> <p>This function converts from local time to Universal - Coordinated Time (UTC). <c>Date1</c> must refer to a local + Coordinated Time (UTC). <c><anno>DateTime1</anno></c> must refer to a local date after Jan 1, 1970.</p> <warning> <p>This function is deprecated. Use @@ -190,15 +239,11 @@ time() = {Hour, Minute, Second} </desc> </func> <func> - <name>local_time_to_universal_time_dst({Date1, Time1}) -> [{Date, Time}]</name> + <name name="local_time_to_universal_time_dst" arity="1"/> <fsummary>Convert from local time to universal time(s)</fsummary> - <type> - <v>Date1 = Date = date()</v> - <v>Time1 = Time = time()</v> - </type> <desc> <p>This function converts from local time to Universal - Coordinated Time (UTC). <c>Date1</c> must refer to a local + Coordinated Time (UTC). <c><anno>DateTime1</anno></c> must refer to a local date after Jan 1, 1970.</p> <p>The return value is a list of 0, 1 or 2 possible UTC times:</p> <taglist> @@ -226,65 +271,48 @@ time() = {Hour, Minute, Second} </desc> </func> <func> - <name>now_to_local_time(Now) -> {Date, Time}</name> + <name name="now_to_local_time" arity="1"/> <fsummary>Convert now to local date and time</fsummary> - <type> - <v>Now -- see erlang:now/0</v> - <v>Date = date()</v> - <v>Time = time()</v> - </type> <desc> <p>This function returns local date and time converted from the return value from <c>erlang:now()</c>.</p> </desc> </func> <func> - <name>now_to_universal_time(Now) -> {Date, Time}</name> - <name>now_to_datetime(Now) -> {Date, Time}</name> + <name name="now_to_universal_time" arity="1"/> + <name name="now_to_datetime" arity="1"/> <fsummary>Convert now to date and time</fsummary> - <type> - <v>Now -- see erlang:now/0</v> - <v>Date = date()</v> - <v>Time = time()</v> - </type> <desc> <p>This function returns Universal Coordinated Time (UTC) converted from the return value from <c>erlang:now()</c>.</p> </desc> </func> <func> - <name>seconds_to_daystime(Seconds) -> {Days, Time}</name> + <name name="seconds_to_daystime" arity="1"/> <fsummary>Compute days and time from seconds</fsummary> - <type> - <v>Seconds = Days = int()</v> - <v>Time = time()</v> - </type> <desc> <p>This function transforms a given number of seconds into days, - hours, minutes, and seconds. The <c>Time</c> part is always - non-negative, but <c>Days</c> is negative if the argument - <c>Seconds</c> is.</p> + hours, minutes, and seconds. The <c><anno>Time</anno></c> part is always + non-negative, but <c><anno>Days</anno></c> is negative if the argument + <c><anno>Seconds</anno></c> is.</p> </desc> </func> <func> - <name>seconds_to_time(Seconds) -> Time</name> + <name name="seconds_to_time" arity="1"/> <fsummary>Compute time from seconds</fsummary> - <type> - <v>Seconds = int() < 86400</v> - <v>Time = time()</v> - </type> + <type name="secs_per_day"/> <desc> <p>This function computes the time from the given number of - seconds. <c>Seconds</c> must be less than the number of + seconds. <c><anno>Seconds</anno></c> must be less than the number of seconds per day (86400).</p> </desc> </func> <func> - <name>time_difference(T1, T2) -> {Days, Time}</name> + <name name="time_difference" arity="2"/> <fsummary>Compute the difference between two times (deprecated)</fsummary> <desc> - <p>This function returns the difference between two <c>{Date, Time}</c> tuples. <c>T2</c> should refer to an epoch later - than <c>T1</c>.</p> + <p>This function returns the difference between two <c>{Date, Time}</c> tuples. <c><anno>T2</anno></c> should refer to an epoch later + than <c><anno>T1</anno></c>.</p> <warning> <p>This function is obsolete. Use the conversion functions for gregorian days and seconds instead.</p> @@ -292,24 +320,17 @@ time() = {Hour, Minute, Second} </desc> </func> <func> - <name>time_to_seconds(Time) -> Seconds</name> + <name name="time_to_seconds" arity="1"/> <fsummary>Compute the number of seconds since midnight up to the given time</fsummary> - <type> - <v>Time = time()</v> - <v>Seconds = int()</v> - </type> + <type name="secs_per_day"/> <desc> <p>This function computes the number of seconds since midnight up to the specified time.</p> </desc> </func> <func> - <name>universal_time() -> {Date, Time}</name> + <name name="universal_time" arity="0"/> <fsummary>Compute universal time</fsummary> - <type> - <v>Date = date()</v> - <v>Time = time()</v> - </type> <desc> <p>This function returns the Universal Coordinated Time (UTC) reported by the underlying operating system. Local time is @@ -317,25 +338,22 @@ time() = {Hour, Minute, Second} </desc> </func> <func> - <name>universal_time_to_local_time({Date1, Time1}) -> {Date2, Time2}</name> + <name name="universal_time_to_local_time" arity="1"/> <fsummary>Convert from universal time to local time</fsummary> - <type> - <v>Date1 = Date2 = date()</v> - <v>Time1 = Time2 = time()</v> - </type> <desc> <p>This function converts from Universal Coordinated Time (UTC) - to local time. <c>Date1</c> must refer to a date after Jan 1, + to local time. <c><anno>DateTime</anno></c> must refer to a date after Jan 1, 1970.</p> </desc> </func> <func> - <name>valid_date(Date) -> bool()</name> - <name>valid_date(Year, Month, Day) -> bool()</name> + <name name="valid_date" arity="1"/> + <name name="valid_date" arity="3"/> + <type variable="Date" name_i="1"/> + <type variable="Year"/> + <type variable="Month"/> + <type variable="Day"/> <fsummary>Check if a date is valid</fsummary> - <type> - <v>Date = date()</v> - </type> <desc> <p>This function checks if a date is a valid.</p> </desc> diff --git a/lib/stdlib/doc/src/dets.xml b/lib/stdlib/doc/src/dets.xml index 8d1398d3b7..2512c84e18 100644 --- a/lib/stdlib/doc/src/dets.xml +++ b/lib/stdlib/doc/src/dets.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2009</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -100,50 +100,99 @@ the process with the error tuple). If given badly formed arguments, all functions exit the process with a <c>badarg</c> message.</p> - <p><em>Types</em></p> - <pre> -access() = read | read_write -auto_save() = infinity | int() -bindings_cont() = tuple() -bool() = true | false -file() = string() -int() = integer() >= 0 -keypos() = integer() >= 1 -name() = atom() | ref() -no_slots() = integer() >= 0 | default -object() = tuple() -object_cont() = tuple() -select_cont() = tuple() -type() = bag | duplicate_bag | set -version() = 8 | 9 | default </pre> </description> + <datatypes> + <datatype> + <name name="access"/> + </datatype> + <datatype> + <name name="auto_save"/> + </datatype> + <datatype> + <name name="bindings_cont"/> + <desc> + <p>Opaque continuation used by <seealso marker="#match/1"> + <c>match/1</c></seealso> and <seealso marker="#match/3"> + <c>match/3</c></seealso>.</p> + </desc> + </datatype> + <datatype> + <name name="cont"/> + <desc> + <p>Opaque continuation used by <seealso marker="#bchunk/2"> + <c>bchunk/2</c></seealso>.</p> + </desc> + </datatype> + <datatype> + <name name="keypos"/> + </datatype> + <datatype> + <name name="match_spec"/> + <desc> + <p>Match specifications, see the <seealso + marker="erts:match_spec">match specification</seealso> + documentation in the ERTS User's Guide and <seealso + marker="ms_transform">ms_transform(3).</seealso></p> + </desc> + </datatype> + <datatype> + <name name="no_slots"/> + </datatype> + <datatype> + <name name="object"/> + </datatype> + <datatype> + <name name="object_cont"/> + <desc> + <p>Opaque continuation used by <seealso marker="#match_object/1"> + <c>match_object/1</c></seealso> and <seealso marker="#match_object/3"> + <c>match_object/3</c></seealso>.</p> + </desc> + </datatype> + <datatype> + <name name="pattern"/> + <desc> + <p>See <seealso marker="ets#match/2">ets:match/2</seealso> for a + description of patterns.</p> + </desc> + </datatype> + <datatype> + <name name="select_cont"/> + <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> + </desc> + </datatype> + <datatype> + <name name="tab_name"/> + </datatype> + <datatype> + <name name="type"/> + </datatype> + <datatype> + <name name="version"/> + </datatype> + </datatypes> <funcs> <func> - <name>all() -> [Name]</name> + <name name="all" arity="0"/> <fsummary>Return a list of the names of all open Dets tables on this node.</fsummary> - <type> - <v>Name = name()</v> - </type> <desc> <p>Returns a list of the names of all open tables on this node.</p> </desc> </func> <func> - <name>bchunk(Name, Continuation) -> {Continuation2, Data} | '$end_of_table' | {error, Reason}</name> + <name name="bchunk" arity="2"/> <fsummary>Return a chunk of objects stored in a Dets table.</fsummary> - <type> - <v>Name = name()</v> - <v>Continuation = start | cont()</v> - <v>Continuation2 = cont()</v> - <v>Data = binary() | tuple()</v> - </type> <desc> <p>Returns a list of objects stored in a table. The exact representation of the returned objects is not public. The lists of data can be used for initializing a table by giving the value <c>bchunk</c> to the <c>format</c> option of the - <c>init_table/3</c> function. The Mnesia application uses this + <seealso marker="#init_table/3"><c>init_table/3</c></seealso> + function. The Mnesia application uses this function for copying open tables.</p> <p>Unless the table is protected using <c>safe_fixtable/2</c>, calls to <c>bchunk/2</c> may not work as expected if @@ -151,24 +200,23 @@ version() = 8 | 9 | default </pre> <p>The first time <c>bchunk/2</c> is called, an initial continuation, the atom <c>start</c>, must be provided.</p> <p>The <c>bchunk/2</c> function returns a tuple - <c>{Continuation2, Data}</c>, where <c>Data</c> is a list of - objects. <c>Continuation2</c> is another continuation which is + <c>{<anno>Continuation2</anno>, <anno>Data</anno>}</c>, + where <c><anno>Data</anno></c> is a list of + objects. <c><anno>Continuation2</anno></c> is another continuation + which is to be passed on to a subsequent call to <c>bchunk/2</c>. With a series of calls to <c>bchunk/2</c> it is possible to extract all objects of the table. </p> <p><c>bchunk/2</c> returns <c>'$end_of_table'</c> when all - objects have been returned, or <c>{error, Reason}</c> if an - error occurs. + objects have been returned, or <c>{error, <anno>Reason</anno>}</c> + if an error occurs. </p> </desc> </func> <func> - <name>close(Name) -> ok | {error, Reason} </name> + <name name="close" arity="1"/> <fsummary>Close a Dets table.</fsummary> - <type> - <v>Name = name()</v> - </type> <desc> <p>Closes a table. Only processes that have opened a table are allowed to close it. @@ -180,22 +228,16 @@ version() = 8 | 9 | default </pre> </desc> </func> <func> - <name>delete(Name, Key) -> ok | {error, Reason}</name> + <name name="delete" arity="2"/> <fsummary>Delete all objects with a given key from a Dets table.</fsummary> - <type> - <v>Name = name()</v> - </type> <desc> - <p>Deletes all objects with the key <c>Key</c> from the table - <c>Name</c>.</p> + <p>Deletes all objects with the key <c><anno>Key</anno></c> from + the table <c><anno>Name</anno></c>.</p> </desc> </func> <func> - <name>delete_all_objects(Name) -> ok | {error, Reason}</name> + <name name="delete_all_objects" arity="1"/> <fsummary>Delete all objects from a Dets table.</fsummary> - <type> - <v>Name = name()</v> - </type> <desc> <p>Deletes all objects from a table in almost constant time. However, if the table if fixed, <c>delete_all_objects(T)</c> @@ -203,12 +245,8 @@ version() = 8 | 9 | default </pre> </desc> </func> <func> - <name>delete_object(Name, Object) -> ok | {error, Reason}</name> + <name name="delete_object" arity="2"/> <fsummary>Delete a given object from a Dets table.</fsummary> - <type> - <v>Name = name()</v> - <v>Object = object()</v> - </type> <desc> <p>Deletes all instances of a given object from a table. If a table is of type <c>bag</c> or <c>duplicate_bag</c>, the @@ -218,18 +256,15 @@ version() = 8 | 9 | default </pre> </desc> </func> <func> - <name>first(Name) -> Key | '$end_of_table'</name> + <name name="first" arity="1"/> <fsummary>Return the first key stored in a Dets table.</fsummary> - <type> - <v>Key = term()</v> - <v>Name = name()</v> - </type> <desc> - <p>Returns the first key stored in the table <c>Name</c> + <p>Returns the first key stored in the table <c><anno>Name</anno></c> according to the table's internal order, or <c>'$end_of_table'</c> if the table is empty.</p> <p>Unless the table is protected using <c>safe_fixtable/2</c>, - subsequent calls to <c>next/2</c> may not work as expected if + subsequent calls to <seealso marker="#next/2"><c>next/2</c></seealso> + may not work as expected if concurrent updates are made to the table.</p> <p>Should an error occur, the process is exited with an error tuple <c>{error, Reason}</c>. The reason for not returning the @@ -243,107 +278,78 @@ version() = 8 | 9 | default </pre> </desc> </func> <func> - <name>foldl(Function, Acc0, Name) -> Acc1 | {error, Reason}</name> - <fsummary>Fold a function over a Dets table.</fsummary> - <type> - <v>Function = fun(Object, AccIn) -> AccOut</v> - <v>Acc0 = Acc1 = AccIn = AccOut = term()</v> - <v>Name = name()</v> - <v>Object = object()</v> - </type> - <desc> - <p>Calls <c>Function</c> on successive elements of the table - <c>Name</c> together with an extra argument <c>AccIn</c>. The - order in which the elements of the table are traversed is - unspecified. <c>Function</c> must return a new accumulator - which is passed to the next call. <c>Acc0</c> is returned if - the table is empty.</p> - </desc> - </func> - <func> - <name>foldr(Function, Acc0, Name) -> Acc1 | {error, Reason}</name> + <name name="foldl" arity="3"/> + <name name="foldr" arity="3"/> <fsummary>Fold a function over a Dets table.</fsummary> - <type> - <v>Function = fun(Object, AccIn) -> AccOut</v> - <v>Acc0 = Acc1 = AccIn = AccOut = term()</v> - <v>Name = name()</v> - <v>Object = object()</v> - </type> <desc> - <p>Calls <c>Function</c> on successive elements of the table - <c>Name</c> together with an extra argument <c>AccIn</c>. The + <p>Calls <c><anno>Function</anno></c> on successive elements of + the table <c><anno>Name</anno></c> together with an extra argument + <c>AccIn</c>. The order in which the elements of the table are traversed is - unspecified. <c>Function</c> must return a new accumulator - which is passed to the next call. <c>Acc0</c> is returned if + unspecified. <c><anno>Function</anno></c> must return a new + accumulator which is passed to the next call. + <c><anno>Acc0</anno></c> is returned if the table is empty.</p> </desc> </func> <func> - <name>from_ets(Name, EtsTab) -> ok | {error, Reason}</name> + <name name="from_ets" arity="2"/> <fsummary>Replace the objects of a Dets table with the objects of an Ets table.</fsummary> - <type> - <v>Name = name()</v> - <v>EtsTab = - see ets(3) -</v> - </type> <desc> - <p>Deletes all objects of the table <c>Name</c> and then - inserts all the objects of the Ets table <c>EtsTab</c>. The - order in which the objects are inserted is not specified. + <p>Deletes all objects of the table <c><anno>Name</anno></c> and then + inserts all the objects of the Ets table <c><anno>EtsTab</anno></c>. + The order in which the objects are inserted is not specified. Since <c>ets:safe_fixtable/2</c> is called the Ets table must be public or owned by the calling process.</p> </desc> </func> <func> - <name>info(Name) -> InfoList | undefined</name> + <name name="info" arity="1"/> <fsummary>Return information about a Dets table.</fsummary> - <type> - <v>Name = name()</v> - <v>InfoList = [{Item, Value}]</v> - </type> <desc> - <p>Returns information about the table <c>Name</c> as a list of - <c>{Item, Value}</c> tuples:</p> + <p>Returns information about the table <c><anno>Name</anno></c> + as a list of tuples:</p> <list type="bulleted"> <item> - <p><c>{file_size, int()}</c>, the size of the file in + <p><c>{file_size, integer() >= 0}</c>, the size of the file in bytes.</p> </item> <item> - <p><c>{filename, file()}</c>, the name of the file - where objects are stored.</p> + <p><c>{filename, <seealso marker="file#type-name">file:name()</seealso>}</c>, + the name of the file where objects are stored.</p> </item> <item> - <p><c>{keypos, keypos()}</c>, the position of the - key.</p> + <p><c>{keypos, <seealso marker="#type-keypos">keypos()</seealso>} + </c>, the position of the key.</p> </item> <item> - <p><c>{size, int()}</c>, the number of objects stored + <p><c>{size, integer() >= 0}</c>, the number of objects stored in the table.</p> </item> <item> - <p><c>{type, type()}</c>, the type of the table.</p> + <p><c>{type, <seealso marker="#type-type">type()</seealso>}</c>, + the type of the table.</p> </item> </list> </desc> </func> <func> - <name>info(Name, Item) -> Value | undefined</name> + <name name="info" arity="2"/> <fsummary>Return the information associated with a given item for a Dets table.</fsummary> - <type> - <v>Name = name()</v> - </type> <desc> - <p>Returns the information associated with <c>Item</c> for the - table <c>Name</c>. In addition to the <c>{Item, Value}</c> + <p>Returns the information associated with <c><anno>Item</anno></c> + for the table <c><anno>Name</anno></c>. + 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> - <p><c>{access, access()}</c>, the access mode.</p> + <p><c>{access, <seealso marker="#type-access">access()</seealso>} + </c>, the access mode.</p> </item> <item> - <p><c>{auto_save, auto_save()}</c>, the auto save - interval.</p> + <p><c>{auto_save, <seealso marker="#type-auto_save"> + auto_save()</seealso>}</c>, the auto save interval.</p> </item> <item> <p><c>{bchunk_format, binary()}</c>, an opaque binary @@ -362,21 +368,22 @@ version() = 8 | 9 | default </pre> <c>erlang:phash2/1</c> BIF is used.</p> </item> <item> - <p><c>{memory, int()}</c>, the size of the file in + <p><c>{memory, integer() >= 0}</c>, the size of the file in bytes. The same value is associated with the item <c>file_size</c>.</p> </item> <item> - <p><c>{no_keys, int()}</c>, the number of different + <p><c>{no_keys, integer >= 0()}</c>, the number of different keys stored in the table. Only available for version 9 tables.</p> </item> <item> - <p><c>{no_objects, int()}</c>, the number of objects + <p><c>{no_objects, integer >= 0()}</c>, the number of objects stored in the table.</p> </item> <item> - <p><c>{no_slots, {Min, Used, Max}}</c>, the number of + <p><c>{no_slots, {</c>Min<c>, </c>Used<c>, </c>Max<c>}}</c>, + the number of slots of the table. <c>Min</c> is the minimum number of slots, <c>Used</c> is the number of currently used slots, and <c>Max</c> is the maximum number of slots. Only @@ -387,7 +394,7 @@ version() = 8 | 9 | default </pre> handles requests to the Dets table.</p> </item> <item> - <p><c>{ram_file, bool()}</c>, whether the table is + <p><c>{ram_file, boolean()}</c>, whether the table is kept in RAM.</p> </item> <item> @@ -399,32 +406,28 @@ version() = 8 | 9 | default </pre> table is not fixed, SafeFixed is the atom <c>false</c>.</p> </item> <item> - <p><c>{version, int()}</c>, the version of the format - of the table.</p> + <p><c>{version, integer()</c>, the version of the format of + the table.</p> </item> </list> </desc> </func> <func> - <name>init_table(Name, InitFun [, Options]) -> ok | {error, Reason}</name> + <name name="init_table" arity="2"/> + <name name="init_table" arity="3"/> <fsummary>Replace all objects of a Dets table.</fsummary> - <type> - <v>Name = atom()</v> - <v>InitFun = fun(Arg) -> Res</v> - <v>Arg = read | close</v> - <v>Res = end_of_input | {[object()], InitFun} | {Data, InitFun} | term()</v> - <v>Data = binary() | tuple()</v> - </type> <desc> - <p>Replaces the existing objects of the table <c>Name</c> with - objects created by calling the input function <c>InitFun</c>, + <p>Replaces the existing objects of the table <c><anno>Name</anno></c> + with objects created by calling the input function + <c><anno>InitFun</anno></c>, see below. The reason for using this function rather than calling <c>insert/2</c> is that of efficiency. It should be noted that the input functions are called by the process that handles requests to the Dets table, not by the calling process.</p> <p>When called with the argument <c>read</c> the function - <c>InitFun</c> is assumed to return <c>end_of_input</c> when + <c><anno>InitFun</anno></c> is assumed to return + <c>end_of_input</c> when there is no more input, or <c>{Objects, Fun}</c>, where <c>Objects</c> is a list of objects and <c>Fun</c> is a new input function. Any other value Value is returned as an error @@ -448,7 +451,8 @@ version() = 8 | 9 | default </pre> item <c>no_slots</c>. See also the <c>min_no_slots</c> option below. </p> - <p>The <c>Options</c> argument is a list of <c>{Key, Val}</c> + <p>The <c><anno>Options</anno></c> argument is a list of + <c>{Key, Val}</c> tuples where the following values are allowed:</p> <list type="bulleted"> <item> @@ -461,87 +465,68 @@ version() = 8 | 9 | default </pre> </item> <item> <p><c>{format, Format}</c>. Specifies the format of the - objects returned by the function <c>InitFun</c>. If + objects returned by the function <c><anno>InitFun</anno></c>. If <c>Format</c> is <c>term</c> (the default), - <c>InitFun</c> is assumed to return a list of tuples. If - <c>Format</c> is <c>bchunk</c>, <c>InitFun</c> is - assumed to return <c>Data</c> as returned by - <c>bchunk/2</c>. This option overrides the + <c><anno>InitFun</anno></c> is assumed to return a list of tuples. If + <c>Format</c> is <c>bchunk</c>, <c><anno>InitFun</anno></c> is + assumed to return <c><anno>Data</anno></c> as returned by + <seealso marker="#bchunk/2"><c>bchunk/2</c></seealso>. + This option overrides the <c>min_no_slots</c> option.</p> </item> </list> </desc> </func> <func> - <name>insert(Name, Objects) -> ok | {error, Reason}</name> + <name name="insert" arity="2"/> <fsummary>Insert one or more objects into a Dets table.</fsummary> - <type> - <v>Name = name()</v> - <v>Objects = object() | [object()]</v> - </type> <desc> - <p>Inserts one or more objects into the table <c>Name</c>. If - there already exists an object with a key matching the key of + <p>Inserts one or more objects into the table <c><anno>Name</anno></c>. + If there already exists an object with a key matching the key of some of the given objects and the table type is <c>set</c>, the old object will be replaced.</p> </desc> </func> <func> - <name>insert_new(Name, Objects) -> Bool</name> + <name name="insert_new" arity="2"/> <fsummary>Insert one or more objects into a Dets table.</fsummary> - <type> - <v>Name = name()</v> - <v>Objects = object() | [object()]</v> - <v>Bool = bool()</v> - </type> <desc> - <p>Inserts one or more objects into the table <c>Name</c>. If - there already exists some object with a key matching the key + <p>Inserts one or more objects into the table <c><anno>Name</anno></c>. + If there already exists some object with a key matching the key of any of the given objects the table is not updated and <c>false</c> is returned, otherwise the objects are inserted and <c>true</c> returned.</p> </desc> </func> <func> - <name>is_compatible_bchunk_format(Name, BchunkFormat) -> Bool</name> + <name name="is_compatible_bchunk_format" arity="2"/> <fsummary>Test compatibility of a table's chunk data.</fsummary> - <type> - <v>Name = name()</v> - <v>BchunkFormat = binary()</v> - <v>Bool = bool()</v> - </type> <desc> <p>Returns <c>true</c> if it would be possible to initialize - the table <c>Name</c>, using <c>init_table/3</c> with the + the table <c><anno>Name</anno></c>, using + <seealso marker="#init_table/3"><c>init_table/3</c></seealso> + with the option <c>{format, bchunk}</c>, with objects read with - <c>bchunk/2</c> from some table <c>T</c> such that calling + <seealso marker="#bchunk/2"><c>bchunk/2</c></seealso> from some + table <c>T</c> such that calling <c>info(T, bchunk_format)</c> returns <c>BchunkFormat</c>.</p> </desc> </func> <func> - <name>is_dets_file(FileName) -> Bool | {error, Reason}</name> + <name name="is_dets_file" arity="1"/> <fsummary>Test for a Dets table.</fsummary> - <type> - <v>FileName = file()</v> - <v>Bool = bool()</v> - </type> <desc> - <p>Returns <c>true</c> if the file <c>FileName</c> is a Dets - table, <c>false</c> otherwise.</p> + <p>Returns <c>true</c> if the file <c><anno>Filename</anno></c> + is a Dets table, <c>false</c> otherwise.</p> </desc> </func> <func> - <name>lookup(Name, Key) -> [Object] | {error, Reason}</name> + <name name="lookup" arity="2"/> <fsummary>Return all objects with a given key stored in a Dets table.</fsummary> - <type> - <v>Key = term()</v> - <v>Name = name()</v> - <v>Object = object()</v> - </type> <desc> - <p>Returns a list of all objects with the key <c>Key</c> - stored in the table <c>Name</c>. For example:</p> + <p>Returns a list of all objects with the key <c><anno>Key</anno></c> + stored in the table <c><anno>Name</anno></c>. For example:</p> <pre> 2> <input>dets:open_file(abc, [{type, bag}]).</input> {ok,abc} @@ -562,71 +547,57 @@ ok </desc> </func> <func> - <name>match(Continuation) -> {[Match], Continuation2} | '$end_of_table' | {error, Reason}</name> + <name name="match" arity="1"/> <fsummary>Match a chunk of objects stored in a Dets table and return a list of variable bindings.</fsummary> - <type> - <v>Continuation = Continuation2 = bindings_cont()</v> - <v>Match = [term()]</v> - </type> <desc> <p>Matches some objects stored in a table and returns a non-empty list of the bindings that match a given pattern in some unspecified order. The table, the pattern, and the number of objects that are matched are all defined by - <c>Continuation</c>, which has been returned by a prior call - to <c>match/1</c> or <c>match/3</c>.</p> + <c><anno>Continuation</anno></c>, which has been returned by a prior + call to <c>match/1</c> or <c>match/3</c>.</p> <p>When all objects of the table have been matched, <c>'$end_of_table'</c> is returned.</p> </desc> </func> <func> - <name>match(Name, Pattern) -> [Match] | {error, Reason}</name> + <name name="match" arity="2"/> <fsummary>Match the objects stored in a Dets table and return a list of variable bindings.</fsummary> - <type> - <v>Name = name()</v> - <v>Pattern = tuple()</v> - <v>Match = [term()]</v> - </type> <desc> - <p>Returns for each object of the table <c>Name</c> that - matches <c>Pattern</c> a list of bindings in some unspecified - order. See <seealso marker="ets">ets(3)</seealso> for a + <p>Returns for each object of the table <c><anno>Name</anno></c> that + matches <c><anno>Pattern</anno></c> a list of bindings in some unspecified + order. See <seealso marker="ets#match/2">ets:match/2</seealso> for a description of patterns. If the keypos'th element of - <c>Pattern</c> is unbound, all objects of the table are + <c><anno>Pattern</anno></c> is unbound, all objects of the table are matched. If the keypos'th element is bound, only the objects with the right key are matched.</p> </desc> </func> <func> - <name>match(Name, Pattern, N) -> {[Match], Continuation} | '$end_of_table' | {error, Reason}</name> + <name name="match" arity="3"/> <fsummary>Match the first chunk of objects stored in a Dets table and return a list of variable bindings.</fsummary> - <type> - <v>Name = name()</v> - <v>Pattern = tuple()</v> - <v>N = default | int()</v> - <v>Match = [term()]</v> - <v>Continuation = bindings_cont()</v> - </type> <desc> - <p>Matches some or all objects of the table <c>Name</c> and + <p>Matches some or all objects of the table <c><anno>Name</anno></c> and returns a non-empty list of the bindings that match - <c>Pattern</c> in some unspecified order. See <seealso marker="ets">ets(3)</seealso> for a description of - patterns.</p> + <c><anno>Pattern</anno></c> in some unspecified order. + See <seealso marker="ets#match/2">ets:match/2</seealso> for a + description of patterns.</p> <p>A tuple of the bindings and a continuation is returned, unless the table is empty, in which case <c>'$end_of_table'</c> is returned. The continuation is to be used when matching further objects by calling - <c>match/1</c>.</p> - <p>If the keypos'th element of <c>Pattern</c> is bound, all - objects of the table are matched. If the keypos'th element is - unbound, all objects of the table are matched, <c>N</c> + <seealso marker="#match/1"><c>match/1</c></seealso>.</p> + <p>If the keypos'th element of <c><anno>Pattern</anno></c> is bound, + all objects of the table are matched. If the keypos'th element is + unbound, all objects of the table are matched, <c><anno>N</anno></c> objects at a time, until at least one object matches or the end of the table has been reached. The default, indicated by - giving <c>N</c> the value <c>default</c>, is to let the number + giving <c><anno>N</anno></c> the value <c>default</c>, + is to let the number of objects vary depending on the sizes of the objects. If - <c>Name</c> is a version 9 table, all objects with the same + <c><anno>Name</anno></c> is a version 9 table, all objects with the same key are always matched at the same time which implies that - more than N objects may sometimes be matched. + more than <anno>N</anno> objects may sometimes be matched. </p> <p>The table should always be protected using <c>safe_fixtable/2</c> before calling <c>match/3</c>, or @@ -634,15 +605,11 @@ ok </desc> </func> <func> - <name>match_delete(Name, Pattern) -> ok | {error, Reason}</name> + <name name="match_delete" arity="2"/> <fsummary>Delete all objects that match a given pattern from a Dets table.</fsummary> - <type> - <v>Name = name()</v> - <v>Pattern = tuple()</v> - </type> <desc> - <p>Deletes all objects that match <c>Pattern</c> from the - table <c>Name</c>. + <p>Deletes all objects that match <c><anno>Pattern</anno></c> from the + table <c><anno>Name</anno></c>. See <seealso marker="ets#match/2">ets:match/2</seealso> for a description of patterns.</p> <p>If the keypos'th element of <c>Pattern</c> is bound, @@ -650,17 +617,13 @@ ok </desc> </func> <func> - <name>match_object(Continuation) -> {[Object], Continuation2} | '$end_of_table' | {error, Reason}</name> + <name name="match_object" arity="1"/> <fsummary>Match a chunk of objects stored in a Dets table and return a list of objects.</fsummary> - <type> - <v>Continuation = Continuation2 = object_cont()</v> - <v>Object = object()</v> - </type> <desc> <p>Returns a non-empty list of some objects stored in a table that match a given pattern in some unspecified order. The table, the pattern, and the number of objects that are matched - are all defined by <c>Continuation</c>, which has been + are all defined by <c><anno>Continuation</anno></c>, which has been returned by a prior call to <c>match_object/1</c> or <c>match_object/3</c>.</p> <p>When all objects of the table have been matched, @@ -668,20 +631,17 @@ ok </desc> </func> <func> - <name>match_object(Name, Pattern) -> [Object] | {error, Reason}</name> + <name name="match_object" arity="2"/> <fsummary>Match the objects stored in a Dets table and return a list of objects.</fsummary> - <type> - <v>Name = name()</v> - <v>Pattern = tuple()</v> - <v>Object = object()</v> - </type> <desc> - <p>Returns a list of all objects of the table <c>Name</c> that - match <c>Pattern</c> in some unspecified order. See <seealso marker="ets">ets(3)</seealso> for a description of patterns. + <p>Returns a list of all objects of the table <c><anno>Name</anno></c> that + match <c><anno>Pattern</anno></c> in some unspecified order. + See <seealso marker="ets#match/2">ets:match/2</seealso> for a + description of patterns. </p> - <p>If the keypos'th element of <c>Pattern</c> is + <p>If the keypos'th element of <c><anno>Pattern</anno></c> is unbound, all objects of the table are matched. If the - keypos'th element of <c>Pattern</c> is bound, only the + keypos'th element of <c><anno>Pattern</anno></c> is bound, only the objects with the right key are matched.</p> <p>Using the <c>match_object</c> functions for traversing all objects of a table is more efficient than calling @@ -689,34 +649,28 @@ ok </desc> </func> <func> - <name>match_object(Name, Pattern, N) -> {[Object], Continuation} | '$end_of_table' | {error, Reason}</name> + <name name="match_object" arity="3"/> <fsummary>Match the first chunk of objects stored in a Dets table and return a list of objects.</fsummary> - <type> - <v>Name = name()</v> - <v>Pattern = tuple()</v> - <v>N = default | int()</v> - <v>Object = object()</v> - <v>Continuation = object_cont()</v> - </type> <desc> - <p>Matches some or all objects stored in the table <c>Name</c> + <p>Matches some or all objects stored in the table <c><anno>Name</anno></c> and returns a non-empty list of the objects that match - <c>Pattern</c> in some unspecified order. See <seealso marker="ets">ets(3)</seealso> for a description of - patterns.</p> + <c><anno>Pattern</anno></c> in some unspecified order. + See <seealso marker="ets#match/2">ets:match/2</seealso> for a + description of patterns.</p> <p>A list of objects and a continuation is returned, unless the table is empty, in which case <c>'$end_of_table'</c> is returned. The continuation is to be used when matching further objects by calling <c>match_object/1</c>.</p> - <p>If the keypos'th element of <c>Pattern</c> is bound, all + <p>If the keypos'th element of <c><anno>Pattern</anno></c> is bound, all objects of the table are matched. If the keypos'th element is - unbound, all objects of the table are matched, <c>N</c> + unbound, all objects of the table are matched, <c><anno>N</anno></c> objects at a time, until at least one object matches or the end of the table has been reached. The default, indicated by - giving <c>N</c> the value <c>default</c>, is to let the number + giving <c><anno>N</anno></c> the value <c>default</c>, is to let the number of objects vary depending on the sizes of the objects. If - <c>Name</c> is a version 9 table, all matching objects with + <c><anno>Name</anno></c> is a version 9 table, all matching objects with the same key are always returned in the same reply which - implies that more than N objects may sometimes be returned. + implies that more than <anno>N</anno> objects may sometimes be returned. </p> <p>The table should always be protected using <c>safe_fixtable/2</c> before calling <c>match_object/3</c>, @@ -724,43 +678,31 @@ ok </desc> </func> <func> - <name>member(Name, Key) -> Bool | {error, Reason}</name> + <name name="member" arity="2"/> <fsummary>Test for occurrence of a key in a Dets table.</fsummary> - <type> - <v>Name = name()</v> - <v>Key = term()</v> - <v>Bool = bool()</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 of the table has the key <c>Key</c>, <c>false</c> + elements of the table has the key <c><anno>Key</anno></c>, <c>false</c> otherwise.</p> </desc> </func> <func> - <name>next(Name, Key1) -> Key2 | '$end_of_table'</name> + <name name="next" arity="2"/> <fsummary>Return the next key in a Dets table.</fsummary> - <type> - <v>Name = name()</v> - <v>Key1 = Key2 = term()</v> - </type> <desc> - <p>Returns the key following <c>Key1</c> in the table - <c>Name</c> according to the table's internal order, or + <p>Returns the key following <c><anno>Key1</anno></c> in the table + <c><anno>Name</anno></c> according to the table's internal order, or <c>'$end_of_table'</c> if there is no next key.</p> <p>Should an error occur, the process is exited with an error tuple <c>{error, Reason}</c>.</p> - <p>Use <c>first/1</c> to find the first key in the table.</p> + <p>Use <seealso marker="#first/1"><c>first/1</c></seealso> to find + the first key in the table.</p> </desc> </func> <func> - <name>open_file(Filename) -> {ok, Reference} | {error, Reason}</name> + <name name="open_file" arity="1"/> <fsummary>Open an existing Dets table.</fsummary> - <type> - <v>FileName = file()</v> - <v>Reference = ref()</v> - </type> <desc> <p>Opens an existing table. If the table has not been properly closed, it will be repaired. The returned reference is to be @@ -769,15 +711,12 @@ ok </desc> </func> <func> - <name>open_file(Name, Args) -> {ok, Name} | {error, Reason}</name> + <name name="open_file" arity="2"/> <fsummary>Open a Dets table.</fsummary> - <type> - <v>Name = atom()</v> - </type> <desc> <p>Opens a table. An empty Dets table is created if no file exists.</p> - <p>The atom <c>Name</c> is the name of the table. The table + <p>The atom <c><anno>Name</anno></c> is the name of the table. The table name must be provided in all subsequent operations on the table. The name can be used by other processes as well, and several process can share one table. @@ -786,18 +725,20 @@ ok name and arguments, then the table will have two users. If one user closes the table, it still remains open until the second user closes the table.</p> - <p>The <c>Args</c> argument is a list of <c>{Key, Val}</c> + <p>The <c><anno>Args</anno></c> argument is a list of <c>{Key, Val}</c> tuples where the following values are allowed:</p> <list type="bulleted"> <item> - <p><c>{access, access()}</c>. It is possible to open + <p><c>{access, <seealso marker="#type-access"> + access()</seealso>}</c>. It is possible to open existing tables in read-only mode. A table which is opened in read-only mode is not subjected to the automatic file reparation algorithm if it is later opened after a crash. The default value is <c>read_write</c>.</p> </item> <item> - <p><c>{auto_save, auto_save()}</c>, the auto save + <p><c>{auto_save, <seealso marker="#type-auto_save"> + auto_save()</seealso>}</c>, the auto save interval. If the interval is an integer <c>Time</c>, the table is flushed to disk whenever it is not accessed for <c>Time</c> milliseconds. A table that has been flushed @@ -807,16 +748,19 @@ ok is 180000 (3 minutes).</p> </item> <item> - <p><c>{estimated_no_objects, int()}</c>. Equivalent to the + <p><c>{estimated_no_objects, <seealso marker="#type-no_slots"> + no_slots()</seealso>}</c>. Equivalent to the <c>min_no_slots</c> option.</p> </item> <item> - <p><c>{file, file()}</c>, the name of the file to be + <p><c>{file, <seealso marker="file#type-name"> + file:name()</seealso>}</c>, the name of the file to be opened. The default value is the name of the table.</p> </item> <item> - <p><c>{max_no_slots, no_slots()}</c>, the maximum number - of slots that will be used. The default value is 2 M, and + <p><c>{max_no_slots, <seealso marker="#type-no_slots"> + no_slots()</seealso>}</c>, the maximum number + of slots that will be used. The default value as well as the maximal value is 32 M. Note that a higher value may increase the fragmentation of the table, and conversely, that a smaller value may decrease the fragmentation, at @@ -824,14 +768,16 @@ ok 9 tables.</p> </item> <item> - <p><c>{min_no_slots, no_slots()}</c>. Application + <p><c>{min_no_slots, <seealso marker="#type-no_slots"> + no_slots()</seealso>}</c>. Application performance can be enhanced with this flag by specifying, when the table is created, the estimated number of different keys that will be stored in the table. The default value as well as the minimum value is 256.</p> </item> <item> - <p><c>{keypos, keypos()}</c>, the position of the + <p><c>{keypos, <seealso marker="#type-keypos"> + keypos()</seealso>}</c>, the position of the element of each object to be used as key. The default value is 1. The ability to explicitly state the key position is most convenient when we want to store Erlang @@ -839,7 +785,7 @@ ok name of the record type.</p> </item> <item> - <p><c>{ram_file, bool()}</c>, whether the table is to + <p><c>{ram_file, boolean()}</c>, whether the table is to be kept in RAM. Keeping the table in RAM may sound like an anomaly, but can enhance the performance of applications which open a table, insert a set of objects, and then @@ -849,7 +795,7 @@ ok </item> <item> <p><c>{repair, Value}</c>. <c>Value</c> can be either - a <c>bool()</c> or the atom <c>force</c>. The flag + a <c>boolean()</c> or the atom <c>force</c>. The flag specifies whether the Dets server should invoke the automatic file reparation algorithm. The default is <c>true</c>. If <c>false</c> is specified, there is no @@ -868,11 +814,12 @@ ok already open.</p> </item> <item> - <p><c>{type, type()}</c>, the type of the table. The - default value is <c>set</c>.</p> + <p><c>{type, <seealso marker="#type-type">type()</seealso>}</c>, + the type of the table. The default value is <c>set</c>.</p> </item> <item> - <p><c>{version, version()}</c>, the version of the format + <p><c>{version, <seealso marker="#type-version"> + version()</seealso>}</c>, the version of the format used for the table. The default value is <c>9</c>. Tables on the format used before OTP R8 can be created by giving the value <c>8</c>. A version 8 table can be converted to @@ -883,12 +830,8 @@ ok </desc> </func> <func> - <name>pid2name(Pid) -> {ok, Name} | undefined</name> + <name name="pid2name" arity="1"/> <fsummary>Return the name of the Dets table handled by a pid.</fsummary> - <type> - <v>Name = name()</v> - <v>Pid = pid()</v> - </type> <desc> <p>Returns the name of the table given the pid of a process that handles requests to a table, or <c>undefined</c> if @@ -897,12 +840,8 @@ ok </desc> </func> <func> - <name>repair_continuation(Continuation, MatchSpec) -> Continuation2</name> + <name name="repair_continuation" arity="2"/> <fsummary>Repair a continuation from select/1 or select/3.</fsummary> - <type> - <v>Continuation = Continuation2 = select_cont()</v> - <v>MatchSpec = match_spec()</v> - </type> <desc> <p>This function can be used to restore an opaque continuation returned by <c>select/3</c> or <c>select/1</c> if the @@ -932,14 +871,11 @@ ok </desc> </func> <func> - <name>safe_fixtable(Name, Fix)</name> + <name name="safe_fixtable" arity="2"/> <fsummary>Fix a Dets table for safe traversal.</fsummary> - <type> - <v>Name = name()</v> - <v>Fix = bool()</v> - </type> <desc> - <p>If <c>Fix</c> is <c>true</c>, the table <c>Name</c> is + <p>If <c><anno>Fix</anno></c> is <c>true</c>, the table + <c><anno>Name</anno></c> is fixed (once more) by the calling process, otherwise the table is released. The table is also released when a fixing process terminates. @@ -961,17 +897,13 @@ ok </desc> </func> <func> - <name>select(Continuation) -> {Selection, Continuation2} | '$end_of_table' | {error, Reason}</name> + <name name="select" arity="1"/> <fsummary>Apply a match specification to some objects stored in a Dets table.</fsummary> - <type> - <v>Continuation = Continuation2 = select_cont()</v> - <v>Selection = [term()]</v> - </type> <desc> <p>Applies a match specification to some objects stored in a table and returns a non-empty list of the results. The table, the match specification, and the number of objects - that are matched are all defined by <c>Continuation</c>, + that are matched are all defined by <c><anno>Continuation</anno></c>, which has been returned by a prior call to <c>select/1</c> or <c>select/3</c>.</p> <p>When all objects of the table have been matched, @@ -979,20 +911,15 @@ ok </desc> </func> <func> - <name>select(Name, MatchSpec) -> Selection | {error, Reason}</name> + <name name="select" arity="2"/> <fsummary>Apply a match specification to all objects stored in a Dets table.</fsummary> - <type> - <v>Name = name()</v> - <v>MatchSpec = match_spec()</v> - <v>Selection = [term()]</v> - </type> <desc> <p>Returns the results of applying the match specification - <c>MatchSpec</c> to all or some objects stored in the table - <c>Name</c>. The order of the objects is not specified. See + <c><anno>MatchSpec</anno></c> to all or some objects stored in the table + <c><anno>Name</anno></c>. The order of the objects is not specified. See the ERTS User's Guide for a description of match specifications.</p> - <p>If the keypos'th element of <c>MatchSpec</c> is + <p>If the keypos'th element of <c><anno>MatchSpec</anno></c> is unbound, the match specification is applied to all objects of the table. If the keypos'th element is bound, the match specification is applied to the objects with the right key(s) @@ -1004,19 +931,12 @@ ok </desc> </func> <func> - <name>select(Name, MatchSpec, N) -> {Selection, Continuation} | '$end_of_table' | {error, Reason}</name> + <name name="select" arity="3"/> <fsummary>Apply a match specification to the first chunk of objects stored in a Dets table.</fsummary> - <type> - <v>Name = name()</v> - <v>MatchSpec = match_spec()</v> - <v>N = default | int()</v> - <v>Selection = [term()]</v> - <v>Continuation = select_cont()</v> - </type> <desc> <p>Returns the results of applying the match specification - <c>MatchSpec</c> to some or all objects stored in the table - <c>Name</c>. The order of the objects is not specified. See + <c><anno>MatchSpec</anno></c> to some or all objects stored in the table + <c><anno>Name</anno></c>. The order of the objects is not specified. See the ERTS User's Guide for a description of match specifications.</p> <p>A tuple of the results of applying the match specification @@ -1024,18 +944,18 @@ ok in which case <c>'$end_of_table'</c> is returned. The continuation is to be used when matching further objects by calling <c>select/1</c>.</p> - <p>If the keypos'th element of <c>MatchSpec</c> is bound, the + <p>If the keypos'th element of <c><anno>MatchSpec</anno></c> is bound, the match specification is applied to all objects of the table with the right key(s). If the keypos'th element of - <c>MatchSpec</c> is unbound, the match specification is - applied to all objects of the table, <c>N</c> objects at a + <c><anno>MatchSpec</anno></c> is unbound, the match specification is + applied to all objects of the table, <c><anno>N</anno></c> objects at a time, until at least one object matches or the end of the table has been reached. The default, indicated by giving - <c>N</c> the value <c>default</c>, is to let the number of + <c><anno>N</anno></c> the value <c>default</c>, is to let the number of objects vary depending on the sizes of the objects. If - <c>Name</c> is a version 9 table, all objects with the same + <c><anno>Name</anno></c> is a version 9 table, all objects with the same key are always handled at the same time which implies that the - match specification may be applied to more than N objects. + match specification may be applied to more than <anno>N</anno> objects. </p> <p>The table should always be protected using <c>safe_fixtable/2</c> before calling <c>select/3</c>, or @@ -1043,48 +963,35 @@ ok </desc> </func> <func> - <name>select_delete(Name, MatchSpec) -> N | {error, Reason}</name> + <name name="select_delete" arity="2"/> <fsummary>Delete all objects that match a given pattern from a Dets table.</fsummary> - <type> - <v>Name = name()</v> - <v>MatchSpec = match_spec()</v> - <v>N = int()</v> - </type> <desc> - <p>Deletes each object from the table <c>Name</c> such that - applying the match specification <c>MatchSpec</c> to the + <p>Deletes each object from the table <c><anno>Name</anno></c> such that + applying the match specification <c><anno>MatchSpec</anno></c> to the object returns the value <c>true</c>. See the ERTS User's Guide for a description of match specifications. Returns the number of deleted objects.</p> - <p>If the keypos'th element of <c>MatchSpec</c> is + <p>If the keypos'th element of <c><anno>MatchSpec</anno></c> is bound, the match specification is applied to the objects with the right key(s) only.</p> </desc> </func> <func> - <name>slot(Name, I) -> '$end_of_table' | [Object] | {error, Reason}</name> + <name name="slot" arity="2"/> <fsummary>Return the list of objects associated with a slot of a Dets table.</fsummary> - <type> - <v>Name = name()</v> - <v>I = int()</v> - <v>Object = object()</v> - </type> <desc> <p>The objects of a table are distributed among slots, starting with slot <c>0</c> and ending with slot n. This function returns the list of objects associated with slot - <c>I</c>. If <c>I</c> is greater than n <c>'$end_of_table'</c> - is returned.</p> + <c><anno>I</anno></c>. If <c><anno>I</anno></c> is greater than n + <c>'$end_of_table'</c> is returned.</p> </desc> </func> <func> - <name>sync(Name) -> ok | {error, Reason}</name> + <name name="sync" arity="1"/> <fsummary>Ensure that all updates made to a Dets table are written to disk.</fsummary> - <type> - <v>Name = name()</v> - </type> <desc> - <p>Ensures that all updates made to the table <c>Name</c> are + <p>Ensures that all updates made to the table <c><anno>Name</anno></c> are written to disk. This also applies to tables which have been opened with the <c>ram_file</c> flag set to <c>true</c>. In this case, the contents of the RAM file are flushed to @@ -1095,25 +1002,17 @@ ok </desc> </func> <func> - <name>table(Name [, Options]) -> QueryHandle</name> + <name name="table" arity="1"/> + <name name="table" arity="2"/> <fsummary>Return a QLC query handle.</fsummary> - <type> - <v>Name = name()</v> - <v>QueryHandle = - a query handle, see qlc(3) -</v> - <v>Options = [Option] | Option</v> - <v>Option = {n_objects, Limit} | {traverse, TraverseMethod}</v> - <v>Limit = default | integer() >= 1</v> - <v>TraverseMethod = first_next | select | {select, MatchSpec}</v> - <v>MatchSpec = match_spec()</v> - </type> <desc> - <p> <marker id="qlc_table"></marker> -Returns a QLC (Query List + <p><marker id="qlc_table"></marker> + Returns a QLC (Query List Comprehension) query handle. The module <c>qlc</c> implements a query language aimed mainly at Mnesia but Ets tables, Dets tables, and lists are also recognized by <c>qlc</c> as sources of data. Calling <c>dets:table/1,2</c> is the - means to make the Dets table <c>Name</c> usable to <c>qlc</c>.</p> + means to make the Dets table <c><anno>Name</anno></c> usable to <c>qlc</c>.</p> <p>When there are only simple restrictions on the key position <c>qlc</c> uses <c>dets:lookup/2</c> to look up the keys, but when that is not possible the whole table is traversed. The @@ -1137,7 +1036,8 @@ Returns a QLC (Query List specification that matches all objects.</p> </item> <item> - <p><c>{select, MatchSpec}</c>. As for <c>select</c> + <p><c>{select, <seealso marker="#type-match_spec"> + match_spec()}</seealso></c>. As for <c>select</c> the table is traversed by calling <c>dets:select/3</c> and <c>dets:select/1</c>. The difference is that the match specification is explicitly given. This is how to @@ -1166,35 +1066,23 @@ true </pre> </desc> </func> <func> - <name>to_ets(Name, EtsTab) -> EtsTab | {error, Reason}</name> + <name name="to_ets" arity="2"/> <fsummary>Insert all objects of a Dets table into an Ets table.</fsummary> - <type> - <v>Name = name()</v> - <v>EtsTab = - see ets(3) -</v> - </type> <desc> - <p>Inserts the objects of the Dets table <c>Name</c> into the - Ets table <c>EtsTab</c>. The order in which the objects are + <p>Inserts the objects of the Dets table <c><anno>Name</anno></c> into the + Ets table <c><anno>EtsTab</anno></c>. The order in which the objects are inserted is not specified. The existing objects of the Ets table are kept unless overwritten.</p> </desc> </func> <func> - <name>traverse(Name, Fun) -> Return | {error, Reason}</name> + <name name="traverse" arity="2"/> <fsummary>Apply a function to all or some objects stored in a Dets table.</fsummary> - <type> - <v>Fun = fun(Object) -> FunReturn</v> - <v>FunReturn = continue | {continue, Val} | {done, Value}</v> - <v>Val = Value = term()</v> - <v>Name = name()</v> - <v>Object = object()</v> - <v>Return = [term()]</v> - </type> <desc> - <p>Applies <c>Fun</c> to each object stored in the table - <c>Name</c> in some unspecified order. Different actions are - taken depending on the return value of <c>Fun</c>. The - following <c>Fun</c> return values are allowed:</p> + <p>Applies <c><anno>Fun</anno></c> to each object stored in the table + <c><anno>Name</anno></c> in some unspecified order. Different actions are + taken depending on the return value of <c><anno>Fun</anno></c>. The + following <c><anno>Fun</anno></c> return values are allowed:</p> <taglist> <tag><c>continue</c></tag> <item> @@ -1206,35 +1094,31 @@ fun(X) -> io:format("~p~n", [X]), continue end. </pre> </item> <tag><c>{continue, Val}</c></tag> <item> - <p>Continue the traversal and accumulate <c>Val</c>. The + <p>Continue the traversal and accumulate <c><anno>Val</anno></c>. The following function is supplied in order to collect all objects of a table in a list: </p> <pre> fun(X) -> {continue, X} end. </pre> </item> - <tag><c>{done, Value}</c></tag> + <tag><c>{done, <anno>Value</anno>}</c></tag> <item> - <p>Terminate the traversal and return <c>[Value | Acc]</c>.</p> + <p>Terminate the traversal and return <c>[<anno>Value</anno> | Acc]</c>.</p> </item> </taglist> - <p>Any other value returned by <c>Fun</c> terminates the + <p>Any other value returned by <c><anno>Fun</anno></c> terminates the traversal and is immediately returned. </p> </desc> </func> <func> - <name>update_counter(Name, Key, Increment) -> Result</name> + <name name="update_counter" arity="3"/> <fsummary>Update a counter object stored in a Dets table.</fsummary> - <type> - <v>Name = name()</v> - <v>Key = term()</v> - <v>Increment = {Pos, Incr} | Incr</v> - <v>Pos = Incr = Result = integer()</v> - </type> <desc> - <p>Updates the object with key <c>Key</c> stored in the table - <c>Name</c> of type <c>set</c> by adding <c>Incr</c> to the - element at the <c>Pos</c>:th position. The new counter value + <p>Updates the object with key <c><anno>Key</anno></c> stored in the + table <c><anno>Name</anno></c> of type <c>set</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 is updated.</p> <p>This functions provides a way of updating a counter, @@ -1252,4 +1136,3 @@ fun(X) -> {continue, X} end. </pre> <seealso marker="qlc">qlc(3)</seealso></p> </section> </erlref> - diff --git a/lib/stdlib/doc/src/dict.xml b/lib/stdlib/doc/src/dict.xml index ebcd2eed09..b01acd02bf 100644 --- a/lib/stdlib/doc/src/dict.xml +++ b/lib/stdlib/doc/src/dict.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2009</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -39,176 +39,120 @@ they do not compare equal (<c>==</c>).</p> </description> - <section> - <title>DATA TYPES</title> - <code type="none"> -dictionary() - as returned by new/0</code> - </section> + <datatypes> + <datatype> + <name><marker id="type-dict">dict()</marker></name> + <desc><p>Dictionary as returned by <c>new/0</c>.</p></desc> + </datatype> + </datatypes> <funcs> <func> - <name>append(Key, Value, Dict1) -> Dict2</name> + <name name="append" arity="3"/> <fsummary>Append a value to keys in a dictionary</fsummary> - <type> - <v>Key = Value = term()</v> - <v>Dict1 = Dict2 = dictionary()</v> - </type> <desc> - <p>This function appends a new <c>Value</c> to the current list - of values associated with <c>Key</c>. An exception is - generated if the initial value associated with <c>Key</c> is - not a list of values.</p> + <p>This function appends a new <c><anno>Value</anno></c> to the current list + of values associated with <c><anno>Key</anno></c>.</p> </desc> </func> <func> - <name>append_list(Key, ValList, Dict1) -> Dict2</name> + <name name="append_list" arity="3"/> <fsummary>Append new values to keys in a dictionary</fsummary> - <type> - <v>ValList = [Value]</v> - <v>Key = Value = term()</v> - <v>Dict1 = Dict2 = dictionary()</v> - </type> <desc> - <p>This function appends a list of values <c>ValList</c> to - the current list of values associated with <c>Key</c>. An + <p>This function appends a list of values <c><anno>ValList</anno></c> to + the current list of values associated with <c><anno>Key</anno></c>. An exception is generated if the initial value associated with - <c>Key</c> is not a list of values.</p> + <c><anno>Key</anno></c> is not a list of values.</p> </desc> </func> <func> - <name>erase(Key, Dict1) -> Dict2</name> + <name name="erase" arity="2"/> <fsummary>Erase a key from a dictionary</fsummary> - <type> - <v>Key = term()</v> - <v>Dict1 = Dict2 = dictionary()</v> - </type> <desc> <p>This function erases all items with a given key from a dictionary.</p> </desc> </func> <func> - <name>fetch(Key, Dict) -> Value</name> + <name name="fetch" arity="2"/> <fsummary>Look-up values in a dictionary</fsummary> - <type> - <v>Key = Value = term()</v> - <v>Dict = dictionary()</v> - </type> <desc> - <p>This function returns the value associated with <c>Key</c> - in the dictionary <c>Dict</c>. <c>fetch</c> assumes that - the <c>Key</c> is present in the dictionary and an exception - is generated if <c>Key</c> is not in the dictionary.</p> + <p>This function returns the value associated with <c><anno>Key</anno></c> + in the dictionary <c><anno>Dict</anno></c>. <c>fetch</c> assumes that + the <c><anno>Key</anno></c> is present in the dictionary and an exception + is generated if <c><anno>Key</anno></c> is not in the dictionary.</p> </desc> </func> <func> - <name>fetch_keys(Dict) -> Keys</name> + <name name="fetch_keys" arity="1"/> <fsummary>Return all keys in a dictionary</fsummary> - <type> - <v>Dict = dictionary()</v> - <v>Keys = [term()]</v> - </type> <desc> <p>This function returns a list of all keys in the dictionary.</p> </desc> </func> <func> - <name>filter(Pred, Dict1) -> Dict2</name> + <name name="filter" arity="2"/> <fsummary>Choose elements which satisfy a predicate</fsummary> - <type> - <v>Pred = fun(Key, Value) -> bool()</v> - <v> Key = Value = term()</v> - <v>Dict1 = Dict2 = dictionary()</v> - </type> <desc> - <p><c>Dict2</c> is a dictionary of all keys and values in - <c>Dict1</c> for which <c>Pred(Key, Value)</c> is <c>true</c>.</p> + <p><c><anno>Dict2</anno></c> is a dictionary of all keys and values in + <c><anno>Dict1</anno></c> for which <c><anno>Pred</anno>(<anno>Key</anno>, <anno>Value</anno>)</c> is <c>true</c>.</p> </desc> </func> <func> - <name>find(Key, Dict) -> {ok, Value} | error</name> + <name name="find" arity="2"/> <fsummary>Search for a key in a dictionary</fsummary> - <type> - <v>Key = Value = term()</v> - <v>Dict = dictionary()</v> - </type> <desc> <p>This function searches for a key in a dictionary. Returns - <c>{ok, Value}</c> where <c>Value</c> is the value associated - with <c>Key</c>, or <c>error</c> if the key is not present in + <c>{ok, <anno>Value</anno>}</c> where <c><anno>Value</anno></c> is the value associated + with <c><anno>Key</anno></c>, or <c>error</c> if the key is not present in the dictionary.</p> </desc> </func> <func> - <name>fold(Fun, Acc0, Dict) -> Acc1</name> + <name name="fold" arity="3"/> <fsummary>Fold a function over a dictionary</fsummary> - <type> - <v>Fun = fun(Key, Value, AccIn) -> AccOut</v> - <v>Key = Value = term()</v> - <v>Acc0 = Acc1 = AccIn = AccOut = term()</v> - <v>Dict = dictionary()</v> - </type> <desc> - <p>Calls <c>Fun</c> on successive keys and values of - <c>Dict</c> together with an extra argument <c>Acc</c> - (short for accumulator). <c>Fun</c> must return a new - accumulator which is passed to the next call. <c>Acc0</c> is + <p>Calls <c><anno>Fun</anno></c> on successive keys and values of + <c><anno>Dict</anno></c> together with an extra argument <c>Acc</c> + (short for accumulator). <c><anno>Fun</anno></c> must return a new + accumulator which is passed to the next call. <c><anno>Acc0</anno></c> is returned if the list is empty. The evaluation order is undefined.</p> </desc> </func> <func> - <name>from_list(List) -> Dict</name> + <name name="from_list" arity="1"/> <fsummary>Convert a list of pairs to a dictionary</fsummary> - <type> - <v>List = [{Key, Value}]</v> - <v>Dict = dictionary()</v> - </type> <desc> - <p>This function converts the key/value list <c>List</c> to a - dictionary.</p> + <p>This function converts the <c><anno>Key</anno></c> - <c><anno>Value</anno></c> list + <c><anno>List</anno></c> to a dictionary.</p> </desc> </func> <func> - <name>is_key(Key, Dict) -> bool()</name> + <name name="is_key" arity="2"/> <fsummary>Test if a key is in a dictionary</fsummary> - <type> - <v>Key = term()</v> - <v>Dict = dictionary()</v> - </type> <desc> - <p>This function tests if <c>Key</c> is contained in - the dictionary <c>Dict</c>.</p> + <p>This function tests if <c><anno>Key</anno></c> is contained in + the dictionary <c><anno>Dict</anno></c>.</p> </desc> </func> <func> - <name>map(Fun, Dict1) -> Dict2</name> + <name name="map" arity="2"/> <fsummary>Map a function over a dictionary</fsummary> - <type> - <v>Fun = fun(Key, Value1) -> Value2</v> - <v> Key = Value1 = Value2 = term()</v> - <v>Dict1 = Dict2 = dictionary()</v> - </type> <desc> - <p><c>map</c> calls <c>Func</c> on successive keys and values - of <c>Dict</c> to return a new value for each key. + <p><c>map</c> calls <c><anno>Fun</anno></c> on successive keys and values + of <c><anno>Dict1</anno></c> to return a new value for each key. The evaluation order is undefined.</p> </desc> </func> <func> - <name>merge(Fun, Dict1, Dict2) -> Dict3</name> + <name name="merge" arity="3"/> <fsummary>Merge two dictionaries</fsummary> - <type> - <v>Fun = fun(Key, Value1, Value2) -> Value</v> - <v> Key = Value1 = Value2 = Value3 = term()</v> - <v>Dict1 = Dict2 = Dict3 = dictionary()</v> - </type> <desc> - <p><c>merge</c> merges two dictionaries, <c>Dict1</c> and - <c>Dict2</c>, to create a new dictionary. All the <c>Key</c> - - <c>Value</c> pairs from both dictionaries are included in + <p><c>merge</c> merges two dictionaries, <c><anno>Dict1</anno></c> and + <c><anno>Dict2</anno></c>, to create a new dictionary. All the <c><anno>Key</anno></c> + - <c><anno>Value</anno></c> pairs from both dictionaries are included in the new dictionary. If a key occurs in both dictionaries then - <c>Fun</c> is called with the key and both values to return a + <c><anno>Fun</anno></c> is called with the key and both values to return a new value. <c>merge</c> could be defined as:</p> <code type="none"> merge(Fun, D1, D2) -> @@ -219,75 +163,52 @@ merge(Fun, D1, D2) -> </desc> </func> <func> - <name>new() -> dictionary()</name> + <name name="new" arity="0"/> <fsummary>Create a dictionary</fsummary> <desc> <p>This function creates a new dictionary.</p> </desc> </func> <func> - <name>size(Dict) -> int()</name> + <name name="size" arity="1"/> <fsummary>Return the number of elements in a dictionary</fsummary> - <type> - <v>Dict = dictionary()</v> - </type> <desc> - <p>Returns the number of elements in a <c>Dict</c>.</p> + <p>Returns the number of elements in a <c><anno>Dict</anno></c>.</p> </desc> </func> <func> - <name>store(Key, Value, Dict1) -> Dict2</name> + <name name="store" arity="3"/> <fsummary>Store a value in a dictionary</fsummary> - <type> - <v>Key = Value = term()</v> - <v>Dict1 = Dict2 = dictionary()</v> - </type> <desc> - <p>This function stores a <c>Key</c> - <c>Value</c> pair in a - dictionary. If the <c>Key</c> already exists in <c>Dict1</c>, - the associated value is replaced by <c>Value</c>.</p> + <p>This function stores a <c><anno>Key</anno></c> - <c><anno>Value</anno></c> pair in a + dictionary. If the <c><anno>Key</anno></c> already exists in <c><anno>Dict1</anno></c>, + the associated value is replaced by <c><anno>Value</anno></c>.</p> </desc> </func> <func> - <name>to_list(Dict) -> List</name> + <name name="to_list" arity="1"/> <fsummary>Convert a dictionary to a list of pairs</fsummary> - <type> - <v>Dict = dictionary()</v> - <v>List = [{Key, Value}]</v> - </type> <desc> <p>This function converts the dictionary to a list representation.</p> </desc> </func> <func> - <name>update(Key, Fun, Dict1) -> Dict2</name> + <name name="update" arity="3"/> <fsummary>Update a value in a dictionary</fsummary> - <type> - <v>Key = term()</v> - <v>Fun = fun(Value1) -> Value2</v> - <v> Value1 = Value2 = term()</v> - <v>Dict1 = Dict2 = dictionary()</v> - </type> <desc> - <p>Update the a value in a dictionary by calling <c>Fun</c> on + <p>Update a value in a dictionary by calling <c><anno>Fun</anno></c> on the value to get a new value. An exception is generated if - <c>Key</c> is not present in the dictionary.</p> + <c><anno>Key</anno></c> is not present in the dictionary.</p> </desc> </func> <func> - <name>update(Key, Fun, Initial, Dict1) -> Dict2</name> + <name name="update" arity="4"/> <fsummary>Update a value in a dictionary</fsummary> - <type> - <v>Key = Initial = term()</v> - <v>Fun = fun(Value1) -> Value2</v> - <v> Value1 = Value2 = term()</v> - <v>Dict1 = Dict2 = dictionary()</v> - </type> <desc> - <p>Update the a value in a dictionary by calling <c>Fun</c> on - the value to get a new value. If <c>Key</c> is not present - in the dictionary then <c>Initial</c> will be stored as + <p>Update a value in a dictionary by calling <c><anno>Fun</anno></c> on + the value to get a new value. If <c><anno>Key</anno></c> is not present + in the dictionary then <c><anno>Initial</anno></c> will be stored as the first value. For example <c>append/3</c> could be defined as:</p> <code type="none"> @@ -296,17 +217,12 @@ append(Key, Val, D) -> </desc> </func> <func> - <name>update_counter(Key, Increment, Dict1) -> Dict2</name> + <name name="update_counter" arity="3"/> <fsummary>Increment a value in a dictionary</fsummary> - <type> - <v>Key = term()</v> - <v>Increment = number()</v> - <v>Dict1 = Dict2 = dictionary()</v> - </type> <desc> - <p>Add <c>Increment</c> to the value associated with <c>Key</c> - and store this value. If <c>Key</c> is not present in - the dictionary then <c>Increment</c> will be stored as + <p>Add <c><anno>Increment</anno></c> to the value associated with <c><anno>Key</anno></c> + and store this value. If <c><anno>Key</anno></c> is not present in + the dictionary then <c><anno>Increment</anno></c> will be stored as the first value.</p> <p>This could be defined as:</p> <code type="none"> diff --git a/lib/stdlib/doc/src/digraph.xml b/lib/stdlib/doc/src/digraph.xml index ad256e671f..0afc70ebe0 100644 --- a/lib/stdlib/doc/src/digraph.xml +++ b/lib/stdlib/doc/src/digraph.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2009</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -87,67 +87,79 @@ is a digraph that has no cycles. </p> </description> + <datatypes> + <datatype> + <name name="d_type"/> + </datatype> + <datatype> + <name name="d_cyclicity"/> + </datatype> + <datatype> + <name name="d_protection"/> + </datatype> + <datatype> + <name><marker id="type-digraph">digraph()</marker></name> + <desc><p>A digraph as returned by <c>new/0,1</c>.</p></desc> + </datatype> + <datatype> + <name><marker id="type-edge">edge()</marker></name> + </datatype> + <datatype> + <name name="label"/> + </datatype> + <datatype> + <name><marker id="type-vertex">vertex()</marker></name> + </datatype> + </datatypes> <funcs> <func> - <name>add_edge(G, E, V1, V2, Label) -> edge() | {error, Reason}</name> - <name>add_edge(G, V1, V2, Label) -> edge() | {error, Reason}</name> - <name>add_edge(G, V1, V2) -> edge() | {error, Reason}</name> + <name name="add_edge" arity="3"/> + <name name="add_edge" arity="4"/> + <name name="add_edge" arity="5"/> <fsummary>Add an edge to a digraph.</fsummary> - <type> - <v>G = digraph()</v> - <v>E = edge()</v> - <v>V1 = V2 = vertex()</v> - <v>Label = label()</v> - <v>Reason = {bad_edge, Path} | {bad_vertex, V}</v> - <v>Path = [vertex()]</v> - </type> + <type name="add_edge_err_rsn"/> <desc> - <p><c>add_edge/5</c> creates (or modifies) the edge <c>E</c> - of the digraph <c>G</c>, using <c>Label</c> as the (new) + <p><c>add_edge/5</c> creates (or modifies) the edge <c><anno>E</anno></c> + of the digraph <c><anno>G</anno></c>, using <c><anno>Label</anno></c> as the (new) <seealso marker="#label">label</seealso> of the edge. The edge is <seealso marker="#emanate">emanating</seealso> from - <c>V1</c> and <seealso marker="#incident">incident</seealso> - on <c>V2</c>. Returns <c>E</c>. + <c><anno>V1</anno></c> and <seealso marker="#incident">incident</seealso> + on <c><anno>V2</anno></c>. Returns <c><anno>E</anno></c>. </p> - <p><c>add_edge(G, V1, V2, Label)</c> is + <p><c>add_edge(<anno>G</anno>, <anno>V1</anno>, <anno>V2</anno>, <anno>Label</anno>)</c> is equivalent to - <c>add_edge(G, E, V1, V2, Label)</c>, - where <c>E</c> is a created edge. The created edge is + <c>add_edge(<anno>G</anno>, <anno>E</anno>, <anno>V1</anno>, <anno>V2</anno>, <anno>Label</anno>)</c>, + where <c><anno>E</anno></c> is a created edge. The created edge is represented by the term <c>['$e' | N]</c>, where N is an integer >= 0. </p> - <p><c>add_edge(G, V1, V2)</c> is equivalent to - <c>add_edge(G, V1, V2, [])</c>. + <p><c>add_edge(<anno>G</anno>, <anno>V1</anno>, <anno>V2</anno>)</c> is equivalent to + <c>add_edge(<anno>G</anno>, <anno>V1</anno>, <anno>V2</anno>, [])</c>. </p> <p>If the edge would create a cycle in an <seealso marker="#acyclic_digraph">acyclic digraph</seealso>, - then <c>{error, {bad_edge, Path}}</c> is returned. If - either of <c>V1</c> or <c>V2</c> is not a vertex of the - digraph <c>G</c>, then - <c>{error, {bad_vertex, </c>V<c>}}</c> is - returned, V = <c>V1</c> or - V = <c>V2</c>. + then <c>{error, {bad_edge, <anno>Path</anno>}}</c> is returned. If + either of <c><anno>V1</anno></c> or <c><anno>V2</anno></c> is not a vertex of the + digraph <c><anno>G</anno></c>, then + <c>{error, {bad_vertex, </c><anno>V</anno><c>}}</c> is + returned, <anno>V</anno> = <c><anno>V1</anno></c> or + <anno>V</anno> = <c><anno>V2</anno></c>. </p> </desc> </func> <func> - <name>add_vertex(G, V, Label) -> vertex()</name> - <name>add_vertex(G, V) -> vertex()</name> - <name>add_vertex(G) -> vertex()</name> + <name name="add_vertex" arity="1"/> + <name name="add_vertex" arity="2"/> + <name name="add_vertex" arity="3"/> <fsummary>Add or modify a vertex of a digraph.</fsummary> - <type> - <v>G = digraph()</v> - <v>V = vertex()</v> - <v>Label = label()</v> - </type> <desc> - <p><c>add_vertex/3</c> creates (or modifies) the vertex <c>V</c> - of the digraph <c>G</c>, using <c>Label</c> as the (new) + <p><c>add_vertex/3</c> creates (or modifies) the vertex <c><anno>V</anno></c> + of the digraph <c><anno>G</anno></c>, using <c><anno>Label</anno></c> as the (new) <seealso marker="#label">label</seealso> of the - vertex. Returns <c>V</c>. + vertex. Returns <c><anno>V</anno></c>. </p> - <p><c>add_vertex(G, V)</c> is equivalent to - <c>add_vertex(G, V, [])</c>. + <p><c>add_vertex(<anno>G</anno>, <anno>V</anno>)</c> is equivalent to + <c>add_vertex(<anno>G</anno>, <anno>V</anno>, [])</c>. </p> <p><c>add_vertex/1</c> creates a vertex using the empty list as label, and returns the created vertex. The created vertex @@ -157,304 +169,227 @@ </desc> </func> <func> - <name>del_edge(G, E) -> true</name> + <name name="del_edge" arity="2"/> <fsummary>Delete an edge from a digraph.</fsummary> - <type> - <v>G = digraph()</v> - <v>E = edge()</v> - </type> <desc> - <p>Deletes the edge <c>E</c> from the digraph <c>G</c>. + <p>Deletes the edge <c><anno>E</anno></c> from the digraph <c><anno>G</anno></c>. </p> </desc> </func> <func> - <name>del_edges(G, Edges) -> true</name> + <name name="del_edges" arity="2"/> <fsummary>Delete edges from a digraph.</fsummary> - <type> - <v>G = digraph()</v> - <v>Edges = [edge()]</v> - </type> <desc> - <p>Deletes the edges in the list <c>Edges</c> from the digraph - <c>G</c>. + <p>Deletes the edges in the list <c><anno>Edges</anno></c> from the digraph + <c><anno>G</anno></c>. </p> </desc> </func> <func> - <name>del_path(G, V1, V2) -> true</name> + <name name="del_path" arity="3"/> <fsummary>Delete paths from a digraph.</fsummary> - <type> - <v>G = digraph()</v> - <v>V1 = V2 = vertex()</v> - </type> <desc> - <p>Deletes edges from the digraph <c>G</c> until there are no + <p>Deletes edges from the digraph <c><anno>G</anno></c> until there are no <seealso marker="#path">paths</seealso> from the vertex - <c>V1</c> to the vertex <c>V2</c>. + <c><anno>V1</anno></c> to the vertex <c><anno>V2</anno></c>. </p> <p>A sketch of the procedure employed: Find an arbitrary <seealso marker="#simple_path">simple path</seealso> - v[1], v[2], ..., v[k] from <c>V1</c> to - <c>V2</c> in <c>G</c>. Remove all edges of - <c>G</c> <seealso marker="#emanate">emanating</seealso> from v[i] + v[1], v[2], ..., v[k] from <c><anno>V1</anno></c> to + <c><anno>V2</anno></c> in <c><anno>G</anno></c>. Remove all edges of + <c><anno>G</anno></c> <seealso marker="#emanate">emanating</seealso> from v[i] and <seealso marker="#incident">incident</seealso> to v[i+1] for 1 <= i < k (including multiple - edges). Repeat until there is no path between <c>V1</c> and - <c>V2</c>. + edges). Repeat until there is no path between <c><anno>V1</anno></c> and + <c><anno>V2</anno></c>. </p> </desc> </func> <func> - <name>del_vertex(G, V) -> true</name> + <name name="del_vertex" arity="2"/> <fsummary>Delete a vertex from a digraph.</fsummary> - <type> - <v>G = digraph()</v> - <v>V = vertex()</v> - </type> <desc> - <p>Deletes the vertex <c>V</c> from the digraph <c>G</c>. Any + <p>Deletes the vertex <c><anno>V</anno></c> from the digraph <c><anno>G</anno></c>. Any edges <seealso marker="#emanate">emanating</seealso> from - <c>V</c> or <seealso marker="#incident">incident</seealso> - on <c>V</c> are also deleted. + <c><anno>V</anno></c> or <seealso marker="#incident">incident</seealso> + on <c><anno>V</anno></c> are also deleted. </p> </desc> </func> <func> - <name>del_vertices(G, Vertices) -> true</name> + <name name="del_vertices" arity="2"/> <fsummary>Delete vertices from a digraph.</fsummary> - <type> - <v>G = digraph()</v> - <v>Vertices = [vertex()]</v> - </type> <desc> - <p>Deletes the vertices in the list <c>Vertices</c> from the - digraph <c>G</c>. + <p>Deletes the vertices in the list <c><anno>Vertices</anno></c> from the + digraph <c><anno>G</anno></c>. </p> </desc> </func> <func> - <name>delete(G) -> true</name> + <name name="delete" arity="1"/> <fsummary>Delete a digraph.</fsummary> - <type> - <v>G = digraph()</v> - </type> <desc> - <p>Deletes the digraph <c>G</c>. This call is important - because digraphs are implemented with <c>Ets</c>. There is - no garbage collection of <c>Ets</c> tables. The digraph + <p>Deletes the digraph <c><anno>G</anno></c>. This call is important + because digraphs are implemented with <c>ETS</c>. There is + no garbage collection of <c>ETS</c> tables. The digraph will, however, be deleted if the process that created the digraph terminates. </p> </desc> </func> <func> - <name>edge(G, E) -> {E, V1, V2, Label} | false</name> + <name name="edge" arity="2"/> <fsummary>Return the vertices and the label of an edge of a digraph.</fsummary> - <type> - <v>G = digraph()</v> - <v>E = edge()</v> - <v>V1 = V2 = vertex()</v> - <v>Label = label()</v> - </type> <desc> - <p>Returns <c>{E, V1, V2, Label}</c> where - <c>Label</c> is the <seealso marker="#label">label</seealso> + <p>Returns <c>{<anno>E</anno>, <anno>V1</anno>, <anno>V2</anno>, <anno>Label</anno>}</c> where + <c><anno>Label</anno></c> is the <seealso marker="#label">label</seealso> of the edge - <c>E</c> <seealso marker="#emanate">emanating</seealso> from - <c>V1</c> and <seealso marker="#incident">incident</seealso> on - <c>V2</c> of the digraph <c>G</c>. - If there is no edge <c>E</c> of the - digraph <c>G</c>, then <c>false</c> is returned. + <c><anno>E</anno></c> <seealso marker="#emanate">emanating</seealso> from + <c><anno>V1</anno></c> and <seealso marker="#incident">incident</seealso> on + <c><anno>V2</anno></c> of the digraph <c><anno>G</anno></c>. + If there is no edge <c><anno>E</anno></c> of the + digraph <c><anno>G</anno></c>, then <c>false</c> is returned. </p> </desc> </func> <func> - <name>edges(G) -> Edges</name> + <name name="edges" arity="1"/> <fsummary>Return all edges of a digraph.</fsummary> - <type> - <v>G = digraph()</v> - <v>Edges = [edge()]</v> - </type> <desc> - <p>Returns a list of all edges of the digraph <c>G</c>, in + <p>Returns a list of all edges of the digraph <c><anno>G</anno></c>, in some unspecified order. </p> </desc> </func> <func> - <name>edges(G, V) -> Edges</name> + <name name="edges" arity="2"/> <fsummary>Return the edges emanating from or incident on a vertex of a digraph.</fsummary> - <type> - <v>G = digraph()</v> - <v>V = vertex()</v> - <v>Edges = [edge()]</v> - </type> <desc> <p>Returns a list of all edges <seealso marker="#emanate">emanating</seealso> from - or <seealso marker="#incident">incident</seealso> on <c>V</c> - of the digraph <c>G</c>, in some unspecified order.</p> + or <seealso marker="#incident">incident</seealso> on <c><anno>V</anno></c> + of the digraph <c><anno>G</anno></c>, in some unspecified order.</p> </desc> </func> <func> - <name>get_cycle(G, V) -> Vertices | false</name> + <name name="get_cycle" arity="2"/> <fsummary>Find one cycle in a digraph.</fsummary> - <type> - <v>G = digraph()</v> - <v>V1 = V2 = vertex()</v> - <v>Vertices = [vertex()]</v> - </type> <desc> <p>If there is a <seealso marker="#simple_cycle">simple cycle</seealso> of length two or more through the vertex - <c>V</c>, then the cycle is returned as a list - <c>[V, ..., V]</c> of vertices, otherwise if there + <c><anno>V</anno></c>, then the cycle is returned as a list + <c>[<anno>V</anno>, ..., <anno>V</anno>]</c> of vertices, otherwise if there is a <seealso marker="#loop">loop</seealso> through - <c>V</c>, then the loop is returned as a list <c>[V]</c>. If - there are no cycles through <c>V</c>, then <c>false</c> is + <c><anno>V</anno></c>, then the loop is returned as a list <c>[<anno>V</anno>]</c>. If + there are no cycles through <c><anno>V</anno></c>, then <c>false</c> is returned. </p> <p><c>get_path/3</c> is used for finding a simple cycle - through <c>V</c>. + through <c><anno>V</anno></c>. </p> </desc> </func> <func> - <name>get_path(G, V1, V2) -> Vertices | false</name> + <name name="get_path" arity="3"/> <fsummary>Find one path in a digraph.</fsummary> - <type> - <v>G = digraph()</v> - <v>V1 = V2 = vertex()</v> - <v>Vertices = [vertex()]</v> - </type> <desc> <p>Tries to find a <seealso marker="#simple_path">simple path</seealso> from - the vertex <c>V1</c> to the vertex - <c>V2</c> of the digraph <c>G</c>. Returns the path as a - list <c>[V1, ..., V2]</c> of vertices, or - <c>false</c> if no simple path from <c>V1</c> to <c>V2</c> + the vertex <c><anno>V1</anno></c> to the vertex + <c><anno>V2</anno></c> of the digraph <c><anno>G</anno></c>. Returns the path as a + list <c>[<anno>V1</anno>, ..., <anno>V2</anno>]</c> of vertices, or + <c>false</c> if no simple path from <c><anno>V1</anno></c> to <c><anno>V2</anno></c> of length one or more exists. </p> - <p>The digraph <c>G</c> is traversed in a depth-first manner, + <p>The digraph <c><anno>G</anno></c> is traversed in a depth-first manner, and the first path found is returned. </p> </desc> </func> <func> - <name>get_short_cycle(G, V) -> Vertices | false</name> + <name name="get_short_cycle" arity="2"/> <fsummary>Find one short cycle in a digraph.</fsummary> - <type> - <v>G = digraph()</v> - <v>V1 = V2 = vertex()</v> - <v>Vertices = [vertex()]</v> - </type> <desc> <p>Tries to find an as short as possible <seealso marker="#simple_cycle">simple cycle</seealso> through - the vertex <c>V</c> of the digraph <c>G</c>. Returns the cycle - as a list <c>[V, ..., V]</c> of vertices, or - <c>false</c> if no simple cycle through <c>V</c> exists. + the vertex <c><anno>V</anno></c> of the digraph <c>G</c>. Returns the cycle + as a list <c>[<anno>V</anno>, ..., <anno>V</anno>]</c> of vertices, or + <c>false</c> if no simple cycle through <c><anno>V</anno></c> exists. Note that a <seealso marker="#loop">loop</seealso> through - <c>V</c> is returned as the list <c>[V, V]</c>. + <c><anno>V</anno></c> is returned as the list <c>[<anno>V</anno>, <anno>V</anno>]</c>. </p> <p><c>get_short_path/3</c> is used for finding a simple cycle - through <c>V</c>. + through <c><anno>V</anno></c>. </p> </desc> </func> <func> - <name>get_short_path(G, V1, V2) -> Vertices | false</name> + <name name="get_short_path" arity="3"/> <fsummary>Find one short path in a digraph.</fsummary> - <type> - <v>G = digraph()</v> - <v>V1 = V2 = vertex()</v> - <v>Vertices = [vertex()]</v> - </type> <desc> <p>Tries to find an as short as possible <seealso marker="#simple_path">simple path</seealso> from - the vertex <c>V1</c> to the vertex <c>V2</c> of the digraph <c>G</c>. - Returns the path as a list <c>[V1, ..., V2]</c> of - vertices, or <c>false</c> if no simple path from <c>V1</c> - to <c>V2</c> of length one or more exists. + the vertex <c><anno>V1</anno></c> to the vertex <c><anno>V2</anno></c> of the digraph <c><anno>G</anno></c>. + Returns the path as a list <c>[<anno>V1</anno>, ..., <anno>V2</anno>]</c> of + vertices, or <c>false</c> if no simple path from <c><anno>V1</anno></c> + to <c><anno>V2</anno></c> of length one or more exists. </p> - <p>The digraph <c>G</c> is traversed in a breadth-first + <p>The digraph <c><anno>G</anno></c> is traversed in a breadth-first manner, and the first path found is returned. </p> </desc> </func> <func> - <name>in_degree(G, V) -> integer()</name> + <name name="in_degree" arity="2"/> <fsummary>Return the in-degree of a vertex of a digraph.</fsummary> - <type> - <v>G= digraph()</v> - <v>V = vertex()</v> - </type> <desc> <p>Returns the <seealso marker="#in_degree">in-degree</seealso> of the vertex - <c>V</c> of the digraph <c>G</c>. + <c><anno>V</anno></c> of the digraph <c><anno>G</anno></c>. </p> </desc> </func> <func> - <name>in_edges(G, V) -> Edges</name> + <name name="in_edges" arity="2"/> <fsummary>Return all edges incident on a vertex of a digraph.</fsummary> - <type> - <v>G = digraph()</v> - <v>V = vertex()</v> - <v>Edges = [edge()]</v> - </type> <desc> <p>Returns a list of all edges <seealso marker="#incident">incident</seealso> on - <c>V</c> of the digraph <c>G</c>, in some unspecified order. + <c><anno>V</anno></c> of the digraph <c><anno>G</anno></c>, in some unspecified order. </p> </desc> </func> <func> - <name>in_neighbours(G, V) -> Vertices</name> + <name name="in_neighbours" arity="2"/> <fsummary>Return all in-neighbours of a vertex of a digraph.</fsummary> - <type> - <v>G = digraph()</v> - <v>V = vertex()</v> - <v>Vertices = [vertex()]</v> - </type> <desc> <p>Returns a list of all <seealso marker="#in_neighbour">in-neighbours</seealso> of - <c>V</c> of the digraph <c>G</c>, in some unspecified order. + <c><anno>V</anno></c> of the digraph <c><anno>G</anno></c>, in some unspecified order. </p> </desc> </func> <func> - <name>info(G) -> InfoList</name> + <name name="info" arity="1"/> <fsummary>Return information about a digraph.</fsummary> - <type> - <v>G = digraph()</v> - <v>InfoList = [{cyclicity, Cyclicity}, {memory, NoWords}, {protection, Protection}]</v> - <v>Cyclicity = cyclic | acyclic</v> - <v>Protection = protected | private</v> - <v>NoWords = integer() >= 0</v> - </type> + <type name="d_cyclicity"/> + <type name="d_protection"/> <desc> <p>Returns a list of <c>{Tag, Value}</c> pairs describing the - digraph <c>G</c>. The following pairs are returned: + digraph <c><anno>G</anno></c>. The following pairs are returned: </p> <list type="bulleted"> <item> - <p><c>{cyclicity, Cyclicity}</c>, where <c>Cyclicity</c> + <p><c>{cyclicity, <anno>Cyclicity</anno>}</c>, where <c><anno>Cyclicity</anno></c> is <c>cyclic</c> or <c>acyclic</c>, according to the options given to <c>new</c>.</p> </item> <item> - <p><c>{memory, NoWords}</c>, where <c>NoWords</c> is - the number of words allocated to the <c>ets</c> tables.</p> + <p><c>{memory, <anno>NoWords</anno>}</c>, where <c><anno>NoWords</anno></c> is + the number of words allocated to the <c>ETS</c> tables.</p> </item> <item> - <p><c>{protection, Protection}</c>, where <c>Protection</c> + <p><c>{protection, <anno>Protection</anno>}</c>, where <c><anno>Protection</anno></c> is <c>protected</c> or <c>private</c>, according to the options given to <c>new</c>.</p> </item> @@ -462,7 +397,7 @@ </desc> </func> <func> - <name>new() -> digraph()</name> + <name name="new" arity="0"/> <fsummary>Return a protected empty digraph, where cycles are allowed.</fsummary> <desc> <p>Equivalent to <c>new([])</c>. @@ -470,15 +405,16 @@ </desc> </func> <func> - <name>new(Type) -> digraph()</name> + <name name="new" arity="1"/> <fsummary>Create a new empty digraph.</fsummary> - <type> - <v>Type = [cyclic | acyclic | private | protected]</v> - </type> + <type variable="Type"/> + <type name="d_type"/> + <type name="d_cyclicity"/> + <type name="d_protection"/> <desc> <p>Returns an <seealso marker="#empty_digraph">empty digraph</seealso> with - properties according to the options in <c>Type</c>:</p> + properties according to the options in <c><anno>Type</anno></c>:</p> <taglist> <tag><c>cyclic</c></tag> <item>Allow <seealso marker="#cycle">cycles</seealso> in the @@ -491,101 +427,72 @@ <item>The digraph can be read and modified by the creating process only.</item> </taglist> - <p>If an unrecognized type option <c>T</c> is given or <c>Type</c> + <p>If an unrecognized type option <c>T</c> is given or <c><anno>Type</anno></c> is not a proper list, there will be a <c>badarg</c> exception. </p> </desc> </func> <func> - <name>no_edges(G) -> integer() >= 0</name> + <name name="no_edges" arity="1"/> <fsummary>Return the number of edges of the a digraph.</fsummary> - <type> - <v>G = digraph()</v> - </type> <desc> - <p>Returns the number of edges of the digraph <c>G</c>. + <p>Returns the number of edges of the digraph <c><anno>G</anno></c>. </p> </desc> </func> <func> - <name>no_vertices(G) -> integer() >= 0</name> + <name name="no_vertices" arity="1"/> <fsummary>Return the number of vertices of a digraph.</fsummary> - <type> - <v>G = digraph()</v> - </type> <desc> - <p>Returns the number of vertices of the digraph <c>G</c>. + <p>Returns the number of vertices of the digraph <c><anno>G</anno></c>. </p> </desc> </func> <func> - <name>out_degree(G, V) -> integer()</name> + <name name="out_degree" arity="2"/> <fsummary>Return the out-degree of a vertex of a digraph.</fsummary> - <type> - <v>G = digraph()</v> - <v>V = vertex()</v> - </type> <desc> <p>Returns the <seealso marker="#out_degree">out-degree</seealso> of the vertex - <c>V</c> of the digraph <c>G</c>. + <c><anno>V</anno></c> of the digraph <c><anno>G</anno></c>. </p> </desc> </func> <func> - <name>out_edges(G, V) -> Edges</name> + <name name="out_edges" arity="2"/> <fsummary>Return all edges emanating from a vertex of a digraph.</fsummary> - <type> - <v>G = digraph()</v> - <v>V = vertex()</v> - <v>Edges = [edge()]</v> - </type> <desc> <p>Returns a list of all edges <seealso marker="#emanate">emanating</seealso> from - <c>V</c> of the digraph <c>G</c>, in some unspecified order. + <c><anno>V</anno></c> of the digraph <c><anno>G</anno></c>, in some unspecified order. </p> </desc> </func> <func> - <name>out_neighbours(G, V) -> Vertices</name> + <name name="out_neighbours" arity="2"/> <fsummary>Return all out-neighbours of a vertex of a digraph.</fsummary> - <type> - <v>G = digraph()</v> - <v>V = vertex()</v> - <v>Vertices = [vertex()]</v> - </type> <desc> <p>Returns a list of all <seealso marker="#out_neighbour">out-neighbours</seealso> of - <c>V</c> of the digraph <c>G</c>, in some unspecified order. + <c><anno>V</anno></c> of the digraph <c><anno>G</anno></c>, in some unspecified order. </p> </desc> </func> <func> - <name>vertex(G, V) -> {V, Label} | false</name> + <name name="vertex" arity="2"/> <fsummary>Return the label of a vertex of a digraph.</fsummary> - <type> - <v>G = digraph()</v> - <v>V = vertex()</v> - <v>Label = label()</v> - </type> <desc> - <p>Returns <c>{V, Label}</c> where <c>Label</c> is the + <p>Returns <c>{<anno>V</anno>, <anno>Label</anno>}</c> where <c><anno>Label</anno></c> is the <seealso marker="#label">label</seealso> of the vertex - <c>V</c> of the digraph <c>G</c>, or <c>false</c> if there - is no vertex <c>V</c> of the digraph <c>G</c>. + <c><anno>V</anno></c> of the digraph <c><anno>G</anno></c>, or <c>false</c> if there + is no vertex <c><anno>V</anno></c> of the digraph <c><anno>G</anno></c>. </p> </desc> </func> <func> - <name>vertices(G) -> Vertices</name> + <name name="vertices" arity="1"/> <fsummary>Return all vertices of a digraph.</fsummary> - <type> - <v>G = digraph()</v> - <v>Vertices = [vertex()]</v> - </type> <desc> - <p>Returns a list of all vertices of the digraph <c>G</c>, in + <p>Returns a list of all vertices of the digraph <c><anno>G</anno></c>, in some unspecified order. </p> </desc> diff --git a/lib/stdlib/doc/src/digraph_utils.xml b/lib/stdlib/doc/src/digraph_utils.xml index 4b137456b3..e44632bfd2 100644 --- a/lib/stdlib/doc/src/digraph_utils.xml +++ b/lib/stdlib/doc/src/digraph_utils.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2000</year><year>2009</year> + <year>2000</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -119,49 +119,43 @@ considering all edges undirected.</p> </description> + <datatypes> + <datatype> + <name><marker id="type-digraph">digraph()</marker></name> + <desc><p>A digraph as returned by <c>digraph:new/0,1</c>.</p></desc> + </datatype> + </datatypes> <funcs> <func> - <name>arborescence_root(Digraph) -> no | {yes, Root}</name> + <name name="arborescence_root" arity="1"/> <fsummary>Check if a digraph is an arborescence.</fsummary> - <type> - <v>Digraph = digraph()</v> - <v>Root = vertex()</v> - </type> <desc> - - <p>Returns <c>{yes, Root}</c> if <c>Root</c> is + <p>Returns <c>{yes, <anno>Root</anno>}</c> if <c><anno>Root</anno></c> is the <seealso marker="#root">root</seealso> of the arborescence - <c>Digraph</c>, <c>no</c> otherwise. + <c><anno>Digraph</anno></c>, <c>no</c> otherwise. </p> </desc> </func> <func> - <name>components(Digraph) -> [Component]</name> + <name name="components" arity="1"/> <fsummary>Return the components of a digraph.</fsummary> - <type> - <v>Digraph = digraph()</v> - <v>Component = [vertex()]</v> - </type> <desc> <p>Returns a list of <seealso marker="#components">connected components</seealso>. Each component is represented by its vertices. The order of the vertices and the order of the components are arbitrary. Each vertex of the digraph - <c>Digraph</c> occurs in exactly one component. + <c><anno>Digraph</anno></c> occurs in exactly one component. </p> </desc> </func> <func> - <name>condensation(Digraph) -> CondensedDigraph</name> + <name name="condensation" arity="1"/> <fsummary>Return a condensed graph of a digraph.</fsummary> - <type> - <v>Digraph = CondensedDigraph = digraph()</v> - </type> <desc> <p>Creates a digraph where the vertices are the <seealso marker="#strong_components">strongly connected - components</seealso> of <c>Digraph</c> as returned by + components</seealso> of <c><anno>Digraph</anno></c> as returned by <c>strong_components/1</c>. If X and Y are strongly connected components, and there exist vertices x and y in X and Y respectively such that there is an @@ -169,7 +163,7 @@ and <seealso marker="#incident">incident</seealso> on y, then an edge emanating from X and incident on Y is created. </p> - <p>The created digraph has the same type as <c>Digraph</c>. + <p>The created digraph has the same type as <c><anno>Digraph</anno></c>. All vertices and edges have the default <seealso marker="#label">label</seealso> <c>[]</c>. </p> @@ -181,12 +175,8 @@ </desc> </func> <func> - <name>cyclic_strong_components(Digraph) -> [StrongComponent]</name> + <name name="cyclic_strong_components" arity="1"/> <fsummary>Return the cyclic strong components of a digraph.</fsummary> - <type> - <v>Digraph = digraph()</v> - <v>StrongComponent = [vertex()]</v> - </type> <desc> <p>Returns a list of <seealso marker="#strong_components">strongly connected components</seealso>. @@ -194,67 +184,50 @@ by its vertices. The order of the vertices and the order of the components are arbitrary. Only vertices that are included in some <seealso marker="#cycle">cycle</seealso> in - <c>Digraph</c> are returned, otherwise the returned list is + <c><anno>Digraph</anno></c> are returned, otherwise the returned list is equal to that returned by <c>strong_components/1</c>. </p> </desc> </func> <func> - <name>is_acyclic(Digraph) -> bool()</name> + <name name="is_acyclic" arity="1"/> <fsummary>Check if a digraph is acyclic.</fsummary> - <type> - <v>Digraph = digraph()</v> - </type> <desc> <p>Returns <c>true</c> if and only if the digraph - <c>Digraph</c> is <seealso marker="#acyclic_digraph">acyclic</seealso>.</p> + <c><anno>Digraph</anno></c> is <seealso marker="#acyclic_digraph">acyclic</seealso>.</p> </desc> </func> <func> - <name>is_arborescence(Digraph) -> bool()</name> + <name name="is_arborescence" arity="1"/> <fsummary>Check if a digraph is an arborescence.</fsummary> - <type> - <v>Digraph = digraph()</v> - </type> <desc> <p>Returns <c>true</c> if and only if the digraph - <c>Digraph</c> is + <c><anno>Digraph</anno></c> is an <seealso marker="#arborescence">arborescence</seealso>.</p> </desc> </func> <func> - <name>is_tree(Digraph) -> bool()</name> + <name name="is_tree" arity="1"/> <fsummary>Check if a digraph is a tree.</fsummary> - <type> - <v>Digraph = digraph()</v> - </type> <desc> <p>Returns <c>true</c> if and only if the digraph - <c>Digraph</c> is + <c><anno>Digraph</anno></c> is a <seealso marker="#tree">tree</seealso>.</p> </desc> </func> <func> - <name>loop_vertices(Digraph) -> Vertices</name> + <name name="loop_vertices" arity="1"/> <fsummary>Return the vertices of a digraph included in some loop.</fsummary> - <type> - <v>Digraph = digraph()</v> - <v>Vertices = [vertex()]</v> - </type> <desc> - <p>Returns a list of all vertices of <c>Digraph</c> that are + <p>Returns a list of all vertices of <c><anno>Digraph</anno></c> that are included in some <seealso marker="#loop">loop</seealso>.</p> </desc> </func> <func> - <name>postorder(Digraph) -> Vertices</name> + <name name="postorder" arity="1"/> <fsummary>Return the vertices of a digraph in post-order.</fsummary> - <type> - <v>Digraph = digraph()</v> - <v>Vertices = [vertex()]</v> - </type> <desc> - <p>Returns all vertices of the digraph <c>Digraph</c>. The + <p>Returns all vertices of the digraph <c><anno>Digraph</anno></c>. The order is given by a <seealso marker="#depth_first_traversal">depth-first traversal</seealso> of the digraph, collecting visited @@ -266,14 +239,10 @@ </desc> </func> <func> - <name>preorder(Digraph) -> Vertices</name> + <name name="preorder" arity="1"/> <fsummary>Return the vertices of a digraph in pre-order.</fsummary> - <type> - <v>Digraph = digraph()</v> - <v>Vertices = [vertex()]</v> - </type> <desc> - <p>Returns all vertices of the digraph <c>Digraph</c>. The + <p>Returns all vertices of the digraph <c><anno>Digraph</anno></c>. The order is given by a <seealso marker="#depth_first_traversal">depth-first traversal</seealso> of the digraph, collecting visited @@ -281,119 +250,94 @@ </desc> </func> <func> - <name>reachable(Vertices, Digraph) -> Vertices</name> + <name name="reachable" arity="2"/> <fsummary>Return the vertices reachable from some vertices of a digraph.</fsummary> - <type> - <v>Digraph = digraph()</v> - <v>Vertices = [vertex()]</v> - </type> <desc> <p>Returns an unsorted list of digraph vertices such that for each vertex in the list, there is - a <seealso marker="#path">path</seealso> in <c>Digraph</c> from some - vertex of <c>Vertices</c> to the vertex. In particular, + a <seealso marker="#path">path</seealso> in <c><anno>Digraph</anno></c> from some + vertex of <c><anno>Vertices</anno></c> to the vertex. In particular, since paths may have length zero, the vertices of - <c>Vertices</c> are included in the returned list. + <c><anno>Vertices</anno></c> are included in the returned list. </p> </desc> </func> <func> - <name>reachable_neighbours(Vertices, Digraph) -> Vertices</name> + <name name="reachable_neighbours" arity="2"/> <fsummary>Return the neighbours reachable from some vertices of a digraph.</fsummary> - <type> - <v>Digraph = digraph()</v> - <v>Vertices = [vertex()]</v> - </type> <desc> <p>Returns an unsorted list of digraph vertices such that for each vertex in the list, there is - a <seealso marker="#path">path</seealso> in <c>Digraph</c> of length - one or more from some vertex of <c>Vertices</c> to the + a <seealso marker="#path">path</seealso> in <c><anno>Digraph</anno></c> of length + one or more from some vertex of <c><anno>Vertices</anno></c> to the vertex. As a consequence, only those vertices - of <c>Vertices</c> that are included in + of <c><anno>Vertices</anno></c> that are included in some <seealso marker="#cycle">cycle</seealso> are returned. </p> </desc> </func> <func> - <name>reaching(Vertices, Digraph) -> Vertices</name> + <name name="reaching" arity="2"/> <fsummary>Return the vertices that reach some vertices of a digraph.</fsummary> - <type> - <v>Digraph = digraph()</v> - <v>Vertices = [vertex()]</v> - </type> <desc> <p>Returns an unsorted list of digraph vertices such that for each vertex in the list, there is a <seealso marker="#path">path</seealso> from the vertex to some - vertex of <c>Vertices</c>. In particular, since paths may have - length zero, the vertices of <c>Vertices</c> are included in + vertex of <c><anno>Vertices</anno></c>. In particular, since paths may have + length zero, the vertices of <c><anno>Vertices</anno></c> are included in the returned list. </p> </desc> </func> <func> - <name>reaching_neighbours(Vertices, Digraph) -> Vertices</name> + <name name="reaching_neighbours" arity="2"/> <fsummary>Return the neighbours that reach some vertices of a digraph.</fsummary> - <type> - <v>Digraph = digraph()</v> - <v>Vertices = [vertex()]</v> - </type> <desc> <p>Returns an unsorted list of digraph vertices such that for each vertex in the list, there is a <seealso marker="#path">path</seealso> of length one or more - from the vertex to some vertex of <c>Vertices</c>. As a consequence, - only those vertices of <c>Vertices</c> that are included in + from the vertex to some vertex of <c><anno>Vertices</anno></c>. As a consequence, + only those vertices of <c><anno>Vertices</anno></c> that are included in some <seealso marker="#cycle">cycle</seealso> are returned. </p> </desc> </func> <func> - <name>strong_components(Digraph) -> [StrongComponent]</name> + <name name="strong_components" arity="1"/> <fsummary>Return the strong components of a digraph.</fsummary> - <type> - <v>Digraph = digraph()</v> - <v>StrongComponent = [vertex()]</v> - </type> <desc> <p>Returns a list of <seealso marker="#strong_components">strongly connected components</seealso>. Each strongly component is represented by its vertices. The order of the vertices and the order of the components are arbitrary. Each vertex of the digraph - <c>Digraph</c> occurs in exactly one strong component. + <c><anno>Digraph</anno></c> occurs in exactly one strong component. </p> </desc> </func> <func> - <name>subgraph(Digraph, Vertices [, Options]) -> Subgraph</name> + <name name="subgraph" arity="2"/> + <name name="subgraph" arity="3"/> <fsummary>Return a subgraph of a digraph.</fsummary> - <type> - <v>Digraph = Subgraph = digraph()</v> - <v>Options = [{type, SubgraphType}, {keep_labels, bool()}]</v> - <v>SubgraphType = inherit | type()</v> - <v>Vertices = [vertex()]</v> - </type> <desc> <p>Creates a maximal <seealso marker="#subgraph">subgraph</seealso> of <c>Digraph</c> having - as vertices those vertices of <c>Digraph</c> that are - mentioned in <c>Vertices</c>. + as vertices those vertices of <c><anno>Digraph</anno></c> that are + mentioned in <c><anno>Vertices</anno></c>. </p> <p>If the value of the option <c>type</c> is <c>inherit</c>, - which is the default, then the type of <c>Digraph</c> is used + which is the default, then the type of <c><anno>Digraph</anno></c> is used for the subgraph as well. Otherwise the option value of <c>type</c> is used as argument to <c>digraph:new/1</c>. </p> <p>If the value of the option <c>keep_labels</c> is <c>true</c>, which is the default, then the <seealso marker="#label">labels</seealso> of vertices and edges - of <c>Digraph</c> are used for the subgraph as well. If the value + of <c><anno>Digraph</anno></c> are used for the subgraph as well. If the value is <c>false</c>, then the default label, <c>[]</c>, is used for the subgraph's vertices and edges. </p> - <p><c>subgraph(Digraph, Vertices)</c> is equivalent to - <c>subgraph(Digraph, Vertices, [])</c>. + <p><c>subgraph(<anno>Digraph</anno>, <anno>Vertices</anno>)</c> is equivalent to + <c>subgraph(<anno>Digraph</anno>, <anno>Vertices</anno>, [])</c>. </p> <p>There will be a <c>badarg</c> exception if any of the arguments are invalid. @@ -401,16 +345,12 @@ </desc> </func> <func> - <name>topsort(Digraph) -> Vertices | false</name> + <name name="topsort" arity="1"/> <fsummary>Return a topological sorting of the vertices of a digraph.</fsummary> - <type> - <v>Digraph = digraph()</v> - <v>Vertices = [vertex()]</v> - </type> <desc> <p>Returns a <seealso marker="#topsort">topological ordering</seealso> of the vertices of the digraph - <c>Digraph</c> if such an ordering exists, <c>false</c> + <c><anno>Digraph</anno></c> if such an ordering exists, <c>false</c> otherwise. For each vertex in the returned list, there are no <seealso marker="#out_neighbour">out-neighbours</seealso> that occur earlier in the list.</p> diff --git a/lib/stdlib/doc/src/epp.xml b/lib/stdlib/doc/src/epp.xml index e6b48b270a..488499581f 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>2010</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -38,65 +38,61 @@ by <c>compile</c> to preprocess macros and include files before the actual parsing takes place.</p> </description> + <datatypes> + <datatype> + <name name="macros"></name> + </datatype> + <datatype> + <name name="epp_handle"></name> + <desc><p>Handle to the epp server.</p></desc> + </datatype> + </datatypes> <funcs> <func> - <name>open(FileName, IncludePath) -> {ok,Epp} | {error, ErrorDescriptor}</name> - <name>open(FileName, IncludePath, PredefMacros) -> {ok,Epp} | {error, ErrorDescriptor}</name> + <name name="open" arity="2"/> + <name name="open" arity="3"/> <fsummary>Open a file for preprocessing</fsummary> - <type> - <v>FileName = atom() | string()</v> - <v>IncludePath = [DirectoryName]</v> - <v>DirectoryName = atom() | string()</v> - <v>PredefMacros = [{atom(),term()}]</v> - <v>Epp = pid() -- handle to the epp server</v> - <v>ErrorDescriptor = term()</v> - </type> <desc> <p>Opens a file for preprocessing.</p> </desc> </func> <func> - <name>close(Epp) -> ok</name> + <name name="close" arity="1"/> <fsummary>Close the preprocessing of the file associated with <c>Epp</c></fsummary> - <type> - <v>Epp = pid() -- handle to the epp server</v> - </type> <desc> <p>Closes the preprocessing of a file.</p> </desc> </func> <func> - <name>parse_erl_form(Epp) -> {ok, AbsForm} | {eof, Line} | {error, ErrorInfo}</name> + <name name="parse_erl_form" arity="1"/> <fsummary>Return the next Erlang form from the opened Erlang source file</fsummary> - <type> - <v>Epp = pid()</v> - <v>AbsForm = term()</v> - <v>Line = integer()</v> - <v>ErrorInfo = see separate description below.</v> - </type> <desc> <p>Returns the next Erlang form from the opened Erlang source file. - The tuple <c>{eof, Line}</c> is returned at end-of-file. The first + The tuple <c>{eof, <anno>Line</anno>}</c> is returned at end-of-file. The first form corresponds to an implicit attribute <c>-file(File,1).</c>, where <c>File</c> is the name of the file.</p> </desc> </func> <func> - <name>parse_file(FileName,IncludePath,PredefMacro) -> {ok,[Form]} | {error,OpenError}</name> - <fsummary>Preprocesse and parse an Erlang source file</fsummary> - <type> - <v>FileName = atom() | string()</v> - <v>IncludePath = [DirectoryName]</v> - <v>DirectoryName = atom() | string()</v> - <v>PredefMacros = [{atom(),term()}]</v> - <v>Form = term() -- same as returned by erl_parse:parse_form</v> - </type> + <name name="parse_file" arity="3"/> + <fsummary>Preprocess and parse an Erlang source file</fsummary> <desc> <p>Preprocesses and parses an Erlang source file. - Note that the tuple <c>{eof, Line}</c> returned at end-of-file is + Note that the tuple <c>{eof, <anno>Line</anno>}</c> returned at end-of-file is included as a "form".</p> </desc> </func> + <func> + <name name="format_error" arity="1"/> + <fsummary>Format an error descriptor</fsummary> + <desc> + <p>Takes an <c><anno>ErrorDescriptor</anno></c> and returns + a string which + describes the error or warning. This function is usually + called implicitly when processing an <c>ErrorInfo</c> + structure (see below).</p> + </desc> + </func> </funcs> <section> diff --git a/lib/stdlib/doc/src/erl_eval.xml b/lib/stdlib/doc/src/erl_eval.xml index 62af23c5eb..d0622594d9 100644 --- a/lib/stdlib/doc/src/erl_eval.xml +++ b/lib/stdlib/doc/src/erl_eval.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2009</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -36,63 +36,108 @@ <description> <p>This module provides an interpreter for Erlang expressions. The expressions are in the abstract syntax as returned by - <c>erl_parse</c>, the Erlang parser, or a call to - <c>io:parse_erl_exprs/2</c>.</p> + <seealso marker="erl_parse"><c>erl_parse</c></seealso>, + the Erlang parser, or <seealso marker="io"> + <c>io</c></seealso>.</p> </description> + <datatypes> + <datatype> + <name name="bindings"/> + </datatype> + <datatype> + <name name="binding_struct"/> + <desc><p>A binding structure.</p></desc> + </datatype> + <datatype> + <name name="expression"/> + </datatype> + <datatype> + <name name="expressions"/> + <desc><p>As returned by <seealso marker="erl_parse#parse_exprs/1"> + <c>erl_parse:parse_exprs/1</c></seealso> or + <seealso marker="io#parse_erl_exprs/2"> + <c>io:parse_erl_exprs/2</c></seealso>.</p></desc> + </datatype> + <datatype> + <name name="expression_list"/> + </datatype> + <datatype> + <name name="func_spec"/> + </datatype> + <datatype> + <name name="lfun_eval_handler"/> + </datatype> + <datatype> + <name name="lfun_value_handler"/> + </datatype> + <datatype> + <name name="local_function_handler"/> + <desc><p>Further described + <seealso marker="#local_function_handler">below.</seealso></p> + </desc> + </datatype> + <datatype> + <name name="name"/> + </datatype> + <datatype> + <name name="nlfun_handler"/> + </datatype> + <datatype> + <name name="non_local_function_handler"/> + <desc><p>Further described + <seealso marker="#non_local_function_handler">below.</seealso></p> + </desc> + </datatype> + <datatype> + <name name="value"/> + </datatype> + </datatypes> <funcs> <func> - <name>exprs(Expressions, Bindings) -> {value, Value, NewBindings}</name> - <name>exprs(Expressions, Bindings, LocalFunctionHandler) -> {value, Value, NewBindings}</name> - <name>exprs(Expressions, Bindings, LocalFunctionHandler, NonlocalFunctionHandler) -> {value, Value, NewBindings}</name> + <name name="exprs" arity="2"/> + <name name="exprs" arity="3"/> + <name name="exprs" arity="4"/> <fsummary>Evaluate expressions</fsummary> - <type> - <v>Expressions = as returned by erl_parse or io:parse_erl_exprs/2</v> - <v>Bindings = as returned by bindings/1</v> - <v>LocalFunctionHandler = {value, Func} | {eval, Func} | none</v> - <v>NonlocalFunctionHandler = {value, Func} | none</v> - </type> <desc> - <p>Evaluates <c>Expressions</c> with the set of bindings - <c>Bindings</c>, where <c>Expressions</c> is a sequence of + <p>Evaluates <c><anno>Expressions</anno></c> with the set of bindings + <c><anno>Bindings</anno></c>, where <c><anno>Expressions</anno></c> + is a sequence of expressions (in abstract syntax) of a type which may be - returned by <c>io:parse_erl_exprs/2</c>. See below for an + returned by <seealso marker="io#parse_erl_exprs/2"> + <c>io:parse_erl_exprs/2</c></seealso>. See below for an explanation of how and when to use the arguments - <c>LocalFunctionHandler</c> and <c>NonlocalFunctionHandler</c>. + <c><anno>LocalFunctionHandler</anno></c> and + <c><anno>NonLocalFunctionHandler</anno></c>. </p> - <p>Returns <c>{value, Value, NewBindings}</c></p> + <p>Returns <c>{value, <anno>Value</anno>, <anno>NewBindings</anno>}</c> + </p> </desc> </func> <func> - <name>expr(Expression, Bindings) -> { value, Value, NewBindings }</name> - <name>expr(Expression, Bindings, LocalFunctionHandler) -> { value, Value, NewBindings }</name> - <name>expr(Expression, Bindings, LocalFunctionHandler, NonlocalFunctionHandler) -> { value, Value, NewBindings }</name> - <name>expr(Expression, Bindings, LocalFunctionHandler, NonlocalFunctionHandler, ReturnFormat) -> { value, Value, NewBindings } | Value</name> + <name name="expr" arity="2"/> + <name name="expr" arity="3"/> + <name name="expr" arity="4"/> + <name name="expr" arity="5"/> <fsummary>Evaluate expression</fsummary> - <type> - <v>Expression = as returned by io:parse_erl_form/2, for example</v> - <v>Bindings = as returned by bindings/1</v> - <v>LocalFunctionHandler = {value, Func} | {eval, Func} | none</v> - <v>NonlocalFunctionHandler = {value, Func} | none</v> - <v>ReturnFormat = value | none</v> - </type> <desc> - <p>Evaluates <c>Expression</c> with the set of bindings - <c>Bindings</c>. <c>Expression</c> is an expression (in - abstract syntax) of a type which may be returned by - <c>io:parse_erl_form/2</c>. See below for an explanation of + <p>Evaluates <c><anno>Expression</anno></c> with the set of bindings + <c><anno>Bindings</anno></c>. <c><anno>Expression</anno></c> + is an expression in + abstract syntax. See below for an explanation of how and when to use the arguments - <c>LocalFunctionHandler</c> and - <c>NonlocalFunctionHandler</c>. + <c><anno>LocalFunctionHandler</anno></c> and + <c><anno>NonLocalFunctionHandler</anno></c>. </p> - <p>Returns <c>{value, Value, NewBindings}</c> by default. But - if the <c>ReturnFormat</c> is <c>value</c> only the <c>Value</c> - is returned.</p> + <p>Returns <c>{value, <anno>Value</anno>, + <anno>NewBindings</anno>}</c> by default. But if the + <c><anno>ReturnFormat</anno></c> is <c>value</c> only + the <c><anno>Value</anno></c> is returned.</p> </desc> </func> <func> - <name>expr_list(ExpressionList, Bindings) -> {ValueList, NewBindings}</name> - <name>expr_list(ExpressionList, Bindings, LocalFunctionHandler) -> {ValueList, NewBindings}</name> - <name>expr_list(ExpressionList, Bindings, LocalFunctionHandler, NonlocalFunctionHandler) -> {ValueList, NewBindings}</name> + <name name="expr_list" arity="2"/> + <name name="expr_list" arity="3"/> + <name name="expr_list" arity="4"/> <fsummary>Evaluate a list of expressions</fsummary> <desc> <p>Evaluates a list of expressions in parallel, using the same @@ -100,18 +145,19 @@ merge the bindings returned from each evaluation. This function is useful in the <c>LocalFunctionHandler</c>. See below. </p> - <p>Returns <c>{ValueList, NewBindings}</c>.</p> + <p>Returns <c>{<anno>ValueList</anno>, <anno>NewBindings</anno>}</c>. + </p> </desc> </func> <func> - <name>new_bindings() -> BindingStruct</name> + <name name="new_bindings" arity="0"/> <fsummary>Return a bindings structure</fsummary> <desc> <p>Returns an empty binding structure.</p> </desc> </func> <func> - <name>bindings(BindingStruct) -> Bindings</name> + <name name="bindings" arity="1"/> <fsummary>Return bindings</fsummary> <desc> <p>Returns the list of bindings contained in the binding @@ -119,25 +165,28 @@ </desc> </func> <func> - <name>binding(Name, BindingStruct) -> Binding</name> + <name name="binding" arity="2"/> <fsummary>Return bindings</fsummary> <desc> - <p>Returns the binding of <c>Name</c> in <c>BindingStruct</c>.</p> + <p>Returns the binding of <c><anno>Name</anno></c> + in <c><anno>BindingStruct</anno></c>.</p> </desc> </func> <func> - <name>add_binding(Name, Value, Bindings) -> BindingStruct</name> + <name name="add_binding" arity="3"/> <fsummary>Add a binding</fsummary> <desc> - <p>Adds the binding <c>Name = Value</c> to <c>Bindings</c>. + <p>Adds the binding <c><anno>Name</anno> = <anno>Value</anno></c> + to <c><anno>BindingStruct</anno></c>. Returns an updated binding structure.</p> </desc> </func> <func> - <name>del_binding(Name, Bindings) -> BindingStruct</name> + <name name="del_binding" arity="2"/> <fsummary>Delete a binding</fsummary> <desc> - <p>Removes the binding of <c>Name</c> in <c>Bindings</c>. + <p>Removes the binding of <c><anno>Name</anno></c> + in <c><anno>BindingStruct</anno></c>. Returns an updated binding structure.</p> </desc> </func> @@ -145,7 +194,8 @@ <section> <title>Local Function Handler</title> - <p>During evaluation of a function, no calls can be made to local + <p><marker id="local_function_handler"></marker> + During evaluation of a function, no calls can be made to local functions. An undefined function error would be generated. However, the optional argument <c>LocalFunctionHandler</c> may be used to define a function @@ -191,7 +241,8 @@ Func(Name, Arguments, Bindings) </code> <section> <title>Non-local Function Handler</title> - <p>The optional argument <c>NonlocalFunctionHandler</c> may be + <p><marker id="non_local_function_handler"></marker> + The optional argument <c>NonlocalFunctionHandler</c> may be used to define a function which is called in the following cases: a functional object (fun) is called; a built-in function is called; a function is called using the M:F syntax, where M diff --git a/lib/stdlib/doc/src/erl_expand_records.xml b/lib/stdlib/doc/src/erl_expand_records.xml index 7fb03e7c50..8ead438b31 100644 --- a/lib/stdlib/doc/src/erl_expand_records.xml +++ b/lib/stdlib/doc/src/erl_expand_records.xml @@ -5,7 +5,7 @@ <header> <copyright> <year>2005</year> - <year>2007</year> + <year>2011</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> @@ -39,12 +39,8 @@ </description> <funcs> <func> - <name>module(AbsForms, CompileOptions) -> AbsForms</name> + <name name="module" arity="2"/> <fsummary>Expand all records in a module</fsummary> - <type> - <v>AbsForms = [term()]</v> - <v>CompileOptions = [term()]</v> - </type> <desc> <p>Expands all records in a module. The returned module has no references to records, neither attributes nor code.</p> diff --git a/lib/stdlib/doc/src/erl_id_trans.xml b/lib/stdlib/doc/src/erl_id_trans.xml index 7c821d2efc..18cc2460f9 100644 --- a/lib/stdlib/doc/src/erl_id_trans.xml +++ b/lib/stdlib/doc/src/erl_id_trans.xml @@ -5,7 +5,7 @@ <header> <copyright> <year>1996</year> - <year>2007</year> + <year>2011</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> @@ -48,8 +48,8 @@ <name>parse_transform(Forms, Options) -> Forms</name> <fsummary>Transform Erlang forms</fsummary> <type> - <v>Forms = [erlang_form()]</v> - <v>Options = [compiler_options()]</v> + <v>Forms = [<seealso marker="erl_parse#type-abstract_form">erl_parse:abstract_form()</seealso>]</v> + <v>Options = [<seealso marker="compile#type-option">compile:option()</seealso>]</v> </type> <desc> <p>Performs an identity transformation on Erlang forms, as an example.</p> @@ -70,7 +70,8 @@ <section> <title>See Also</title> - <p><seealso marker="erl_parse">erl_parse(3)</seealso>, compile(3).</p> + <p><seealso marker="erl_parse">erl_parse(3)</seealso>, + <seealso marker="compiler:compile">compile(3)</seealso>.</p> </section> </erlref> diff --git a/lib/stdlib/doc/src/erl_internal.xml b/lib/stdlib/doc/src/erl_internal.xml index 906b95deb7..b8d5ad73b3 100644 --- a/lib/stdlib/doc/src/erl_internal.xml +++ b/lib/stdlib/doc/src/erl_internal.xml @@ -5,7 +5,7 @@ <header> <copyright> <year>1996</year> - <year>2007</year> + <year>2011</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> @@ -42,112 +42,75 @@ </description> <funcs> <func> - <name>bif(Name, Arity) -> bool()</name> + <name name="bif" arity="2"/> <fsummary>Test for an Erlang BIF</fsummary> - <type> - <v>Name = atom()</v> - <v>Arity = integer()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>Name/Arity</c> is an Erlang BIF + <p>Returns <c>true</c> if <c><anno>Name</anno>/<anno>Arity</anno></c> is an Erlang BIF which is automatically recognized by the compiler, otherwise <c>false</c>.</p> </desc> </func> <func> - <name>guard_bif(Name, Arity) -> bool()</name> + <name name="guard_bif" arity="2"/> <fsummary>Test for an Erlang BIF allowed in guards</fsummary> - <type> - <v>Name = atom()</v> - <v>Arity = integer()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>Name/Arity</c> is an Erlang BIF + <p>Returns <c>true</c> if <c><anno>Name</anno>/<anno>Arity</anno></c> is an Erlang BIF which is allowed in guards, otherwise <c>false</c>.</p> </desc> </func> <func> - <name>type_test(Name, Arity) -> bool()</name> + <name name="type_test" arity="2"/> <fsummary>Test for a valid type test</fsummary> - <type> - <v>Name = atom()</v> - <v>Arity = integer()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>Name/Arity</c> is a valid Erlang + <p>Returns <c>true</c> if <c><anno>Name</anno>/<anno>Arity</anno></c> is a valid Erlang type test, otherwise <c>false</c>.</p> </desc> </func> <func> - <name>arith_op(OpName, Arity) -> bool()</name> + <name name="arith_op" arity="2"/> <fsummary>Test for an arithmetic operator</fsummary> - <type> - <v>OpName = atom()</v> - <v>Arity = integer()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>OpName/Arity</c> is an arithmetic + <p>Returns <c>true</c> if <c><anno>OpName</anno>/<anno>Arity</anno></c> is an arithmetic operator, otherwise <c>false</c>.</p> </desc> </func> <func> - <name>bool_op(OpName, Arity) -> bool()</name> + <name name="bool_op" arity="2"/> <fsummary>Test for a Boolean operator</fsummary> - <type> - <v>OpName = atom()</v> - <v>Arity = integer()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>OpName/Arity</c> is a Boolean + <p>Returns <c>true</c> if <c><anno>OpName</anno>/<anno>Arity</anno></c> is a Boolean operator, otherwise <c>false</c>.</p> </desc> </func> <func> - <name>comp_op(OpName, Arity) -> bool()</name> + <name name="comp_op" arity="2"/> <fsummary>Test for a comparison operator</fsummary> - <type> - <v>OpName = atom()</v> - <v>Arity = integer()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>OpName/Arity</c> is a comparison + <p>Returns <c>true</c> if <c><anno>OpName</anno>/<anno>Arity</anno></c> is a comparison operator, otherwise <c>false</c>.</p> </desc> </func> <func> - <name>list_op(OpName, Arity) -> bool()</name> + <name name="list_op" arity="2"/> <fsummary>Test for a list operator</fsummary> - <type> - <v>OpName = atom()</v> - <v>Arity = integer()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>OpName/Arity</c> is a list + <p>Returns <c>true</c> if <c><anno>OpName</anno>/<anno>Arity</anno></c> is a list operator, otherwise <c>false</c>.</p> </desc> </func> <func> - <name>send_op(OpName, Arity) -> bool()</name> + <name name="send_op" arity="2"/> <fsummary>Test for a send operator</fsummary> - <type> - <v>OpName = atom()</v> - <v>Arity = integer()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>OpName/Arity</c> is a send + <p>Returns <c>true</c> if <c><anno>OpName</anno>/<anno>Arity</anno></c> is a send operator, otherwise <c>false</c>.</p> </desc> </func> <func> - <name>op_type(OpName, Arity) -> Type</name> + <name name="op_type" arity="2"/> <fsummary>Return operator type</fsummary> - <type> - <v>OpName = atom()</v> - <v>Arity = integer()</v> - <v>Type = arith | bool | comp | list | send</v> - </type> <desc> - <p>Returns the <c>Type</c> of operator that <c>OpName/Arity</c> + <p>Returns the <c><anno>Type</anno></c> of operator that <c><anno>OpName</anno>/<anno>Arity</anno></c> belongs to, or generates a <c>function_clause</c> error if it is not an operator at all.</p> diff --git a/lib/stdlib/doc/src/erl_lint.xml b/lib/stdlib/doc/src/erl_lint.xml index 6a7d37765c..b7fbdd8799 100644 --- a/lib/stdlib/doc/src/erl_lint.xml +++ b/lib/stdlib/doc/src/erl_lint.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2010</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -60,29 +60,30 @@ functions separately unless you have written your own Erlang compiler.</p> </description> + <datatypes> + <datatype> + <name name="error_info"/> + </datatype> + <datatype> + <name name="error_description"/> + </datatype> + </datatypes> <funcs> <func> - <name>module(AbsForms) -> {ok,Warnings} | {error,Errors,Warnings}</name> - <name>module(AbsForms, FileName) -> {ok,Warnings} | {error,Errors,Warnings}</name> - <name>module(AbsForms, FileName, CompileOptions) -> {ok,Warnings} | {error,Errors,Warnings}</name> + <name name="module" arity="1"/> + <name name="module" arity="2"/> + <name name="module" arity="3"/> <fsummary>Check a module for errors</fsummary> - <type> - <v>AbsForms = [term()]</v> - <v>FileName = FileName2 = atom() | string()</v> - <v>Warnings = Errors = [{Filename2,[ErrorInfo]}]</v> - <v>ErrorInfo = see separate description below.</v> - <v>CompileOptions = [term()]</v> - </type> <desc> <p>This function checks all the forms in a module for errors. It returns: </p> <taglist> - <tag><c>{ok,Warnings}</c></tag> + <tag><c>{ok,<anno>Warnings</anno>}</c></tag> <item> <p>There were no errors in the module.</p> </item> - <tag><c>{error,Errors,Warnings}</c></tag> + <tag><c>{error,<anno>Errors</anno>,<anno>Warnings</anno>}</c></tag> <item> <p>There were errors in the module.</p> </item> @@ -93,14 +94,14 @@ elements of <c>Options</c> that control the warnings are only described in <seealso marker="compiler:compile#erl_lint_options">compile(3)</seealso>. </p> - <p>The <c>AbsForms</c> of a module which comes from a file + <p>The <c><anno>AbsForms</anno></c> of a module which comes from a file that is read through <c>epp</c>, the Erlang pre-processor, can come from many files. This means that any references to - errors must include the file name (see <seealso marker="epp">epp(3)</seealso>, or parser <seealso marker="erl_parse">erl_parse(3)</seealso> The warnings and - errors returned have the following format: + errors must include the file name (see <seealso marker="epp">epp(3)</seealso>, or parser <seealso marker="erl_parse">erl_parse(3)</seealso>). + The warnings and errors returned have the following format: </p> <code type="none"> - [{FileName2,[ErrorInfo]}] </code> + [{<anno>FileName2</anno>,[<anno>ErrorInfo</anno>]}] </code> <p>The errors and warnings are listed in the order in which they are encountered in the forms. This means that the errors from one file may be split into different entries in @@ -108,27 +109,20 @@ </desc> </func> <func> - <name>is_guard_test(Expr) -> bool()</name> + <name name="is_guard_test" arity="1"/> <fsummary>Test for a guard test</fsummary> - <type> - <v>Expr = term()</v> - </type> <desc> - <p>This function tests if <c>Expr</c> is a legal guard test. - <c>Expr</c> is an Erlang term representing the abstract form + <p>This function tests if <c><anno>Expr</anno></c> is a legal guard test. + <c><anno>Expr</anno></c> is an Erlang term representing the abstract form for the expression. <c>erl_parse:parse_exprs(Tokens)</c> can - be used to generate a list of <c>Expr</c>.</p> + be used to generate a list of <c><anno>Expr</anno></c>.</p> </desc> </func> <func> - <name>format_error(ErrorDescriptor) -> Chars</name> + <name name="format_error" arity="1"/> <fsummary>Format an error descriptor</fsummary> - <type> - <v>ErrorDescriptor = errordesc()</v> - <v>Chars = [char() | Chars]</v> - </type> <desc> - <p>Takes an <c>ErrorDescriptor</c> and returns a string which + <p>Takes an <c><anno>ErrorDescriptor</anno></c> and returns a string which describes the error or warning. This function is usually called implicitly when processing an <c>ErrorInfo</c> structure (see below).</p> diff --git a/lib/stdlib/doc/src/erl_parse.xml b/lib/stdlib/doc/src/erl_parse.xml index ae8a8afd5c..bafc2e0746 100644 --- a/lib/stdlib/doc/src/erl_parse.xml +++ b/lib/stdlib/doc/src/erl_parse.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2010</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -36,31 +36,51 @@ <description> <p>This module is the basic Erlang parser which converts tokens into the abstract form of either forms (i.e., top-level constructs), - expressions, or terms. The Abstract Format is described in the ERTS - User's Guide. + expressions, or terms. The Abstract Format is described in the + <seealso marker="erts:absform">ERTS User's Guide</seealso>. Note that a token list must end with the <em>dot</em> token in order - to be acceptable to the parse functions (see erl_scan).</p> + to be acceptable to the parse functions (see <seealso marker="erl_scan">erl_scan(3)</seealso>).</p> </description> + <datatypes> + <datatype> + <name name="abstract_clause"></name> + <desc><p>Parse tree for Erlang clause.</p> + </desc> + </datatype> + <datatype> + <name name="abstract_expr"></name> + <desc><p>Parse tree for Erlang expression.</p> + </desc> + </datatype> + <datatype> + <name name="abstract_form"></name> + <desc><p>Parse tree for Erlang form.</p> + </desc> + </datatype> + <datatype> + <name name="error_description"></name> + </datatype> + <datatype> + <name name="error_info"></name> + </datatype> + <datatype> + <name name="token"></name> + </datatype> + </datatypes> <funcs> <func> - <name>parse_form(Tokens) -> {ok, AbsForm} | {error, ErrorInfo}</name> + <name name="parse_form" arity="1"/> <fsummary>Parse an Erlang form</fsummary> - <type> - <v>Tokens = [Token]</v> - <v>Token = {Tag,Line} | {Tag,Line,term()}</v> - <v>Tag = atom()</v> - <v>AbsForm = term()</v> - <v>ErrorInfo = see section Error Information below.</v> - </type> <desc> - <p>This function parses <c>Tokens</c> as if it were a form. It returns:</p> + <p>This function parses <c><anno>Tokens</anno></c> as if it were + a form. It returns:</p> <taglist> - <tag><c>{ok, AbsForm}</c></tag> + <tag><c>{ok, <anno>AbsForm</anno>}</c></tag> <item> - <p>The parsing was successful. <c>AbsForm</c> is the + <p>The parsing was successful. <c><anno>AbsForm</anno></c> is the abstract form of the parsed form.</p> </item> - <tag><c>{error, ErrorInfo}</c></tag> + <tag><c>{error, <anno>ErrorInfo</anno>}</c></tag> <item> <p>An error occurred.</p> </item> @@ -68,25 +88,18 @@ </desc> </func> <func> - <name>parse_exprs(Tokens) -> {ok, Expr_list} | {error, ErrorInfo}</name> + <name name="parse_exprs" arity="1"/> <fsummary>Parse Erlang expressions</fsummary> - <type> - <v>Tokens = [Token]</v> - <v>Token = {Tag,Line} | {Tag,Line,term()}</v> - <v>Tag = atom()</v> - <v>Expr_list = [AbsExpr]</v> - <v>AbsExpr = term()</v> - <v>ErrorInfo = see section Error Information below.</v> - </type> <desc> - <p>This function parses <c>Tokens</c> as if it were a list of expressions. It returns:</p> + <p>This function parses <c><anno>Tokens</anno></c> as if it were + a list of expressions. It returns:</p> <taglist> - <tag><c>{ok, Expr_list}</c></tag> + <tag><c>{ok, <anno>ExprList</anno>}</c></tag> <item> - <p>The parsing was successful. <c>Expr_list</c> is a + <p>The parsing was successful. <c><anno>ExprList</anno></c> is a list of the abstract forms of the parsed expressions.</p> </item> - <tag><c>{error, ErrorInfo}</c></tag> + <tag><c>{error, <anno>ErrorInfo</anno>}</c></tag> <item> <p>An error occurred.</p> </item> @@ -94,21 +107,15 @@ </desc> </func> <func> - <name>parse_term(Tokens) -> {ok, Term} | {error, ErrorInfo}</name> + <name name="parse_term" arity="1"/> <fsummary>Parse an Erlang term</fsummary> - <type> - <v>Tokens = [Token]</v> - <v>Token = {Tag,Line} | {Tag,Line,term()}</v> - <v>Tag = atom()</v> - <v>Term = term()</v> - <v>ErrorInfo = see section Error Information below.</v> - </type> <desc> - <p>This function parses <c>Tokens</c> as if it were a term. It returns:</p> + <p>This function parses <c><anno>Tokens</anno></c> as if it were + a term. It returns:</p> <taglist> - <tag><c>{ok, Term}</c></tag> + <tag><c>{ok, <anno>Term</anno>}</c></tag> <item> - <p>The parsing was successful. <c>Term</c> is + <p>The parsing was successful. <c><anno>Term</anno></c> is the Erlang term corresponding to the token list.</p> </item> <tag><c>{error, ErrorInfo}</c></tag> @@ -122,7 +129,8 @@ <name>format_error(ErrorDescriptor) -> Chars</name> <fsummary>Format an error descriptor</fsummary> <type> - <v>ErrorDescriptor = errordesc()</v> + <v>ErrorDescriptor = <seealso + marker="#type-error_info">error_description()</seealso></v> <v>Chars = [char() | Chars]</v> </type> <desc> @@ -133,44 +141,32 @@ </desc> </func> <func> - <name>tokens(AbsTerm) -> Tokens</name> - <name>tokens(AbsTerm, MoreTokens) -> Tokens</name> + <name name="tokens" arity="1"/> + <name name="tokens" arity="2"/> <fsummary>Generate a list of tokens for an expression</fsummary> - <type> - <v>Tokens = MoreTokens = [Token]</v> - <v>Token = {Tag,Line} | {Tag,Line,term()}</v> - <v>Tag = atom()</v> - <v>AbsTerm = term()</v> - <v>ErrorInfo = see section Error Information below.</v> - </type> <desc> <p>This function generates a list of tokens representing the abstract - form <c>AbsTerm</c> of an expression. Optionally, it appends - <c>Moretokens</c>.</p> + form <c><anno>AbsTerm</anno></c> of an expression. Optionally, it + appends <c><anno>MoreTokens</anno></c>.</p> </desc> </func> <func> - <name>normalise(AbsTerm) -> Data</name> + <name name="normalise" arity="1"/> <fsummary>Convert abstract form to an Erlang term</fsummary> - <type> - <v>AbsTerm = Data = term()</v> - </type> <desc> - <p>Converts the abstract form <c>AbsTerm</c> of a term into a + <p>Converts the abstract form <c><anno>AbsTerm</anno></c> of a + term into a conventional Erlang data structure (i.e., the term itself). This is the inverse of <c>abstract/1</c>.</p> </desc> </func> <func> - <name>abstract(Data) -> AbsTerm</name> + <name name="abstract" arity="1"/> <fsummary>Convert an Erlang term into an abstract form</fsummary> - <type> - <v>Data = AbsTerm = term()</v> - </type> <desc> - <p>Converts the Erlang data structure <c>Data</c> into an - abstract form of type <c>AbsTerm</c>. This is the inverse of - <c>normalise/1</c>.</p> + <p>Converts the Erlang data structure <c><anno>Data</anno></c> into an + abstract form of type <c><anno>AbsTerm</anno></c>. + This is the inverse of <c>normalise/1</c>.</p> </desc> </func> </funcs> diff --git a/lib/stdlib/doc/src/erl_pp.xml b/lib/stdlib/doc/src/erl_pp.xml index 6b15c5afd3..57b5828bcd 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>2007</year> + <year>2011</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> @@ -43,93 +43,82 @@ <p>All functions can have an optional argument which specifies a hook that is called if an attempt is made to print an unknown form.</p> </description> + <datatypes> + <datatype> + <name name="hook_function"/> + <desc> + <p>The optional argument <marker id="hook_function"> + <c>HookFunction</c></marker>, shown in the functions described below, + defines a function which is called when an unknown form occurs where there + should be a valid expression.</p> + + <p>If <c>HookFunction</c> is equal to <c>none</c> there is no hook + function.</p> + + <p>The called hook function should return a (possibly deep) list + of characters. <seealso marker="#expr/4"><c>expr/4</c></seealso> + is useful in a hook. + </p> + <p>If <c><anno>CurrentIndentation</anno></c> is negative, there will be no line + breaks and only a space is used as a separator.</p> + </desc> + </datatype> + </datatypes> <funcs> <func> - <name>form(Form) -> DeepCharList</name> - <name>form(Form, HookFunction) -> DeepCharList</name> + <name name="form" arity="1"/> + <name name="form" arity="2"/> <fsummary>Pretty print a form</fsummary> - <type> - <v>Form = term()</v> - <v>HookFunction = see separate description below.</v> - <v>DeepCharList = [char()|DeepCharList]</v> - </type> <desc> <p>Pretty prints a - <c>Form</c> which is an abstract form of a type which is - returned by <c>erl_parse:parse_form</c>.</p> + <c><anno>Form</anno></c> which is an abstract form of a type which is + returned by <seealso marker="erl_parse#parse_form/1"> + <c>erl_parse:parse_form/1</c></seealso>.</p> </desc> </func> <func> - <name>attribute(Attribute) -> DeepCharList</name> - <name>attribute(Attribute, HookFunction) -> DeepCharList</name> + <name name="attribute" arity="1"/> + <name name="attribute" arity="2"/> <fsummary>Pretty print an attribute</fsummary> - <type> - <v>Attribute = term()</v> - <v>HookFunction = see separate description below.</v> - <v>DeepCharList = [char()|DeepCharList]</v> - </type> <desc> <p>The same as <c>form</c>, but only for the attribute - <c>Attribute</c>.</p> + <c><anno>Attribute</anno></c>.</p> </desc> </func> <func> - <name>function(Function) -> DeepCharList</name> - <name>function(Function, HookFunction) -> DeepCharList</name> + <name name="function" arity="1"/> + <name name="function" arity="2"/> <fsummary>Pretty print a function</fsummary> - <type> - <v>Function = term()</v> - <v>HookFunction = see separate description below.</v> - <v>DeepCharList = [char()|DeepCharList]</v> - </type> <desc> <p>The same as <c>form</c>, but only for the function - <c>Function</c>.</p> + <c><anno>Function</anno></c>.</p> </desc> </func> <func> - <name>guard(Guard) -> DeepCharList</name> - <name>guard(Guard, HookFunction) -> DeepCharList</name> + <name name="guard" arity="1"/> + <name name="guard" arity="2"/> <fsummary>Pretty print a guard</fsummary> - <type> - <v>Form = term()</v> - <v>HookFunction = see separate description below.</v> - <v>DeepCharList = [char()|DeepCharList]</v> - </type> <desc> <p>The same as <c>form</c>, but only for the guard test - <c>Guard</c>.</p> + <c><anno>Guard</anno></c>.</p> </desc> </func> <func> - <name>exprs(Expressions) -> DeepCharList</name> - <name>exprs(Expressions, HookFunction) -> DeepCharList</name> - <name>exprs(Expressions, Indent, HookFunction) -> DeepCharList</name> + <name name="exprs" arity="1"/> + <name name="exprs" arity="2"/> + <name name="exprs" arity="3"/> <fsummary>Pretty print <c>Expressions</c></fsummary> - <type> - <v>Expressions = term()</v> - <v>HookFunction = see separate description below.</v> - <v>Indent = integer()</v> - <v>DeepCharList = [char()|DeepCharList]</v> - </type> <desc> <p>The same as <c>form</c>, but only for the sequence of - expressions in <c>Expressions</c>.</p> + expressions in <c><anno>Expressions</anno></c>.</p> </desc> </func> <func> - <name>expr(Expression) -> DeepCharList</name> - <name>expr(Expression, HookFunction) -> DeepCharList</name> - <name>expr(Expression, Indent, HookFunction) -> DeepCharList</name> - <name>expr(Expression, Indent, Precedence, HookFunction) ->-> DeepCharList</name> + <name name="expr" arity="1"/> + <name name="expr" arity="2"/> + <name name="expr" arity="3"/> + <name name="expr" arity="4"/> <fsummary>Pretty print one <c>Expression</c></fsummary> - <type> - <v>Expression = term()</v> - <v>HookFunction = see separate description below.</v> - <v>Indent = integer()</v> - <v>Precedence = </v> - <v>DeepCharList = [char()|DeepCharList]</v> - </type> <desc> <p>This function prints one expression. It is useful for implementing hooks (see below).</p> </desc> @@ -137,32 +126,6 @@ </funcs> <section> - <title>Unknown Expression Hooks</title> - <p>The optional argument <c>HookFunction</c>, shown in the functions described above, defines a function which is called when an unknown form occurs where there - should be a valid expression. It can have the following formats:</p> - <taglist> - <tag><c>Function</c></tag> - <item> - <p>The hook function is called by:</p> - <code type="none"> -Function(Expr, - CurrentIndentation, - CurrentPrecedence, - HookFunction) </code> - </item> - <tag><c>none</c></tag> - <item> - <p>There is no hook function</p> - </item> - </taglist> - <p>The called hook function should return a (possibly deep) list - of characters. <c>expr/4</c> is useful in a hook. - </p> - <p>If <c>CurrentIndentation</c> is negative, there will be no line - breaks and only a space is used as a separator.</p> - </section> - - <section> <title>Bugs</title> <p>It should be possible to have hook functions for unknown forms at places other than expressions.</p> diff --git a/lib/stdlib/doc/src/erl_scan.xml b/lib/stdlib/doc/src/erl_scan.xml index 4175146c3c..54240dea19 100644 --- a/lib/stdlib/doc/src/erl_scan.xml +++ b/lib/stdlib/doc/src/erl_scan.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2009</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -13,12 +13,12 @@ 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. - + </legalnotice> <title>erl_scan</title> @@ -37,56 +37,96 @@ <p>This module contains functions for tokenizing characters into Erlang tokens.</p> </description> - <section> - <title>DATA TYPES</title> - <code type="none"> -category() = atom() -column() = integer() > 0 -line() = integer() -location() = line() | {line(), column()} -reserved_word_fun() -> fun(atom()) -> bool() -set_attribute_fun() -> fun(term()) -> term() -symbol() = atom() | float() | integer() | string() -token() = {category(), attributes()} | {category(), attributes(), symbol()} -attributes() = line() | list() | tuple()</code> - </section> + <datatypes> + <datatype> + <name name="attribute_info"></name> + </datatype> + <datatype> + <name name="attributes"></name> + </datatype> + <datatype> + <name name="attributes_data"></name> + </datatype> + <datatype> + <name name="category"></name> + </datatype> + <datatype> + <name name="column"></name> + </datatype> + <datatype> + <name name="error_description"></name> + </datatype> + <datatype> + <name name="error_info"></name> + </datatype> + <datatype> + <name name="info_line"></name> + </datatype> + <datatype> + <name name="info_location"></name> + </datatype> + <datatype> + <name name="line"></name> + </datatype> + <datatype> + <name name="location"></name> + </datatype> + <datatype> + <name name="option"></name> + </datatype> + <datatype> + <name name="options"></name> + </datatype> + <datatype> + <name name="symbol"></name> + </datatype> + <datatype> + <name name="resword_fun"></name> + </datatype> + <datatype> + <name name="token"></name> + </datatype> + <datatype> + <name name="token_info"></name> + </datatype> + <datatype> + <name name="tokens"></name> + </datatype> + <datatype> + <name name="tokens_result"></name> + </datatype> + </datatypes> <funcs> <func> - <name>string(String) -> Return</name> - <name>string(String, StartLocation) -> Return</name> - <name>string(String, StartLocation, Options) -> Return</name> + <name name="string" arity="1"/> + <name name="string" arity="2"/> + <name name="string" arity="3"/> <fsummary>Scan a string and return the Erlang tokens</fsummary> - <type> - <v>String = string()</v> - <v>Return = {ok, Tokens, EndLocation} | Error</v> - <v>Tokens = [token()]</v> - <v>Error = {error, ErrorInfo, EndLocation}</v> - <v>StartLocation = EndLocation = location()</v> - <v>Options = Option | [Option]</v> - <v>Option = {reserved_word_fun,reserved_word_fun()} - | return_comments | return_white_spaces | return - | text</v> - </type> <desc> - <p>Takes the list of characters <c>String</c> and tries to - scan (tokenize) them. Returns <c>{ok, Tokens, EndLocation}</c>, - where <c>Tokens</c> are the Erlang tokens from - <c>String</c>. <c>EndLocation</c> is the first location - after the last token.</p> - <p><c>{error, ErrorInfo, EndLocation}</c> is returned if an - error occurs. <c>EndLocation</c> is the first location after + <p>Takes the list of characters <c><anno>String</anno></c> and tries to + scan (tokenize) them. Returns <c>{ok, <anno>Tokens</anno>, + <anno>EndLocation</anno>}</c>, + where <c><anno>Tokens</anno></c> are the Erlang tokens from + <c><anno>String</anno></c>. <c><anno>EndLocation</anno></c> + is the first location after the last token.</p> + <p><c>{error, <anno>ErrorInfo</anno>, <anno>ErrorLocation</anno>}</c> + is returned if an error occurs. + <c><anno>ErrorLocation</anno></c> is the first location after the erroneous token.</p> - <p><c>string(String)</c> is equivalent to - <c>string(String, 1)</c>, and <c>string(String, - StartLocation)</c> is equivalent to <c>string(String, - StartLocation, [])</c>.</p> - <p><c>StartLocation</c> indicates the initial location when - scanning starts. If <c>StartLocation</c> is a line - <c>attributes()</c> as well as <c>EndLocation</c> and - <c>ErrorLocation</c> will be lines. If - <c>StartLocation</c> is a pair of a line and a column + <p><c>string(<anno>String</anno>)</c> is equivalent to + <c>string(<anno>String</anno>, 1)</c>, and + <c>string(<anno>String</anno>, + <anno>StartLocation</anno>)</c> is equivalent to + <c>string(<anno>String</anno>, + <anno>StartLocation</anno>, [])</c>.</p> + <p><c><anno>StartLocation</anno></c> indicates the initial location + when scanning starts. If <c><anno>StartLocation</anno></c> is a line + <c>attributes()</c> as well as <c><anno>EndLocation</anno></c> and + <c><anno>ErrorLocation</anno></c> will be lines. If + <c><anno>StartLocation</anno></c> is a pair of a line and a column <c>attributes()</c> takes the form of an opaque compound - data type, and <c>EndLocation</c> and <c>ErrorLocation</c> + data type, and <c><anno>EndLocation</anno></c> and + <c><anno>ErrorLocation</anno></c> will be pairs of a line and a column. The <em>token attributes</em> contain information about the column and the line where the token begins, as well as the text of the @@ -103,7 +143,7 @@ attributes() = line() | list() | tuple()</code> Info, atom()}</c>, <c>{char, Info, integer()}</c>, <c>{comment, Info, string()}</c>, <c>{float, Info, float()}</c>, <c>{integer, - Info, integer()}</c>, <c>{var, Info, atom()}</c>, + Info, integer()}</c>, <c>{var, Info, atom()}</c>, and <c>{white_space, Info, string()}</c>.</p> <p>The valid options are:</p> <taglist> @@ -134,253 +174,218 @@ attributes() = line() | list() | tuple()</code> </desc> </func> <func> - <name>tokens(Continuation, CharSpec, StartLocation) -> Return</name> - <name>tokens(Continuation, CharSpec, StartLocation, Options) -> Return</name> + <name name="tokens" arity="3"/> + <name name="tokens" arity="4"/> + <type name="char_spec"/> + <type name="return_cont"/> + <type_desc name="return_cont">An opaque continuation</type_desc> <fsummary>Re-entrant scanner</fsummary> - <type> - <v>Continuation = [] | Continuation1</v> - <v>Return = {done, Result, LeftOverChars} | {more, Continuation1}</v> - <v>LeftOverChars = CharSpec</v> - <v>CharSpec = string() | eof</v> - <v>Continuation1 = tuple()</v> - <v>Result = {ok, Tokens, EndLocation} | {eof, EndLocation} | Error</v> - <v>Tokens = [token()]</v> - <v>Error = {error, ErrorInfo, EndLocation}</v> - <v>StartLocation = EndLocation = location()</v> - <v>Options = Option | [Option]</v> - <v>Option = {reserved_word_fun,reserved_word_fun()} - | return_comments | return_white_spaces | return</v> - </type> <desc> <p>This is the re-entrant scanner which scans characters until a <em>dot</em> ('.' followed by a white space) or <c>eof</c> has been reached. It returns:</p> <taglist> - <tag><c>{done, Result, LeftOverChars}</c></tag> + <tag><c>{done, <anno>Result</anno>, <anno>LeftOverChars</anno>}</c> + </tag> <item> <p>This return indicates that there is sufficient input - data to get a result. <c>Result</c> is:</p> + data to get a result. <c><anno>Result</anno></c> is:</p> <taglist> - <tag><c>{ok, Tokens, EndLocation}</c></tag> + <tag><c>{ok, Tokens, EndLocation}</c> + </tag> <item> - <p>The scanning was successful. <c>Tokens</c> is the - list of tokens including <em>dot</em>.</p> + <p>The scanning was successful. <c>Tokens</c> + is the list of tokens including <em>dot</em>.</p> </item> <tag><c>{eof, EndLocation}</c></tag> <item> <p>End of file was encountered before any more tokens.</p> </item> - <tag><c>{error, ErrorInfo, EndLocation}</c></tag> + <tag><c>{error, ErrorInfo, EndLocation}</c> + </tag> <item> - <p>An error occurred. <c>LeftOverChars</c> is the remaining - characters of the input data, + <p>An error occurred. <c><anno>LeftOverChars</anno></c> + is the remaining characters of the input data, starting from <c>EndLocation</c>.</p> </item> </taglist> </item> - <tag><c>{more, Continuation1}</c></tag> + <tag><c>{more, <anno>Continuation1</anno>}</c></tag> <item> <p>More data is required for building a term. - <c>Continuation1</c> must be passed in a new call to + <c><anno>Continuation1</anno></c> must be passed in a new call to <c>tokens/3,4</c> when more data is available.</p> </item> </taglist> - <p>The <c>CharSpec</c> <c>eof</c> signals end of file. - <c>LeftOverChars</c> will then take the value <c>eof</c> as - well.</p> - <p><c>tokens(Continuation, CharSpec, StartLocation)</c> is - equivalent to <c>tokens(Continuation, CharSpec, - StartLocation, [])</c>.</p> + <p>The <c><anno>CharSpec</anno></c> <c>eof</c> signals end of file. + <c><anno>LeftOverChars</anno></c> will then take the value <c>eof</c> + as well.</p> + <p><c>tokens(<anno>Continuation</anno>, <anno>CharSpec</anno>, + <anno>StartLocation</anno>)</c> is equivalent to + <c>tokens(<anno>Continuation</anno>, <anno>CharSpec</anno>, + <anno>StartLocation</anno>, [])</c>.</p> <p>See <seealso marker="#string/3">string/3</seealso> for a description of the various options.</p> </desc> </func> <func> - <name>reserved_word(Atom) -> bool()</name> + <name name="reserved_word" arity="1"/> <fsummary>Test for a reserved word</fsummary> - <type> - <v>Atom = atom()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>Atom</c> is an Erlang reserved - word, otherwise <c>false</c>.</p> + <p>Returns <c>true</c> if <c><anno>Atom</anno></c> is an Erlang + reserved word, otherwise <c>false</c>.</p> </desc> </func> <func> - <name>token_info(Token) -> TokenInfo</name> + <name name="token_info" arity="1"/> <fsummary>Return information about a token</fsummary> - <type> - <v>Token = token()</v> - <v>TokenInfo = [TokenInfoTuple]</v> - <v>TokenInfoTuple = {TokenItem, Info}</v> - <v>TokenItem = atom()</v> - <v>Info = term()</v> - </type> <desc> <p>Returns a list containing information about the token - <c>Token</c>. The order of the <c>TokenInfoTuple</c>s is not - defined. The following <c>TokenItem</c>s are returned: - <c>category</c>, <c>column</c>, <c>length</c>, - <c>line</c>, <c>symbol</c>, and <c>text</c>. See <seealso + <c><anno>Token</anno></c>. The order of the + <c><anno>TokenInfoTuple</anno></c>s is not + defined. See <seealso marker="#token_info/2">token_info/2</seealso> for information about specific - <c>TokenInfoTuple</c>s.</p> + <c><anno>TokenInfoTuple</anno></c>s.</p> <p>Note that if <c>token_info(Token, TokenItem)</c> returns - <c>undefined</c> for some <c>TokenItem</c> in the list above, the - item is not included in <c>TokenInfo</c>.</p> + <c>undefined</c> for some <c>TokenItem</c>, the + item is not included in <c><anno>TokenInfo</anno></c>.</p> </desc> </func> <func> - <name>token_info(Token, TokenItemSpec) -> TokenInfo</name> + <name name="token_info" arity="2" clause_i="1"/> + <name name="token_info" arity="2" clause_i="2"/> + <type name="token_item"/> + <type name="attribute_item"/> <fsummary>Return information about a token</fsummary> - <type> - <v>Token = token()</v> - <v>TokenItemSpec = TokenItem | [TokenItem]</v> - <v>TokenInfo = TokenInfoTuple | undefined | [TokenInfoTuple]</v> - <v>TokenInfoTuple = {TokenItem, Info}</v> - <v>TokenItem = atom()</v> - <v>Info = term()</v> - </type> <desc> <p>Returns a list containing information about the token - <c>Token</c>. If <c>TokenItemSpec</c> is a single - <c>TokenItem</c>, the returned value is the corresponding + <c><anno>Token</anno></c>. If one single + <c><anno>TokenItem</anno></c> is given the returned value is + the corresponding <c>TokenInfoTuple</c>, or <c>undefined</c> if the - <c>TokenItem</c> has no value. If <c>TokenItemSpec</c> is a - list of - <c>TokenItem</c>, the result is a list of - <c>TokenInfoTuple</c>. The <c>TokenInfoTuple</c>s will - appear with the corresponding - <c>TokenItem</c>s in the same order as the <c>TokenItem</c>s - appeared in the list of <c>TokenItem</c>s. <c>TokenItem</c>s - with no value are not included in the list of - <c>TokenInfoTuple</c>.</p> - <p>The following <c>TokenInfoTuple</c>s with corresponding - <c>TokenItem</c>s are valid:</p> + <c>TokenItem</c> has no value. If a list of + <c><anno>TokenItem</anno></c>s is given the result is a list of + <c><anno>TokenInfoTuple</anno></c>. The + <c><anno>TokenInfoTuple</anno></c>s will + appear with the corresponding <c><anno>TokenItem</anno></c>s in + the same order as the <c><anno>TokenItem</anno></c>s + appear in the list of <c>TokenItem</c>s. + <c><anno>TokenItem</anno></c>s with no value are not included + in the list of <c><anno>TokenInfoTuple</anno></c>.</p> + <p>The following <c><anno>TokenInfoTuple</anno></c>s with corresponding + <c><anno>TokenItem</anno></c>s are valid:</p> <taglist> - <tag><c>{category, category()}</c></tag> + <tag><c>{category, <seealso marker="#type-category"> + category()</seealso>}</c></tag> <item><p>The category of the token.</p> </item> - <tag><c>{column, column()}</c></tag> + <tag><c>{column, <seealso marker="#type-column"> + column()</seealso>}</c></tag> <item><p>The column where the token begins.</p> </item> <tag><c>{length, integer() > 0}</c></tag> <item><p>The length of the token's text.</p> </item> - <tag><c>{line, line()}</c></tag> + <tag><c>{line, <seealso marker="#type-line"> + line()</seealso>}</c></tag> <item><p>The line where the token begins.</p> </item> - <tag><c>{location, location()}</c></tag> + <tag><c>{location, <seealso marker="#type-location"> + location()</seealso>}</c></tag> <item><p>The line and column where the token begins, or just the line if the column unknown.</p> </item> - <tag><c>{symbol, symbol()}</c></tag> + <tag><c>{symbol, <seealso marker="#type-symbol"> + symbol()</seealso>}</c></tag> <item><p>The token's symbol.</p> </item> <tag><c>{text, string()}</c></tag> - <item><p>The token's text..</p> + <item><p>The token's text.</p> </item> </taglist> </desc> </func> <func> - <name>attributes_info(Attributes) -> AttributesInfo</name> + <name name="attributes_info" arity="1"/> <fsummary>Return information about token attributes</fsummary> - <type> - <v>Attributes = attributes()</v> - <v>AttributesInfo = [AttributeInfoTuple]</v> - <v>AttributeInfoTuple = {AttributeItem, Info}</v> - <v>AttributeItem = atom()</v> - <v>Info = term()</v> - </type> <desc> <p>Returns a list containing information about the token - attributes <c>Attributes</c>. The order of the - <c>AttributeInfoTuple</c>s is not defined. The following - <c>AttributeItem</c>s are returned: - <c>column</c>, <c>length</c>, <c>line</c>, and <c>text</c>. + attributes <c><anno>Attributes</anno></c>. The order of the + <c><anno>AttributeInfoTuple</anno></c>s is not defined. See <seealso marker="#attributes_info/2">attributes_info/2</seealso> for information about specific - <c>AttributeInfoTuple</c>s.</p> + <c><anno>AttributeInfoTuple</anno></c>s.</p> <p>Note that if <c>attributes_info(Token, AttributeItem)</c> returns <c>undefined</c> for some <c>AttributeItem</c> in the list above, the item is not included in - <c>AttributesInfo</c>.</p> + <c><anno>AttributesInfo</anno></c>.</p> </desc> </func> <func> - <name>attributes_info(Attributes, AttributeItemSpec) -> AttributesInfo</name> + <name name="attributes_info" arity="2" clause_i="1"/> + <name name="attributes_info" arity="2" clause_i="2"/> <fsummary>Return information about a token attributes</fsummary> - <type> - <v>Attributes = attributes()</v> - <v>AttributeItemSpec = AttributeItem | [AttributeItem]</v> - <v>AttributesInfo = AttributeInfoTuple | undefined - | [AttributeInfoTuple]</v> - <v>AttributeInfoTuple = {AttributeItem, Info}</v> - <v>AttributeItem = atom()</v> - <v>Info = term()</v> - </type> + <type name="attribute_item"/> <desc> <p>Returns a list containing information about the token - attributes <c>Attributes</c>. If <c>AttributeItemSpec</c> is - a single <c>AttributeItem</c>, the returned value is the - corresponding <c>AttributeInfoTuple</c>, or <c>undefined</c> - if the <c>AttributeItem</c> has no value. If - <c>AttributeItemSpec</c> is a list of - <c>AttributeItem</c>, the result is a list of - <c>AttributeInfoTuple</c>. The <c>AttributeInfoTuple</c>s - will appear with the corresponding <c>AttributeItem</c>s in - the same order as the <c>AttributeItem</c>s appeared in the - list of <c>AttributeItem</c>s. <c>AttributeItem</c>s with no + attributes <c><anno>Attributes</anno></c>. If one single + <c><anno>AttributeItem</anno></c> is given the returned value is the + corresponding <c><anno>AttributeInfoTuple</anno></c>, + or <c>undefined</c> if the <c><anno>AttributeItem</anno></c> + has no value. If a list of <c><anno>AttributeItem</anno></c> + is given the result is a list of + <c><anno>AttributeInfoTuple</anno></c>. + The <c><anno>AttributeInfoTuple</anno></c>s + will appear with the corresponding <c><anno>AttributeItem</anno></c>s + in the same order as the <c><anno>AttributeItem</anno></c>s + appear in the list of <c><anno>AttributeItem</anno></c>s. + <c><anno>AttributeItem</anno></c>s with no value are not included in the list of - <c>AttributeInfoTuple</c>.</p> - <p>The following <c>AttributeInfoTuple</c>s with corresponding - <c>AttributeItem</c>s are valid:</p> + <c><anno>AttributeInfoTuple</anno></c>.</p> + <p>The following <c><anno>AttributeInfoTuple</anno></c>s with + corresponding <c><anno>AttributeItem</anno></c>s are valid:</p> <taglist> - <tag><c>{column, column()}</c></tag> + <tag><c>{column, <seealso marker="#type-column"> + column()</seealso>}</c></tag> <item><p>The column where the token begins.</p> </item> <tag><c>{length, integer() > 0}</c></tag> <item><p>The length of the token's text.</p> </item> - <tag><c>{line, line()}</c></tag> + <tag><c>{line, <seealso marker="#type-line"> + line()</seealso>}</c></tag> <item><p>The line where the token begins.</p> </item> - <tag><c>{location, location()}</c></tag> + <tag><c>{location, <seealso marker="#type-location"> + location()</seealso>}</c></tag> <item><p>The line and column where the token begins, or just the line if the column unknown.</p> </item> <tag><c>{text, string()}</c></tag> - <item><p>The token's text..</p> + <item><p>The token's text.</p> </item> </taglist> </desc> </func> <func> - <name>set_attribute(AttributeItem, Attributes, SetAttributeFun) -> AttributesInfo</name> + <name name="set_attribute" arity="3"/> <fsummary>Set a token attribute value</fsummary> - <type> - <v>AttributeItem = line</v> - <v>Attributes = attributes()</v> - <v>SetAttributeFun = set_attribute_fun()</v> - </type> <desc> <p>Sets the value of the <c>line</c> attribute of the token - attributes <c>Attributes</c>.</p> - <p>The <c>SetAttributeFun</c> is called with the value of + attributes <c><anno>Attributes</anno></c>.</p> + <p>The <c><anno>SetAttributeFun</anno></c> is called with the value of the <c>line</c> attribute, and is to return the new value of the <c>line</c> attribute.</p> </desc> </func> <func> - <name>format_error(ErrorDescriptor) -> string()</name> + <name name="format_error" arity="1"/> <fsummary>Format an error descriptor</fsummary> - <type> - <v>ErrorDescriptor = errordesc()</v> - </type> <desc> - <p>Takes an <c>ErrorDescriptor</c> and returns a string which + <p>Takes an <c><anno>ErrorDescriptor</anno></c> and returns + a string which describes the error or warning. This function is usually called implicitly when processing an <c>ErrorInfo</c> structure (see below).</p> diff --git a/lib/stdlib/doc/src/ets.xml b/lib/stdlib/doc/src/ets.xml index 7b9f0e7772..8c952708c5 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>2009</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -13,12 +13,12 @@ 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. - + </legalnotice> <title>ets</title> @@ -56,8 +56,8 @@ Even if there are no references to a table from any process, it will not automatically be destroyed unless the owner process terminates. It can be destroyed explicitly by using - <c>delete/1</c>.</p> - <p>Since R13B01, table ownership can be transferred at process termination + <c>delete/1</c>. The default owner is the process that created the + table. Table ownership can be transferred at process termination by using the <seealso marker="#heir">heir</seealso> option or explicitly by calling <seealso marker="#give_away/3">give_away/3</seealso>.</p> <p>Some implementation details:</p> @@ -82,11 +82,15 @@ <c>float()</c> that extends to the same value, hence the key <c>1</c> and the key <c>1.0</c> are regarded as equal in an <c>ordered_set</c> table.</p> - <p>In general, the functions below will exit with reason - <c>badarg</c> if any argument is of the wrong format, or if the - table identifier is invalid.</p> </description> - + <section> + <title>Failure</title> + <p>In general, the functions below will exit with reason + <c>badarg</c> if any argument is of the wrong format, if the + table identifier is invalid or if the operation is denied due to + table access rights (<seealso marker="#protected">protected</seealso> + or <seealso marker="#private">private</seealso>).</p> + </section> <section><marker id="concurrency"></marker> <title>Concurrency</title> <p>This module provides some limited support for concurrent access. @@ -122,15 +126,30 @@ <em>ERTS User's Guide</em>.</p> </section> - <section> - <title>DATA TYPES</title> - <code type="none"> -match_spec() - a match specification, see above - -tid() - a table identifier, as returned by new/2</code> - </section> + <datatypes> + <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> + </desc> + </datatype> + <datatype> + <name name="match_spec"/> + <desc><p>A match specification, see above.</p></desc> + </datatype> + <datatype> + <name name="match_pattern"/> + </datatype> + <datatype> + <name name="tab"/> + </datatype> + <datatype> + <name name="tid"/> + <desc><p>A table identifier, as returned by new/2.</p></desc> + </datatype> + </datatypes> <funcs> <func> <name>all() -> [Tab]</name> @@ -193,37 +212,25 @@ tid() </desc> </func> <func> - <name>file2tab(Filename) -> {ok,Tab} | {error,Reason}</name> + <name name="file2tab" arity="1"/> <fsummary>Read an ETS table from a file.</fsummary> - <type> - <v>Filename = string() | atom()</v> - <v>Tab = tid() | atom()</v> - <v>Reason = term()</v> - </type> <desc> <p>Reads a file produced by <seealso marker="#tab2file/2">tab2file/2</seealso> or <seealso marker="#tab2file/3">tab2file/3</seealso> and creates the - corresponding table <c>Tab</c>.</p> - <p>Equivalent to <c>file2tab(Filename,[])</c>.</p> + corresponding table <c><anno>Tab</anno></c>.</p> + <p>Equivalent to <c>file2tab(<anno>Filename</anno>, [])</c>.</p> </desc> </func> <func> - <name>file2tab(Filename,Options) -> {ok,Tab} | {error,Reason}</name> + <name name="file2tab" arity="2"/> <fsummary>Read an ETS table from a file.</fsummary> - <type> - <v>Filename = string() | atom()</v> - <v>Tab = tid() | atom()</v> - <v>Options = [Option]</v> - <v>Option = {verify, bool()}</v> - <v>Reason = term()</v> - </type> <desc> <p>Reads a file produced by <seealso marker="#tab2file/2">tab2file/2</seealso> or <seealso marker="#tab2file/3">tab2file/3</seealso> and creates the - corresponding table <c>Tab</c>.</p> - <p>The currently only supported option is <c>{verify,bool()}</c>. If + corresponding table <c><anno>Tab</anno></c>.</p> + <p>The currently only supported option is <c>{verify,boolean()}</c>. If verification is turned on (by means of specifying <c>{verify,true}</c>), the function utilizes whatever information is present in the file to assert that the @@ -267,70 +274,52 @@ tid() </desc> </func> <func> - <name>foldl(Function, Acc0, Tab) -> Acc1</name> + <name name="foldl" arity="3"/> <fsummary>Fold a function over an ETS table</fsummary> - <type> - <v>Function = fun(A, AccIn) -> AccOut</v> - <v>Tab = tid() | atom()</v> - <v>Acc0 = Acc1 = AccIn = AccOut = term()</v> - </type> <desc> - <p><c>Acc0</c> is returned if the table is empty. + <p><c><anno>Acc0</anno></c> is returned if the table is empty. This function is similar to <c>lists:foldl/3</c>. The order in which the elements of the table are traversed is unspecified, except for tables of type <c>ordered_set</c>, for which they are traversed first to last.</p> - <p>If <c>Function</c> inserts objects into the table, or another + <p>If <c><anno>Function</anno></c> inserts objects into the table, or another process inserts objects into the table, those objects <em>may</em> (depending on key ordering) be included in the traversal.</p> </desc> </func> <func> - <name>foldr(Function, Acc0, Tab) -> Acc1</name> + <name name="foldr" arity="3"/> <fsummary>Fold a function over an ETS table</fsummary> - <type> - <v>Function = fun(A, AccIn) -> AccOut</v> - <v>Tab = tid() | atom()</v> - <v>Acc0 = Acc1 = AccIn = AccOut = term()</v> - </type> <desc> - <p><c>Acc0</c> is returned if the table is empty. + <p><c><anno>Acc0</anno></c> is returned if the table is empty. This function is similar to <c>lists:foldr/3</c>. The order in which the elements of the table are traversed is unspecified, except for tables of type <c>ordered_set</c>, for which they are traversed last to first.</p> - <p>If <c>Function</c> inserts objects into the table, or another + <p>If <c><anno>Function</anno></c> inserts objects into the table, or another process inserts objects into the table, those objects <em>may</em> (depending on key ordering) be included in the traversal.</p> </desc> </func> <func> - <name>from_dets(Tab, DetsTab) -> true</name> + <name name="from_dets" arity="2"/> <fsummary>Fill an ETS table with objects from a Dets table.</fsummary> - <type> - <v>Tab = tid() | atom()</v> - <v>DetsTab = atom()</v> - </type> <desc> <p>Fills an already created ETS table with the objects in the - already opened Dets table named <c>DetsTab</c>. The existing + already opened Dets table named <c><anno>DetsTab</anno></c>. The existing objects of the ETS table are kept unless overwritten.</p> <p>Throws a badarg error if any of the tables does not exist or the dets table is not open.</p> </desc> </func> <func> - <name>fun2ms(LiteralFun) -> MatchSpec</name> + <name name="fun2ms" arity="1"/> <fsummary>Pseudo function that transforms fun syntax to a match_spec.</fsummary> - <type> - <v>LiteralFun -- see below</v> - <v>MatchSpec = match_spec()</v> - </type> <desc> <p>Pseudo function that by means of a <c>parse_transform</c> - translates <c>LiteralFun</c> typed as parameter in the + translates <c><anno>LiteralFun</anno></c> typed as parameter in the function call to a <seealso marker="#match_spec">match_spec</seealso>. With "literal" is meant that the fun needs to textually be written @@ -338,7 +327,7 @@ tid() variable which in turn is passed to the function).</p> <p>The parse transform is implemented in the module <c>ms_transform</c> and the source <em>must</em> include the - file <c>ms_transform.hrl</c> in <c>stdlib</c> for this + file <c>ms_transform.hrl</c> in STDLIB for this pseudo function to work. Failing to include the hrl file in the source will result in a runtime error, not a compile time ditto. The include file is easiest included by adding @@ -418,20 +407,17 @@ Error: fun containing local Erlang function calls </desc> </func> <func> - <name>i() -> ok</name> + <name name="i" arity="0"/> <fsummary>Display information about all ETS tables on tty.</fsummary> <desc> <p>Displays information about all ETS tables on tty.</p> </desc> </func> <func> - <name>i(Tab) -> ok</name> + <name name="i" arity="1"/> <fsummary>Browse an ETS table on tty.</fsummary> - <type> - <v>Tab = tid() | atom()</v> - </type> <desc> - <p>Browses the table <c>Tab</c> on tty.</p> + <p>Browses the table <c><anno>Tab</anno></c> on tty.</p> </desc> </func> <func> @@ -450,7 +436,7 @@ Error: fun containing local Erlang function calls correct type, this function fails with reason <c>badarg</c>.</p> <list type="bulleted"> - <item><c>Item=memory, Value=int()</c> <br></br> + <item><c>Item=memory, Value=integer()</c> <br></br> The number of words allocated to the table.</item> <item><c>Item=owner, Value=pid()</c> <br></br> @@ -462,7 +448,7 @@ Error: fun containing local Erlang function calls <item><c>Item=name, Value=atom()</c> <br></br> The name of the table.</item> - <item><c>Item=size, Value=int()</c> <br></br> + <item><c>Item=size, Value=integer()</c> <br></br> The number of objects inserted in the table.</item> <item><c>Item=node, Value=atom()</c> <br></br> @@ -475,12 +461,15 @@ Error: fun containing local Erlang function calls <item><c>Item=type, Value=set|ordered_set|bag|duplicate_bag</c> <br></br> The table type.</item> - <item><c>Item=keypos, Value=int()</c> <br></br> + <item><c>Item=keypos, Value=integer()</c> <br></br> The key position.</item> <item><c>Item=protection, Value=public|protected|private</c> <br></br> The table access rights.</item> + <item><c>Item=compressed, Value=true|false</c> <br></br> + + Indicates if the table is compressed or not.</item> </list> </desc> </func> @@ -529,25 +518,19 @@ Error: fun containing local Erlang function calls </desc> </func> <func> - <name>init_table(Name, InitFun) -> true</name> + <name name="init_table" arity="2"/> <fsummary>Replace all objects of an ETS table.</fsummary> - <type> - <v>Name = atom()</v> - <v>InitFun = fun(Arg) -> Res</v> - <v>Arg = read | close</v> - <v>Res = end_of_input | {[object()], InitFun} | term()</v> - </type> <desc> - <p>Replaces the existing objects of the table <c>Tab</c> with - objects created by calling the input function <c>InitFun</c>, + <p>Replaces the existing objects of the table <c><anno>Tab</anno></c> with + objects created by calling the input function <c><anno>InitFun</anno></c>, see below. This function is provided for compatibility with the <c>dets</c> module, it is not more efficient than filling a table by using <c>ets:insert/2</c>. </p> <p>When called with the argument <c>read</c> the function - <c>InitFun</c> is assumed to return <c>end_of_input</c> when - there is no more input, or <c>{Objects, Fun}</c>, where - <c>Objects</c> is a list of objects and <c>Fun</c> is a new + <c><anno>InitFun</anno></c> is assumed to return <c>end_of_input</c> when + there is no more input, or <c>{<anno>Objects</anno>, Fun}</c>, where + <c><anno>Objects</anno></c> is a list of objects and <c>Fun</c> is a new input function. Any other value Value is returned as an error <c>{error, {init_fun, Value}}</c>. Each input function will be called exactly once, and should an error occur, the last @@ -587,7 +570,7 @@ Error: fun containing local Erlang function calls </desc> </func> <func> - <name>insert_new(Tab, ObjectOrObjects) -> bool()</name> + <name>insert_new(Tab, ObjectOrObjects) -> boolean()</name> <fsummary>Insert an object into an ETS table if the key is not already present.</fsummary> <type> <v>Tab = tid() | atom()</v> @@ -608,7 +591,7 @@ Error: fun containing local Erlang function calls </desc> </func> <func> - <name>is_compiled_ms(Term) -> bool()</name> + <name>is_compiled_ms(Term) -> boolean()</name> <fsummary>Checks if an Erlang term is the result of ets:match_spec_compile</fsummary> <type> <v>Term = term()</v> @@ -701,7 +684,7 @@ ets:is_compiled_ms(Broken).</code> <type> <v>Tab = tid() | atom()</v> <v>Key = term()</v> - <v>Pos = int()</v> + <v>Pos = integer()</v> <v>Elem = term() | [term()]</v> </type> <desc> @@ -795,15 +778,11 @@ ets:is_compiled_ms(Broken).</code> </desc> </func> <func> - <name>match_delete(Tab, Pattern) -> true</name> + <name name="match_delete" arity="2"/> <fsummary>Delete all objects which match a given pattern from an ETS table.</fsummary> - <type> - <v>Tab = tid() | atom()</v> - <v>Pattern = tuple()</v> - </type> <desc> - <p>Deletes all objects which match the pattern <c>Pattern</c> - from the table <c>Tab</c>. See <c>match/2</c> for a + <p>Deletes all objects which match the pattern <c><anno>Pattern</anno></c> + from the table <c><anno>Tab</anno></c>. See <c>match/2</c> for a description of patterns.</p> </desc> </func> @@ -947,10 +926,11 @@ ets:select(Table,MatchSpec),</code> <type> <v>Name = atom()</v> <v>Options = [Option]</v> - <v> Option = Type | Access | named_table | {keypos,Pos} | {heir,pid(),HeirData} | {heir,none} | {write_concurrency,bool()}</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> Pos = int()</v> + <v> Tweaks = {write_concurrency,boolean()} | {read_concurrency,boolean()} | compressed</v> + <v> Pos = integer()</v> <v> HeirData = term()</v> </type> <desc> @@ -963,7 +943,7 @@ ets:select(Table,MatchSpec),</code> table is named or not. If one or more options are left out, the default values are used. This means that not specifying any options (<c>[]</c>) is the same as specifying - <c>[set,protected,{keypos,1},{heir,none},{write_concurrency,false}]</c>.</p> + <c>[set,protected,{keypos,1},{heir,none},{write_concurrency,false},{read_concurrency,false}]</c>.</p> <list type="bulleted"> <item> <p><c>set</c> @@ -1002,12 +982,14 @@ ets:select(Table,MatchSpec),</code> Any process may read or write to the table.</p> </item> <item> + <marker id="protected"></marker> <p><c>protected</c> The owner process can read and write to the table. Other processes can only read the table. This is the default setting for the access rights.</p> </item> <item> + <marker id="private"></marker> <p><c>private</c> Only the owner process can read or write to the table.</p> </item> @@ -1039,15 +1021,22 @@ ets:select(Table,MatchSpec),</code> the owner terminates.</p> </item> <item> - <p><c>{write_concurrency,bool()}</c> - Performance tuning. Default is <c>false</c>, which means that the table - is optimized towards concurrent read access. An operation that + <marker id="new_2_write_concurrency"></marker> + <p><c>{write_concurrency,boolean()}</c> + Performance tuning. Default is <c>false</c>, in which case an operation that mutates (writes to) the table will obtain exclusive access, blocking any concurrent access of the same table until finished. 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 single access and concurrent reader performance. + at the expense of sequential access and concurrent reader performance. + 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 + read bursts and large concurrent write bursts are common (see the + documentation of the + <seealso marker="#new_2_read_concurrency">read_concurrency</seealso> + option for more information). Note that this option does not change any guarantees about <seealso marker="#concurrency">atomicy and isolation</seealso>. Functions that makes such promises over several objects (like @@ -1055,6 +1044,38 @@ ets:select(Table,MatchSpec),</code> <p>Table type <c>ordered_set</c> is not affected by this option in current implementation.</p> </item> + <item> + <marker id="new_2_read_concurrency"></marker> + <p><c>{read_concurrency,boolean()}</c> + Performance tuning. Default is <c>false</c>. When set to + <c>true</c>, the table is optimized for concurrent read + operations. When this option is enabled on a runtime system with + SMP support, read operations become much cheaper; especially on + systems with multiple physical processors. However, switching + between read and write operations becomes more expensive. You + typically want to enable this option when concurrent read + operations are much more frequent than write operations, or when + concurrent reads and writes comes in large read and write + bursts (i.e., lots of reads not interrupted by writes, and lots + of writes not interrupted by reads). You typically do + <em>not</em> want to enable this option when the common access + pattern is a few read operations interleaved with a few write + operations repeatedly. In this case you will get a performance + degradation by enabling this option. The <c>read_concurrency</c> + option can be combined with the + <seealso marker="#new_2_write_concurrency">write_concurrency</seealso> + option. You typically want to combine these when large concurrent + read bursts and large concurrent write bursts are common.</p> + </item> + <item> + <marker id="new_2_compressed"></marker> + <p><c>compressed</c> + If this option is present, the table data will be stored in a more compact format to + consume less memory. The downside is that it will make table operations slower. + Especially operations that need to inspect entire objects, + such as <c>match</c> and <c>select</c>, will get much slower. The key element + is not compressed in current implementation.</p> + </item> </list> </desc> </func> @@ -1111,12 +1132,8 @@ ets:select(Table,MatchSpec),</code> </desc> </func> <func> - <name>repair_continuation(Continuation, MatchSpec) -> Continuation</name> + <name name="repair_continuation" arity="2"/> <fsummary>Repair a continuation from ets:select/1 or ets:select/3 that has passed through external representation</fsummary> - <type> - <v>Continuation = term()</v> - <v>MatchSpec = match_spec()</v> - </type> <desc> <p>This function can be used to restore an opaque continuation returned by <c>ets:select/3</c> or <c>ets:select/1</c> if the @@ -1355,6 +1372,28 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code> </desc> </func> <func> + <name>select_count(Tab, MatchSpec) -> NumMatched</name> + <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 + <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 + the match_spec the object is not considered a match and is + therefore not counted.</p> + <p>The function could be described as a <c>match_delete/2</c> + that does not actually delete any elements, but only counts + them.</p> + <p>The function returns the number of objects matched.</p> + </desc> + </func> + <func> <name>select_delete(Tab, MatchSpec) -> NumDeleted</name> <fsummary>Match the objects in an ETS table against a match_spec and deletes objects where the match_spec returns 'true'</fsummary> <type> @@ -1381,25 +1420,82 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code> </desc> </func> <func> - <name>select_count(Tab, MatchSpec) -> NumMatched</name> - <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> + <name>select_reverse(Tab, MatchSpec) -> [Match]</name> + <fsummary>Match the objects in an ETS table against a match_spec.</fsummary> <type> <v>Tab = tid() | atom()</v> - <v>Object = tuple()</v> + <v>Match = term()</v> <v>MatchSpec = match_spec()</v> - <v>NumMatched = integer()</v> </type> <desc> - <p>Matches the objects in the table <c>Tab</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 - the match_spec the object is not considered a match and is - therefore not counted.</p> - <p>The function could be described as a <c>match_delete/2</c> - that does not actually delete any elements, but only counts - them.</p> - <p>The function returns the number of objects matched.</p> + + <p>Works like <c>select/2</c>, but returns the list in reverse + order for the <c>ordered_set</c> table type. For all other table + types, the return value is identical to that of <c>select/2</c>.</p> + + </desc> + </func> + <func> + <name>select_reverse(Tab, MatchSpec, Limit) -> {[Match],Continuation} | '$end_of_table'</name> + <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> + table type, traversing is done starting at the last object in + Erlang term order and moves towards the first. For all other + table types, the return value is identical to that of + <c>select/3</c>.</p> + + <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> + matching objects in the table, not the first.</p> + + </desc> + </func> + <func> + <name>select_reverse(Continuation) -> {[Match],Continuation} | '$end_of_table'</name> + <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_reverse/3</c>. If the table is an + <c>ordered_set</c>, the traversal of the table will continue + towards objects with keys earlier in the Erlang term order. The + 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>Example:</p> + <code> +1> T = ets:new(x,[ordered_set]). +2> [ ets:insert(T,{N}) || N <- lists:seq(1,10) ]. +... +3> {R0,C0} = ets:select_reverse(T,[{'_',[],['$_']}],4). +... +4> R0. +[{10},{9},{8},{7}] +5> {R1,C1} = ets:select_reverse(C0). +... +6> R1. +[{6},{5},{4},{3}] +7> {R2,C2} = ets:select_reverse(C1). +... +8> R2. +[{2},{1}] +9> '$end_of_table' = ets:select_reverse(C2). +... + </code> </desc> </func> <func> @@ -1423,7 +1519,7 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code> <fsummary>Return all objects in a given slot of an ETS table.</fsummary> <type> <v>Tab = tid() | atom()</v> - <v>I = int()</v> + <v>I = integer()</v> <v>Object = tuple()</v> </type> <desc> @@ -1444,32 +1540,19 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code> </desc> </func> <func> - <name>tab2file(Tab, Filename) -> ok | {error,Reason}</name> + <name name="tab2file" arity="2"/> <fsummary>Dump an ETS table to a file.</fsummary> - <type> - <v>Tab = tid() | atom()</v> - <v>Filename = string() | atom()</v> - <v>Reason = term()</v> - </type> <desc> - <p>Dumps the table <c>Tab</c> to the file <c>Filename</c>.</p> - <p>Equivalent to <c>tab2file(Tab, Filename,[])</c></p> + <p>Dumps the table <c><anno>Tab</anno></c> to the file <c><anno>Filename</anno></c>.</p> + <p>Equivalent to <c>tab2file(<anno>Tab</anno>, <anno>Filename</anno>,[])</c></p> </desc> </func> <func> - <name>tab2file(Tab, Filename, Options) -> ok | {error,Reason}</name> + <name name="tab2file" arity="3"/> <fsummary>Dump an ETS table to a file.</fsummary> - <type> - <v>Tab = tid() | atom()</v> - <v>Filename = string() | atom()</v> - <v>Options = [Option]</v> - <v>Option = {extended_info, [ExtInfo]}</v> - <v>ExtInfo = object_count | md5sum</v> - <v>Reason = term()</v> - </type> <desc> - <p>Dumps the table <c>Tab</c> to the file <c>Filename</c>.</p> + <p>Dumps the table <c><anno>Tab</anno></c> to the file <c><anno>Filename</anno></c>.</p> <p>When dumping the table, certain information about the table is dumped to a header at the beginning of the dump. This information contains data about the table type, @@ -1506,26 +1589,15 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code> </desc> </func> <func> - <name>tab2list(Tab) -> [Object]</name> + <name name="tab2list" arity="1"/> <fsummary>Return a list of all objects in an ETS table.</fsummary> - <type> - <v>Tab = tid() | atom()</v> - <v>Object = tuple()</v> - </type> <desc> - <p>Returns a list of all objects in the table <c>Tab</c>.</p> + <p>Returns a list of all objects in the table <c><anno>Tab</anno></c>.</p> </desc> </func> <func> - <name>tabfile_info(Filename) -> {ok, TableInfo} | {error, Reason}</name> + <name name="tabfile_info" arity="1"/> <fsummary>Return a list of all objects in an ETS table.</fsummary> - <type> - <v>Filename = string() | atom()</v> - <v>TableInfo = [InfoItem]</v> - <v>InfoItem = {InfoTag, term()}</v> - <v>InfoTag = name | type | protection | named_table | keypos | size | extended_info | version</v> - <v>Reason = term()</v> - </type> <desc> <p>Returns information about the table dumped to file by <seealso marker="#tab2file/2">tab2file/2</seealso> or @@ -1571,7 +1643,7 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code> one or more of the atoms <c>object_count</c> and <c>md5sum</c>.</item> <tag>version</tag> - <item>A tuple <c>{Major,Minor}</c> containing the major and + <item>A tuple <c>{<anno>Major</anno>,<anno>Minor</anno>}</c> containing the major and minor version of the file format for ets table dumps. This version field was added beginning with stdlib-1.5.1, files dumped with older versions will return <c>{0,0}</c> in this @@ -1584,20 +1656,11 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code> </desc> </func> <func> - <name>table(Tab [, Options]) -> QueryHandle</name> + <name name="table" arity="1"/> + <name name="table" arity="2"/> <fsummary>Return a QLC query handle.</fsummary> - <type> - <v>Tab = tid() | atom()</v> - <v>QueryHandle = - a query handle, see qlc(3) -</v> - <v>Options = [Option] | Option</v> - <v>Option = {n_objects, NObjects} | {traverse, TraverseMethod}</v> - <v>NObjects = default | integer() > 0</v> - <v>TraverseMethod = first_next | last_prev | select | {select, MatchSpec}</v> - <v>MatchSpec = match_spec()</v> - </type> <desc> - <p> <marker id="qlc_table"></marker> -Returns a QLC (Query List + <p><marker id="qlc_table"></marker>Returns a QLC (Query List Comprehension) query handle. The module <c>qlc</c> implements a query language aimed mainly at Mnesia but ETS tables, Dets tables, and lists are also recognized by QLC as sources of @@ -1632,7 +1695,7 @@ Returns a QLC (Query List that matches all objects.</p> </item> <item> - <p><c>{select, MatchSpec}</c>. As for <c>select</c> + <p><c>{select, <anno>MatchSpec</anno>}</c>. As for <c>select</c> the table is traversed by calling <c>ets:select/3</c> and <c>ets:select/1</c>. The difference is that the match_spec is explicitly given. This is how to state @@ -1660,41 +1723,31 @@ true</pre> </desc> </func> <func> - <name>test_ms(Tuple, MatchSpec) -> {ok, Result} | {error, Errors}</name> + <name name="test_ms" arity="2"/> <fsummary>Test a match_spec for use in ets:select/2.</fsummary> - <type> - <v>Tuple = tuple()</v> - <v>MatchSpec = match_spec()</v> - <v>Result = term()</v> - <v>Errors = [{warning|error, string()}]</v> - </type> <desc> <p>This function is a utility to test a <seealso marker="#match_spec">match_spec</seealso> used in calls to <c>ets:select/2</c>. The function both tests - <c>MatchSpec</c> for "syntactic" correctness and runs the - match_spec against the object <c>Tuple</c>. If the match_spec - contains errors, the tuple <c>{error, Errors}</c> is returned - where <c>Errors</c> is a list of natural language + <c><anno>MatchSpec</anno></c> for "syntactic" correctness and runs the + match_spec against the object <c><anno>Tuple</anno></c>. If the match_spec + contains errors, the tuple <c>{error, <anno>Errors</anno>}</c> is returned + where <c><anno>Errors</anno></c> is a list of natural language descriptions of what was wrong with the match_spec. If the match_spec is syntactically OK, the function returns - <c>{ok,Term}</c> where <c>Term</c> is what would have been + <c>{ok,<anno>Result</anno>}</c> where <c><anno>Result</anno></c> is what would have been the result in a real <c>ets:select/2</c> call or <c>false</c> - if the match_spec does not match the object <c>Tuple</c>.</p> + if the match_spec does not match the object <c><anno>Tuple</anno></c>.</p> <p>This is a useful debugging and test tool, especially when writing complicated <c>ets:select/2</c> calls.</p> </desc> </func> <func> - <name>to_dets(Tab, DetsTab) -> Tab</name> + <name name="to_dets" arity="2"/> <fsummary>Fill a Dets table with objects from an ETS table.</fsummary> - <type> - <v>Tab = tid() | atom()</v> - <v>DetsTab = atom()</v> - </type> <desc> <p>Fills an already created/opened Dets table with the objects - in the already opened ETS table named <c>Tab</c>. The Dets + in the already opened ETS table named <c><anno>Tab</anno></c>. The Dets table is emptied before the objects are inserted.</p> </desc> </func> @@ -1707,7 +1760,7 @@ true</pre> <v>Tab = tid() | atom()</v> <v>Key = term()</v> <v>UpdateOp = {Pos,Incr} | {Pos,Incr,Threshold,SetValue}</v> - <v>Pos = Incr = Threshold = SetValue = Result = int()</v> + <v>Pos = Incr = Threshold = SetValue = Result = integer()</v> </type> <desc> <p>This function provides an efficient way to update one or more @@ -1769,7 +1822,7 @@ true</pre> <type> <v>Tab = tid() | atom()</v> <v>Key = Value = term()</v> - <v>Pos = int()</v> + <v>Pos = integer()</v> </type> <desc> <p>This function provides an efficient way to update one or more diff --git a/lib/stdlib/doc/src/file_sorter.xml b/lib/stdlib/doc/src/file_sorter.xml index ccb32659a0..a6b3633066 100644 --- a/lib/stdlib/doc/src/file_sorter.xml +++ b/lib/stdlib/doc/src/file_sorter.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2001</year><year>2010</year> + <year>2001</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -89,7 +89,7 @@ considerably. The <c>keysort</c>, <c>keymerge</c> and <c>keycheck</c> functions do not accept ordering functions. </item> - <item><c>{unique, bool()}</c>. When sorting or merging files, + <item><c>{unique, boolean()}</c>. When sorting or merging files, only the first of a sequence of terms that compare equal (<c>==</c>) is output if this option is set to <c>true</c>. The default value is <c>false</c> which implies that all terms that @@ -112,7 +112,7 @@ overwritten. Temporary files are deleted unless some uncaught EXIT signal occurs. </item> - <item><c>{compressed, bool()}</c>. Temporary files and the + <item><c>{compressed, boolean()}</c>. Temporary files and the output file may be compressed. The default value <c>false</c> implies that written files are not compressed. Regardless of the value of the <c>compressed</c> @@ -128,39 +128,6 @@ merged at a time. This option should rarely be needed. </item> </list> - <p>To summarize, here is the syntax of the options:</p> - <list type="bulleted"> - <item> - <p><c>Options = [Option] | Option</c></p> - </item> - <item> - <p><c>Option = {header, HeaderLength} | {format, Format} | {order, Order} | {unique, bool()} | {tmpdir, TempDirectory} | {compressed, bool()} | {size, Size} | {no_files, NoFiles}</c></p> - </item> - <item> - <p><c>HeaderLength = int() > 0</c></p> - </item> - <item> - <p><c>Format = binary_term | term | binary | FormatFun</c></p> - </item> - <item> - <p><c>FormatFun = fun(Binary) -> Term</c></p> - </item> - <item> - <p><c>Order = ascending | descending | OrderFun</c></p> - </item> - <item> - <p><c>OrderFun = fun(Term, Term) -> bool()</c></p> - </item> - <item> - <p><c>TempDirectory = "" | file_name()</c></p> - </item> - <item> - <p><c>Size = int() >= 0</c></p> - </item> - <item> - <p><c>NoFiles = int() > 1</c></p> - </item> - </list> <p>As an alternative to sorting files, a function of one argument can be given as input. When called with the argument <c>read</c> the function is assumed to return <c>end_of_input</c> or @@ -234,8 +201,8 @@ output(L) -> occurs are:</p> <list type="bulleted"> <item> - <p><c>bad_object</c>, <c>{bad_object, FileName}</c>. - Applying the format function failed for some binary, + <p><c>bad_object</c>, <c>{bad_object, FileName}</c>. + Applying the format function failed for some binary, or the key(s) could not be extracted from some term.</p> </item> <item> @@ -243,148 +210,181 @@ output(L) -> to read some term.</p> </item> <item> - <p><c>{file_error, FileName, Reason2}</c>. See - <c>file(3)</c> for an explanation of <c>Reason2</c>.</p> + <p><c>{file_error, FileName, file:posix()}</c>. See + <c>file(3)</c> for an explanation of <c>file:posix()</c>.</p> </item> <item> - <p><c>{premature_eof, FileName}</c>. End-of-file was + <p><c>{premature_eof, FileName}</c>. End-of-file was encountered inside some binary term.</p> </item> </list> - <p><em>Types</em></p> - <pre> -Binary = binary() -FileName = file_name() -FileNames = [FileName] -ICommand = read | close -IReply = end_of_input | {end_of_input, Value} | {[Object], Infun} | InputReply -Infun = fun(ICommand) -> IReply -Input = FileNames | Infun -InputReply = Term -KeyPos = int() > 0 | [int() > 0] -OCommand = {value, Value} | [Object] | close -OReply = Outfun | OutputReply -Object = Term | Binary -Outfun = fun(OCommand) -> OReply -Output = FileName | Outfun -OutputReply = Term -Term = term() -Value = Term</pre> </description> + + <datatypes> + <datatype> + <name name="file_name"/><br/> + </datatype> + <datatype> + <name name="file_names"/><br/> + </datatype> + <datatype> + <name name="i_command"/><br/> + </datatype> + <datatype> + <name name="i_reply"/><br/> + </datatype> + <datatype> + <name name="infun"/><br/> + </datatype> + <datatype> + <name name="input"/><br/> + </datatype> + <datatype> + <name name="input_reply"/><br/> + </datatype> + <datatype> + <name name="o_command"/><br/> + </datatype> + <datatype> + <name name="o_reply"/><br/> + </datatype> + <datatype> + <name name="object"/><br/> + </datatype> + <datatype> + <name name="outfun"/><br/> + </datatype> + <datatype> + <name name="output"/><br/> + </datatype> + <datatype> + <name name="output_reply"/><br/> + </datatype> + <datatype> + <name name="value"/><br/> + </datatype> + <datatype> + <name name="options"/><br/> + </datatype> + <datatype> + <name name="option"/><br/> + </datatype> + <datatype> + <name name="format"/><br/> + </datatype> + <datatype> + <name name="format_fun"/><br/> + </datatype> + <datatype> + <name name="header_length"/><br/> + </datatype> + <datatype> + <name name="key_pos"/><br/> + </datatype> + <datatype> + <name name="no_files"/><br/> + </datatype> + <datatype> + <name name="order"/><br/> + </datatype> + <datatype> + <name name="order_fun"/><br/> + </datatype> + <datatype> + <name name="size"/><br/> + </datatype> + <datatype> + <name name="tmp_directory"/><br/> + </datatype> + <datatype> + <name name="reason"/><br/> + </datatype> + </datatypes> + <funcs> <func> - <name>sort(FileName) -> Reply</name> - <name>sort(Input, Output) -> Reply</name> - <name>sort(Input, Output, Options) -> Reply</name> + <name name="sort" arity="1"/> <fsummary>Sort terms on files.</fsummary> - <type> - <v>Reply = ok | {error, Reason} | InputReply | OutputReply</v> - </type> <desc> - <p>Sorts terms on files. - </p> - <p><c>sort(FileName)</c> is equivalent to - <c>sort([FileName], FileName)</c>. - </p> - <p><c>sort(Input, Output)</c> is equivalent to - <c>sort(Input, Output, [])</c>. - </p> - <p></p> + <p>Sorts terms on files. <c>sort(FileName)</c> is equivalent + to <c>sort([FileName], FileName)</c>.</p> + </desc> + </func> + <func> + <name name="sort" arity="2"/> + <name name="sort" arity="3"/> + <fsummary>Sort terms on files.</fsummary> + <desc> + <p>Sorts terms on files. <c>sort(Input, Output)</c> is + equivalent to <c>sort(Input, Output, [])</c>.</p> + </desc> + </func> + <func> + <name name="keysort" arity="2"/> + <fsummary>Sort terms on files by key.</fsummary> + <desc> + <p>Sorts tuples on files. <c>keysort(N, FileName)</c> is + equivalent to <c>keysort(N, [FileName], FileName)</c>.</p> </desc> </func> <func> - <name>keysort(KeyPos, FileName) -> Reply</name> - <name>keysort(KeyPos, Input, Output) -> Reply</name> - <name>keysort(KeyPos, Input, Output, Options) -> Reply</name> + <name name="keysort" arity="3"/> + <name name="keysort" arity="4"/> <fsummary>Sort terms on files by key.</fsummary> - <type> - <v>Reply = ok | {error, Reason} | InputReply | OutputReply</v> - </type> <desc> <p>Sorts tuples on files. The sort is performed on the - element(s) mentioned in <c>KeyPos</c>. If two tuples - compare equal (<c>==</c>) on one element, next element according to - <c>KeyPos</c> is compared. The sort is stable. - </p> - <p><c>keysort(N, FileName)</c> is equivalent to - <c>keysort(N, [FileName], FileName)</c>. - </p> + element(s) mentioned in <c><anno>KeyPos</anno></c>. If two + tuples compare equal (<c>==</c>) on one element, next + element according to <c><anno>KeyPos</anno></c> + is compared. The sort is stable.</p> <p><c>keysort(N, Input, Output)</c> is equivalent to - <c>keysort(N, Input, Output, [])</c>. - </p> - <p></p> + <c>keysort(N, Input, Output, [])</c>.</p> </desc> </func> <func> - <name>merge(FileNames, Output) -> Reply</name> - <name>merge(FileNames, Output, Options) -> Reply</name> + <name name="merge" arity="2"/> + <name name="merge" arity="3"/> <fsummary>Merge terms on files.</fsummary> - <type> - <v>Reply = ok | {error, Reason} | OutputReply</v> - </type> <desc> <p>Merges terms on files. Each input file is assumed to be - sorted. - </p> + sorted.</p> <p><c>merge(FileNames, Output)</c> is equivalent to - <c>merge(FileNames, Output, [])</c>. - </p> + <c>merge(FileNames, Output, [])</c>.</p> </desc> </func> <func> - <name>keymerge(KeyPos, FileNames, Output) -> Reply</name> - <name>keymerge(KeyPos, FileNames, Output, Options) -> Reply</name> + <name name="keymerge" arity="3"/> + <name name="keymerge" arity="4"/> <fsummary>Merge terms on files by key.</fsummary> - <type> - <v>Reply = ok | {error, Reason} | OutputReply</v> - </type> <desc> <p>Merges tuples on files. Each input file is assumed to be - sorted on key(s). - </p> + sorted on key(s).</p> <p><c>keymerge(KeyPos, FileNames, Output)</c> is equivalent - to <c>keymerge(KeyPos, FileNames, Output, [])</c>. - </p> - <p></p> + to <c>keymerge(KeyPos, FileNames, Output, [])</c>.</p> </desc> </func> <func> - <name>check(FileName) -> Reply</name> - <name>check(FileNames, Options) -> Reply</name> + <name name="check" arity="1"/> + <name name="check" arity="2"/> <fsummary>Check whether terms on files are sorted.</fsummary> - <type> - <v>Reply = {ok, [Result]} | {error, Reason}</v> - <v>Result = {FileName, TermPosition, Term}</v> - <v>TermPosition = int() > 1</v> - </type> <desc> <p>Checks files for sortedness. If a file is not sorted, the first out-of-order element is returned. The first term on a - file has position 1. - </p> + file has position 1.</p> <p><c>check(FileName)</c> is equivalent to - <c>check([FileName], [])</c>. - </p> + <c>check([FileName], [])</c>.</p> </desc> </func> <func> - <name>keycheck(KeyPos, FileName) -> CheckReply</name> - <name>keycheck(KeyPos, FileNames, Options) -> Reply</name> + <name name="keycheck" arity="2"/> + <name name="keycheck" arity="3"/> <fsummary>Check whether terms on files are sorted by key.</fsummary> - <type> - <v>Reply = {ok, [Result]} | {error, Reason}</v> - <v>Result = {FileName, TermPosition, Term}</v> - <v>TermPosition = int() > 1</v> - </type> <desc> <p>Checks files for sortedness. If a file is not sorted, the first out-of-order element is returned. The first term on a - file has position 1. - </p> + file has position 1.</p> <p><c>keycheck(KeyPos, FileName)</c> is equivalent - to <c>keycheck(KeyPos, [FileName], [])</c>. - </p> - <p></p> + to <c>keycheck(KeyPos, [FileName], [])</c>.</p> </desc> </func> </funcs> diff --git a/lib/stdlib/doc/src/filelib.xml b/lib/stdlib/doc/src/filelib.xml index c1c4ca9350..f3079c7337 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>2009</year> + <year>2003</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -36,101 +36,94 @@ <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>For more information about raw file names, see the <seealso marker="kernel:file">file</seealso> module.</p> </description> - <section> - <title>DATA TYPES</title> - <code type="none"> -filename() = string() | atom() | DeepList -dirname() = filename() -DeepList = [char() | atom() | DeepList]</code> - </section> + <datatypes> + <datatype> + <name name="filename"/> + </datatype> + <datatype> + <name name="dirname"/> + </datatype> + </datatypes> <funcs> <func> - <name>ensure_dir(Name) -> ok | {error, Reason}</name> + <name name="ensure_dir" arity="1"/> <fsummary>Ensure that all parent directories for a file or directory exist.</fsummary> - <type> - <v>Name = filename() | dirname()</v> - <v>Reason = posix() -- see file(3)</v> - </type> <desc> <p>The <c>ensure_dir/1</c> function ensures that all parent - directories for the given file or directory name <c>Name</c> + directories for the given file or directory name <c><anno>Name</anno></c> exist, trying to create them if necessary.</p> <p>Returns <c>ok</c> if all parent directories already exist - or could be created, or <c>{error, Reason}</c> if some parent + or could be created, or <c>{error, <anno>Reason</anno>}</c> if some parent directory does not exist and could not be created for some reason.</p> </desc> </func> <func> - <name>file_size(Filename) -> integer()</name> + <name name="file_size" arity="1"/> <fsummary>Return the size in bytes of the file.</fsummary> <desc> <p>The <c>file_size</c> function returns the size of the given file.</p> </desc> </func> <func> - <name>fold_files(Dir, RegExp, Recursive, Fun, AccIn) -> AccOut </name> + <name name="fold_files" arity="5"/> <fsummary>Fold over all files matching a regular expression.</fsummary> - <type> - <v>Dir = dirname()</v> - <v>RegExp = regular_expression_string()</v> - <v>Recursive = true|false</v> - <v>Fun = fun(F, AccIn) -> AccOut</v> - <v>AccIn = AccOut = term()</v> - </type> <desc> <p>The <c>fold_files/5</c> function folds the function - <c>Fun</c> over all (regular) files <c>F</c> in the - directory <c>Dir</c> that match the regular expression <c>RegExp</c> + <c><anno>Fun</anno></c> over all (regular) files <c><anno>F</anno></c> in the + directory <c><anno>Dir</anno></c> that match the regular expression <c><anno>RegExp</anno></c> (see the <seealso marker="re">re</seealso> module for a description of the allowed regular expressions). - If <c>Recursive</c> is true all sub-directories to <c>Dir</c> + If <c><anno>Recursive</anno></c> is true all sub-directories to <c>Dir</c> are processed. The regular expression matching is done on just the filename without the directory part.</p> + + <p>If Unicode file name translation is in effect and the file + system is completely transparent, file names that cannot be + interpreted as Unicode may be encountered, in which case the + <c>fun()</c> must be prepared to handle raw file names + (i.e. binaries). If the regular expression contains + codepoints beyond 255, it will not match file names that do + not conform to the expected character encoding (i.e. are not + encoded in valid UTF-8).</p> + + <p>For more information about raw file names, see the + <seealso marker="kernel:file">file</seealso> module.</p> </desc> </func> <func> - <name>is_dir(Name) -> true | false</name> + <name name="is_dir" arity="1"/> <fsummary>Test whether Name refer to a directory or not</fsummary> - <type> - <v>Name = filename() | dirname()</v> - </type> <desc> - <p>The <c>is_dir/1</c> function returns <c>true</c> if <c>Name</c> + <p>The <c>is_dir/1</c> function returns <c>true</c> if <c><anno>Name</anno></c> refers to a directory, and <c>false</c> otherwise.</p> </desc> </func> <func> - <name>is_file(Name) -> true | false</name> + <name name="is_file" arity="1"/> <fsummary>Test whether Name refer to a file or directory.</fsummary> - <type> - <v>Name = filename() | dirname()</v> - </type> <desc> - <p>The <c>is_file/1</c> function returns <c>true</c> if <c>Name</c> + <p>The <c>is_file/1</c> function returns <c>true</c> if <c><anno>Name</anno></c> refers to a file or a directory, and <c>false</c> otherwise.</p> </desc> </func> <func> - <name>is_regular(Name) -> true | false</name> + <name name="is_regular" arity="1"/> <fsummary>Test whether Name refer to a (regular) file.</fsummary> - <type> - <v>Name = filename()</v> - </type> <desc> - <p>The <c>is_regular/1</c> function returns <c>true</c> if <c>Name</c> + <p>The <c>is_regular/1</c> function returns <c>true</c> if <c><anno>Name</anno></c> refers to a file (regular file), and <c>false</c> otherwise.</p> </desc> </func> <func> - <name>last_modified(Name) -> {{Year,Month,Day},{Hour,Min,Sec}} | 0</name> + <name name="last_modified" arity="1"/> <fsummary>Return the local date and time when a file was last modified.</fsummary> - <type> - <v>Name = filename() | dirname()</v> - </type> <desc> <p>The <c>last_modified/1</c> function returns the date and time the given file or directory was last modified, or 0 if the file @@ -138,14 +131,11 @@ DeepList = [char() | atom() | DeepList]</code> </desc> </func> <func> - <name>wildcard(Wildcard) -> list()</name> + <name name="wildcard" arity="1"/> <fsummary>Match filenames using Unix-style wildcards.</fsummary> - <type> - <v>Wildcard = filename() | dirname()</v> - </type> <desc> <p>The <c>wildcard/1</c> function returns a list of all files - that match Unix-style wildcard-string <c>Wildcard</c>.</p> + that match Unix-style wildcard-string <c><anno>Wildcard</anno></c>.</p> <p>The wildcard string looks like an ordinary filename, except that certain "wildcard characters" are interpreted in a special way. The following characters are special: @@ -160,6 +150,12 @@ DeepList = [char() | atom() | DeepList]</code> <p>Matches any number of characters up to the end of the filename, the next dot, or the next slash.</p> </item> + <tag>[Character1,Character2,...]</tag> + <item> + <p>Matches any of the characters listed. Two characters + separated by a hyphen will match a range of characters. + Example: <c>[A-Z]</c> will match any uppercase letter.</p> + </item> <tag>{Item,...}</tag> <item> <p>Alternation. Matches one of the alternatives.</p> @@ -199,15 +195,11 @@ DeepList = [char() | atom() | DeepList]</code> </desc> </func> <func> - <name>wildcard(Wildcard, Cwd) -> list()</name> + <name name="wildcard" arity="2"/> <fsummary>Match filenames using Unix-style wildcards starting at a specified directory.</fsummary> - <type> - <v>Wildcard = filename() | dirname()</v> - <v>Cwd = dirname()</v> - </type> <desc> <p>The <c>wildcard/2</c> function works like <c>wildcard/1</c>, - except that instead of the actual working directory, <c>Cwd</c> + except that instead of the actual working directory, <c><anno>Cwd</anno></c> will be used.</p> </desc> </func> diff --git a/lib/stdlib/doc/src/filename.xml b/lib/stdlib/doc/src/filename.xml index 0cf82fa48b..bc3a616d39 100644 --- a/lib/stdlib/doc/src/filename.xml +++ b/lib/stdlib/doc/src/filename.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1997</year><year>2009</year> + <year>1997</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -43,23 +43,16 @@ only, even if the arguments contain back slashes. Use <c>join/1</c> to normalize a file name by removing redundant directory separators.</p> + <p>The module supports raw file names in the way that if a binary is present, or the file name cannot be interpreted according to the return value of + <seealso marker="kernel:file#native_name_encoding/0">file:native_name_encoding/0</seealso>, a raw file name will also be returned. For example filename:join/1 provided with a path component being a binary (and also not being possible to interpret under the current native file name encoding) will result in a raw file name being returned (the join operation will have been performed of course). For more information about raw file names, see the <seealso marker="kernel:file">file</seealso> module.</p> </description> - <section> - <title>DATA TYPES</title> - <code type="none"> -name() = string() | atom() | DeepList - DeepList = [char() | atom() | DeepList]</code> - </section> <funcs> <func> - <name>absname(Filename) -> string()</name> + <name name="absname" arity="1"/> <fsummary>Convert a filename to an absolute name, relative the working directory</fsummary> - <type> - <v>Filename = name()</v> - </type> <desc> - <p>Converts a relative <c>Filename</c> and returns an absolute + <p>Converts a relative <c><anno>Filename</anno></c> and returns an absolute name. No attempt is made to create the shortest absolute name, because this can give incorrect results on file systems which allow links.</p> @@ -86,44 +79,33 @@ name() = string() | atom() | DeepList </desc> </func> <func> - <name>absname(Filename, Dir) -> string()</name> + <name name="absname" arity="2"/> <fsummary>Convert a filename to an absolute name, relative a specified directory</fsummary> - <type> - <v>Filename = name()</v> - <v>Dir = string()</v> - </type> <desc> <p>This function works like <c>absname/1</c>, except that the directory to which the file name should be made relative - is given explicitly in the <c>Dir</c> argument.</p> + is given explicitly in the <c><anno>Dir</anno></c> argument.</p> </desc> </func> <func> - <name>absname_join(Dir, Filename) -> string()</name> + <name name="absname_join" arity="2"/> <fsummary>Join an absolute directory with a relative filename</fsummary> - <type> - <v>Dir = string()</v> - <v>Filename = name()</v> - </type> <desc> <p>Joins an absolute directory with a relative filename. Similar to <c>join/2</c>, but on platforms with tight restrictions on raw filename length and no support for symbolic links (read: VxWorks), leading parent directory - components in <c>Filename</c> are matched against trailing - directory components in <c>Dir</c> so they can be removed + components in <c><anno>Filename</anno></c> are matched against trailing + directory components in <c><anno>Dir</anno></c> so they can be removed from the result - minimizing its length.</p> </desc> </func> <func> - <name>basename(Filename) -> string()</name> + <name name="basename" arity="1"/> <fsummary>Return the last component of a filename</fsummary> - <type> - <v>Filename = name()</v> - </type> <desc> - <p>Returns the last component of <c>Filename</c>, or - <c>Filename</c> itself if it does not contain any directory + <p>Returns the last component of <c><anno>Filename</anno></c>, or + <c><anno>Filename</anno></c> itself if it does not contain any directory separators.</p> <pre> 5> <input>filename:basename("foo").</input> @@ -135,14 +117,11 @@ name() = string() | atom() | DeepList </desc> </func> <func> - <name>basename(Filename, Ext) -> string()</name> + <name name="basename" arity="2"/> <fsummary>Return the last component of a filename, stripped of the specified extension</fsummary> - <type> - <v>Filename = Ext = name()</v> - </type> <desc> - <p>Returns the last component of <c>Filename</c> with the - extension <c>Ext</c> stripped. This function should be used + <p>Returns the last component of <c><anno>Filename</anno></c> with the + extension <c><anno>Ext</anno></c> stripped. This function should be used to remove a specific extension which might, or might not, be there. Use <c>rootname(basename(Filename))</c> to remove an extension that exists, but you are not sure which one it is.</p> @@ -160,13 +139,10 @@ name() = string() | atom() | DeepList </desc> </func> <func> - <name>dirname(Filename) -> string()</name> + <name name="dirname" arity="1"/> <fsummary>Return the directory part of a path name</fsummary> - <type> - <v>Filename = name()</v> - </type> <desc> - <p>Returns the directory part of <c>Filename</c>.</p> + <p>Returns the directory part of <c><anno>Filename</anno></c>.</p> <pre> 13> <input>filename:dirname("/usr/src/kalle.erl").</input> "/usr/src" @@ -178,13 +154,10 @@ name() = string() | atom() | DeepList </desc> </func> <func> - <name>extension(Filename) -> string()</name> + <name name="extension" arity="1"/> <fsummary>Return the file extension</fsummary> - <type> - <v>Filename = name()</v> - </type> <desc> - <p>Returns the file extension of <c>Filename</c>, including + <p>Returns the file extension of <c><anno>Filename</anno></c>, including the period. Returns an empty string if there is no extension.</p> <pre> 15> <input>filename:extension("foo.erl").</input> @@ -194,11 +167,8 @@ name() = string() | atom() | DeepList </desc> </func> <func> - <name>flatten(Filename) -> string()</name> + <name name="flatten" arity="1"/> <fsummary>Convert a filename to a flat string</fsummary> - <type> - <v>Filename = name()</v> - </type> <desc> <p>Converts a possibly deep list filename consisting of characters and atoms into the corresponding flat string @@ -206,14 +176,11 @@ name() = string() | atom() | DeepList </desc> </func> <func> - <name>join(Components) -> string()</name> + <name name="join" arity="1"/> <fsummary>Join a list of filename components with directory separators</fsummary> - <type> - <v>Components = [string()]</v> - </type> <desc> - <p>Joins a list of file name <c>Components</c> with directory - separators. If one of the elements of <c>Components</c> + <p>Joins a list of file name <c><anno>Components</anno></c> with directory + separators. If one of the elements of <c><anno>Components</anno></c> includes an absolute path, for example <c>"/xxx"</c>, the preceding elements, if any, are removed from the result.</p> <p>The result is "normalized":</p> @@ -233,24 +200,18 @@ name() = string() | atom() | DeepList </desc> </func> <func> - <name>join(Name1, Name2) -> string()</name> + <name name="join" arity="2"/> <fsummary>Join two filename components with directory separators</fsummary> - <type> - <v>Name1 = Name2 = string()</v> - </type> <desc> <p>Joins two file name components with directory separators. - Equivalent to <c>join([Name1, Name2])</c>.</p> + Equivalent to <c>join([<anno>Name1</anno>, <anno>Name2</anno>])</c>.</p> </desc> </func> <func> - <name>nativename(Path) -> string()</name> + <name name="nativename" arity="1"/> <fsummary>Return the native form of a file path</fsummary> - <type> - <v>Path = string()</v> - </type> <desc> - <p>Converts <c>Path</c> to a form accepted by the command shell + <p>Converts <c><anno>Path</anno></c> to a form accepted by the command shell and native applications on the current platform. On Windows, forward slashes is converted to backward slashes. On all platforms, the name is normalized as done by <c>join/1</c>.</p> @@ -263,7 +224,7 @@ name() = string() | atom() | DeepList </desc> </func> <func> - <name>pathtype(Path) -> absolute | relative | volumerelative</name> + <name name="pathtype" arity="1"/> <fsummary>Return the type of a path</fsummary> <desc> <p>Returns the type of path, one of <c>absolute</c>, @@ -293,16 +254,13 @@ name() = string() | atom() | DeepList </desc> </func> <func> - <name>rootname(Filename) -> string()</name> - <name>rootname(Filename, Ext) -> string()</name> + <name name="rootname" arity="1"/> + <name name="rootname" arity="2"/> <fsummary>Remove a filename extension</fsummary> - <type> - <v>Filename = Ext = name()</v> - </type> <desc> <p>Remove a filename extension. <c>rootname/2</c> works as <c>rootname/1</c>, except that the extension is removed only - if it is <c>Ext</c>.</p> + if it is <c><anno>Ext</anno></c>.</p> <pre> 20> <input>filename:rootname("/beam.src/kalle").</input> /beam.src/kalle" @@ -315,15 +273,11 @@ name() = string() | atom() | DeepList </desc> </func> <func> - <name>split(Filename) -> Components</name> + <name name="split" arity="1"/> <fsummary>Split a filename into its path components</fsummary> - <type> - <v>Filename = name()</v> - <v>Components = [string()]</v> - </type> <desc> <p>Returns a list whose elements are the path components of - <c>Filename</c>.</p> + <c><anno>Filename</anno></c>.</p> <pre> 24> <input>filename:split("/usr/local/bin").</input> ["/","usr","local","bin"] @@ -334,47 +288,38 @@ name() = string() | atom() | DeepList </desc> </func> <func> - <name>find_src(Beam) -> {SourceFile, Options} | {error,{ErrorReason,Module}}</name> - <name>find_src(Beam, Rules) -> {SourceFile, Options} | {error,{ErrorReason,Module}}</name> + <name name="find_src" arity="1"/> + <name name="find_src" arity="2"/> <fsummary>Find the filename and compiler options for a module</fsummary> - <type> - <v>Beam = Module | Filename</v> - <v> Module = atom()</v> - <v> Filename = string() | atom()</v> - <v>SourceFile = string()</v> - <v>Options = [Opt]</v> - <v> Opt = {i, string()} | {outdir, string()} | {d, atom()}</v> - <v>ErrorReason = non_existing | preloaded | interpreted</v> - </type> <desc> <p>Finds the source filename and compiler options for a module. The result can be fed to <c>compile:file/2</c> in order to compile the file again.</p> - <p>The <c>Beam</c> argument, which can be a string or an atom, + <p>The <c><anno>Beam</anno></c> argument, which can be a string or an atom, specifies either the module name or the path to the source code, with or without the <c>".erl"</c> extension. In either case, the module must be known by the code server, i.e. - <c>code:which(Module)</c> must succeed.</p> - <p><c>Rules</c> describes how the source directory can be found, + <c>code:which(<anno>Module</anno>)</c> must succeed.</p> + <p><c><anno>Rules</anno></c> describes how the source directory can be found, when the object code directory is known. It is a list of - tuples <c>{BinSuffix, SourceSuffix}</c> and is interpreted + tuples <c>{<anno>BinSuffix</anno>, <anno>SourceSuffix</anno>}</c> and is interpreted as follows: If the end of the directory name where the object - is located matches <c>BinSuffix</c>, then the source code - directory has the same name, but with <c>BinSuffix</c> - replaced by <c>SourceSuffix</c>. <c>Rules</c> defaults to:</p> + is located matches <c><anno>BinSuffix</anno></c>, then the source code + directory has the same name, but with <c><anno>BinSuffix</anno></c> + replaced by <c><anno>SourceSuffix</anno></c>. <c><anno>Rules</anno></c> defaults to:</p> <code type="none"> [{"", ""}, {"ebin", "src"}, {"ebin", "esrc"}]</code> <p>If the source file is found in the resulting directory, then the function returns that location together with - <c>Options</c>. Otherwise, the next rule is tried, and so on.</p> + <c><anno>Options</anno></c>. Otherwise, the next rule is tried, and so on.</p> - <p>The function returns <c>{SourceFile, Options}</c> if it succeeds. - <c>SourceFile</c> is the absolute path to the source file - without the <c>".erl"</c> extension. <c>Options</c> include + <p>The function returns <c>{<anno>SourceFile</anno>, <anno>Options</anno>}</c> if it succeeds. + <c><anno>SourceFile</anno></c> is the absolute path to the source file + without the <c>".erl"</c> extension. <c><anno>Options</anno></c> include the options which are necessary to recompile the file with <c>compile:file/2</c>, but excludes options such as <c>report</c> or <c>verbose</c> which do not change the way - code is generated. The paths in the <c>{outdir, Path}</c> + code is generated. The paths in the <c>{outdir, <anno>Path</anno>}</c> and <c>{i, Path}</c> options are guaranteed to be absolute.</p> diff --git a/lib/stdlib/doc/src/gb_sets.xml b/lib/stdlib/doc/src/gb_sets.xml index 60d8bcbfa3..38de51322f 100644 --- a/lib/stdlib/doc/src/gb_sets.xml +++ b/lib/stdlib/doc/src/gb_sets.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2001</year><year>2010</year> + <year>2001</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -114,34 +114,32 @@ </list> </section> - <section> - <title>DATA TYPES</title> - <code type="none"> -gb_set() = a GB set</code> - </section> + <datatypes> + <datatype> + <name><marker id="type-gb_set">gb_set()</marker></name> + <desc><p>A GB set.</p></desc> + </datatype> + <datatype> + <name name="iter"/> + <desc><p>A GB set iterator.</p></desc> + </datatype> + </datatypes> <funcs> <func> - <name>add(Element, Set1) -> Set2</name> - <name>add_element(Element, Set1) -> Set2</name> + <name name="add" arity="2"/> + <name name="add_element" arity="2"/> <fsummary>Add a (possibly existing) element to a gb_set</fsummary> - <type> - <v>Element = term()</v> - <v>Set1 = Set2 = gb_set()</v> - </type> <desc> - <p>Returns a new gb_set formed from <c>Set1</c> with - <c>Element</c> inserted. If <c>Element</c> is already an - element in <c>Set1</c>, nothing is changed.</p> + <p>Returns a new gb_set formed from <c><anno>Set1</anno></c> with + <c><anno>Element</anno></c> inserted. If <c><anno>Element</anno></c> is already an + element in <c><anno>Set1</anno></c>, nothing is changed.</p> </desc> </func> <func> - <name>balance(Set1) -> Set2</name> + <name name="balance" arity="1"/> <fsummary>Rebalance tree representation of a gb_set</fsummary> - <type> - <v>Set1 = Set2 = gb_set()</v> - </type> <desc> - <p>Rebalances the tree representation of <c>Set1</c>. Note that + <p>Rebalances the tree representation of <c><anno>Set1</anno></c>. Note that this is rarely necessary, but may be motivated when a large number of elements have been deleted from the tree without further insertions. Rebalancing could then be forced in order @@ -150,208 +148,144 @@ gb_set() = a GB set</code> </desc> </func> <func> - <name>delete(Element, Set1) -> Set2</name> + <name name="delete" arity="2"/> <fsummary>Remove an element from a gb_set</fsummary> - <type> - <v>Element = term()</v> - <v>Set1 = Set2 = gb_set()</v> - </type> <desc> - <p>Returns a new gb_set formed from <c>Set1</c> with - <c>Element</c> removed. Assumes that <c>Element</c> is present - in <c>Set1</c>.</p> + <p>Returns a new gb_set formed from <c><anno>Set1</anno></c> with + <c><anno>Element</anno></c> removed. Assumes that <c><anno>Element</anno></c> is present + in <c><anno>Set1</anno></c>.</p> </desc> </func> <func> - <name>delete_any(Element, Set1) -> Set2</name> - <name>del_element(Element, Set1) -> Set2</name> + <name name="delete_any" arity="2"/> + <name name="del_element" arity="2"/> <fsummary>Remove a (possibly non-existing) element from a gb_set</fsummary> - <type> - <v>Element = term()</v> - <v>Set1 = Set2 = gb_set()</v> - </type> <desc> - <p>Returns a new gb_set formed from <c>Set1</c> with - <c>Element</c> removed. If <c>Element</c> is not an element - in <c>Set1</c>, nothing is changed.</p> + <p>Returns a new gb_set formed from <c><anno>Set1</anno></c> with + <c><anno>Element</anno></c> removed. If <c><anno>Element</anno></c> is not an element + in <c><anno>Set1</anno></c>, nothing is changed.</p> </desc> </func> <func> - <name>difference(Set1, Set2) -> Set3</name> - <name>subtract(Set1, Set2) -> Set3</name> + <name name="difference" arity="2"/> + <name name="subtract" arity="2"/> <fsummary>Return the difference of two gb_sets</fsummary> - <type> - <v>Set1 = Set2 = Set3 = gb_set()</v> - </type> <desc> - <p>Returns only the elements of <c>Set1</c> which are not also - elements of <c>Set2</c>.</p> + <p>Returns only the elements of <c><anno>Set1</anno></c> which are not also + elements of <c><anno>Set2</anno></c>.</p> </desc> </func> <func> - <name>empty() -> Set</name> - <name>new() -> Set</name> + <name name="empty" arity="0"/> + <name name="new" arity="0"/> <fsummary>Return an empty gb_set</fsummary> - <type> - <v>Set = gb_set()</v> - </type> <desc> <p>Returns a new empty gb_set.</p> </desc> </func> <func> - <name>filter(Pred, Set1) -> Set2</name> + <name name="filter" arity="2"/> <fsummary>Filter gb_set elements</fsummary> - <type> - <v>Pred = fun (E) -> bool()</v> - <v> E = term()</v> - <v>Set1 = Set2 = gb_set()</v> - </type> <desc> - <p>Filters elements in <c>Set1</c> using predicate function - <c>Pred</c>.</p> + <p>Filters elements in <c><anno>Set1</anno></c> using predicate function + <c><anno>Pred</anno></c>.</p> </desc> </func> <func> - <name>fold(Function, Acc0, Set) -> Acc1</name> + <name name="fold" arity="3"/> <fsummary>Fold over gb_set elements</fsummary> - <type> - <v>Function = fun (E, AccIn) -> AccOut</v> - <v>Acc0 = Acc1 = AccIn = AccOut = term()</v> - <v> E = term()</v> - <v>Set = gb_set()</v> - </type> <desc> - <p>Folds <c>Function</c> over every element in <c>Set</c> + <p>Folds <c><anno>Function</anno></c> over every element in <c><anno>Set</anno></c> returning the final value of the accumulator.</p> </desc> </func> <func> - <name>from_list(List) -> Set</name> + <name name="from_list" arity="1"/> <fsummary>Convert a list into a gb_set</fsummary> - <type> - <v>List = [term()]</v> - <v>Set = gb_set()</v> - </type> <desc> - <p>Returns a gb_set of the elements in <c>List</c>, where - <c>List</c> may be unordered and contain duplicates.</p> + <p>Returns a gb_set of the elements in <c><anno>List</anno></c>, where + <c><anno>List</anno></c> may be unordered and contain duplicates.</p> </desc> </func> <func> - <name>from_ordset(List) -> Set</name> + <name name="from_ordset" arity="1"/> <fsummary>Make a gb_set from an ordset list</fsummary> - <type> - <v>List = [term()]</v> - <v>Set = gb_set()</v> - </type> <desc> - <p>Turns an ordered-set list <c>List</c> into a gb_set. The list + <p>Turns an ordered-set list <c><anno>List</anno></c> into a gb_set. The list must not contain duplicates.</p> </desc> </func> <func> - <name>insert(Element, Set1) -> Set2</name> + <name name="insert" arity="2"/> <fsummary>Add a new element to a gb_set</fsummary> - <type> - <v>Element = term()</v> - <v>Set1 = Set2 = gb_set()</v> - </type> <desc> - <p>Returns a new gb_set formed from <c>Set1</c> with - <c>Element</c> inserted. Assumes that <c>Element</c> is not - present in <c>Set1</c>.</p> + <p>Returns a new gb_set formed from <c><anno>Set1</anno></c> with + <c><anno>Element</anno></c> inserted. Assumes that <c><anno>Element</anno></c> is not + present in <c><anno>Set1</anno></c>.</p> </desc> </func> <func> - <name>intersection(Set1, Set2) -> Set3</name> + <name name="intersection" arity="2"/> <fsummary>Return the intersection of two gb_sets</fsummary> - <type> - <v>Set1 = Set2 = Set3 = gb_set()</v> - </type> <desc> - <p>Returns the intersection of <c>Set1</c> and <c>Set2</c>.</p> + <p>Returns the intersection of <c><anno>Set1</anno></c> and <c><anno>Set2</anno></c>.</p> </desc> </func> <func> - <name>intersection(SetList) -> Set</name> + <name name="intersection" arity="1"/> <fsummary>Return the intersection of a list of gb_sets</fsummary> - <type> - <v>SetList = [gb_set()]</v> - <v>Set = gb_set()</v> - </type> <desc> <p>Returns the intersection of the non-empty list of gb_sets.</p> </desc> </func> <func> - <name>is_disjoint(Set1, Set2) -> bool()</name> + <name name="is_disjoint" arity="2"/> <fsummary>Check whether two gb_sets are disjoint</fsummary> - <type> - <v>Set1 = Set2 = gb_set()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>Set1</c> and - <c>Set2</c> are disjoint (have no elements in common), + <p>Returns <c>true</c> if <c><anno>Set1</anno></c> and + <c><anno>Set2</anno></c> are disjoint (have no elements in common), and <c>false</c> otherwise.</p> </desc> </func> <func> - <name>is_empty(Set) -> bool()</name> + <name name="is_empty" arity="1"/> <fsummary>Test for empty gb_set</fsummary> - <type> - <v>Set = gb_set()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>Set</c> is an empty set, and + <p>Returns <c>true</c> if <c><anno>Set</anno></c> is an empty set, and <c>false</c> otherwise.</p> </desc> </func> <func> - <name>is_member(Element, Set) -> bool()</name> - <name>is_element(Element, Set) -> bool()</name> + <name name="is_member" arity="2"/> + <name name="is_element" arity="2"/> <fsummary>Test for membership of a gb_set</fsummary> - <type> - <v>Element = term()</v> - <v>Set = gb_set()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>Element</c> is an element of - <c>Set</c>, otherwise <c>false</c>.</p> + <p>Returns <c>true</c> if <c><anno>Element</anno></c> is an element of + <c><anno>Set</anno></c>, otherwise <c>false</c>.</p> </desc> </func> <func> - <name>is_set(Term) -> bool()</name> + <name name="is_set" arity="1"/> <fsummary>Test for a gb_set</fsummary> - <type> - <v>Term = term()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>Set</c> appears to be a gb_set, + <p>Returns <c>true</c> if <c><anno>Term</anno></c> appears to be a gb_set, otherwise <c>false</c>.</p> </desc> </func> <func> - <name>is_subset(Set1, Set2) -> bool()</name> + <name name="is_subset" arity="2"/> <fsummary>Test for subset</fsummary> - <type> - <v>Set1 = Set2 = gb_set()</v> - </type> <desc> - <p>Returns <c>true</c> when every element of <c>Set1</c> is - also a member of <c>Set2</c>, otherwise <c>false</c>.</p> + <p>Returns <c>true</c> when every element of <c><anno>Set1</anno></c> is + also a member of <c><anno>Set2</anno></c>, otherwise <c>false</c>.</p> </desc> </func> <func> - <name>iterator(Set) -> Iter</name> + <name name="iterator" arity="1"/> <fsummary>Return an iterator for a gb_set</fsummary> - <type> - <v>Set = gb_set()</v> - <v>Iter = term()</v> - </type> <desc> <p>Returns an iterator that can be used for traversing the - entries of <c>Set</c>; see <c>next/1</c>. The implementation + entries of <c><anno>Set</anno></c>; see <c>next/1</c>. The implementation of this is very efficient; traversing the whole set using <c>next/1</c> is only slightly slower than getting the list of all elements using <c>to_list/1</c> and traversing that. @@ -361,118 +295,84 @@ gb_set() = a GB set</code> </desc> </func> <func> - <name>largest(Set) -> term()</name> + <name name="largest" arity="1"/> <fsummary>Return largest element</fsummary> - <type> - <v>Set = gb_set()</v> - </type> <desc> - <p>Returns the largest element in <c>Set</c>. Assumes that - <c>Set</c> is nonempty.</p> + <p>Returns the largest element in <c><anno>Set</anno></c>. Assumes that + <c><anno>Set</anno></c> is nonempty.</p> </desc> </func> <func> - <name>next(Iter1) -> {Element, Iter2} | none</name> + <name name="next" arity="1"/> <fsummary>Traverse a gb_set with an iterator</fsummary> - <type> - <v>Iter1 = Iter2 = Element = term()</v> - </type> <desc> - <p>Returns <c>{Element, Iter2}</c> where <c>Element</c> is the - smallest element referred to by the iterator <c>Iter1</c>, - and <c>Iter2</c> is the new iterator to be used for + <p>Returns <c>{<anno>Element</anno>, <anno>Iter2</anno>}</c> where <c><anno>Element</anno></c> is the + smallest element referred to by the iterator <c><anno>Iter1</anno></c>, + and <c><anno>Iter2</anno></c> is the new iterator to be used for traversing the remaining elements, or the atom <c>none</c> if no elements remain.</p> </desc> </func> <func> - <name>singleton(Element) -> gb_set()</name> + <name name="singleton" arity="1"/> <fsummary>Return a gb_set with one element</fsummary> - <type> - <v>Element = term()</v> - </type> <desc> - <p>Returns a gb_set containing only the element <c>Element</c>.</p> + <p>Returns a gb_set containing only the element <c><anno>Element</anno></c>.</p> </desc> </func> <func> - <name>size(Set) -> int()</name> + <name name="size" arity="1"/> <fsummary>Return the number of elements in a gb_set</fsummary> - <type> - <v>Set = gb_set()</v> - </type> <desc> - <p>Returns the number of elements in <c>Set</c>.</p> + <p>Returns the number of elements in <c><anno>Set</anno></c>.</p> </desc> </func> <func> - <name>smallest(Set) -> term()</name> + <name name="smallest" arity="1"/> <fsummary>Return smallest element</fsummary> - <type> - <v>Set = gb_set()</v> - </type> <desc> - <p>Returns the smallest element in <c>Set</c>. Assumes that - <c>Set</c> is nonempty.</p> + <p>Returns the smallest element in <c><anno>Set</anno></c>. Assumes that + <c><anno>Set</anno></c> is nonempty.</p> </desc> </func> <func> - <name>take_largest(Set1) -> {Element, Set2}</name> + <name name="take_largest" arity="1"/> <fsummary>Extract largest element</fsummary> - <type> - <v>Set1 = Set2 = gb_set()</v> - <v>Element = term()</v> - </type> <desc> - <p>Returns <c>{Element, Set2}</c>, where <c>Element</c> is the - largest element in <c>Set1</c>, and <c>Set2</c> is this set - with <c>Element</c> deleted. Assumes that <c>Set1</c> is + <p>Returns <c>{<anno>Element</anno>, <anno>Set2</anno>}</c>, where <c><anno>Element</anno></c> is the + largest element in <c><anno>Set1</anno></c>, and <c><anno>Set2</anno></c> is this set + with <c><anno>Element</anno></c> deleted. Assumes that <c><anno>Set1</anno></c> is nonempty.</p> </desc> </func> <func> - <name>take_smallest(Set1) -> {Element, Set2}</name> + <name name="take_smallest" arity="1"/> <fsummary>Extract smallest element</fsummary> - <type> - <v>Set1 = Set2 = gb_set()</v> - <v>Element = term()</v> - </type> <desc> - <p>Returns <c>{Element, Set2}</c>, where <c>Element</c> is the - smallest element in <c>Set1</c>, and <c>Set2</c> is this set - with <c>Element</c> deleted. Assumes that <c>Set1</c> is + <p>Returns <c>{<anno>Element</anno>, <anno>Set2</anno>}</c>, where <c><anno>Element</anno></c> is the + smallest element in <c><anno>Set1</anno></c>, and <c><anno>Set2</anno></c> is this set + with <c><anno>Element</anno></c> deleted. Assumes that <c><anno>Set1</anno></c> is nonempty.</p> </desc> </func> <func> - <name>to_list(Set) -> List</name> + <name name="to_list" arity="1"/> <fsummary>Convert a gb_set into a list</fsummary> - <type> - <v>Set = gb_set()</v> - <v>List = [term()]</v> - </type> <desc> - <p>Returns the elements of <c>Set</c> as a list.</p> + <p>Returns the elements of <c><anno>Set</anno></c> as a list.</p> </desc> </func> <func> - <name>union(Set1, Set2) -> Set3</name> + <name name="union" arity="2"/> <fsummary>Return the union of two gb_sets</fsummary> - <type> - <v>Set1 = Set2 = Set3 = gb_set()</v> - </type> <desc> - <p>Returns the merged (union) gb_set of <c>Set1</c> and - <c>Set2</c>.</p> + <p>Returns the merged (union) gb_set of <c><anno>Set1</anno></c> and + <c><anno>Set2</anno></c>.</p> </desc> </func> <func> - <name>union(SetList) -> Set</name> + <name name="union" arity="1"/> <fsummary>Return the union of a list of gb_sets</fsummary> - <type> - <v>SetList = [gb_set()]</v> - <v>Set = gb_set()</v> - </type> <desc> <p>Returns the merged (union) gb_set of the list of gb_sets.</p> </desc> diff --git a/lib/stdlib/doc/src/gb_trees.xml b/lib/stdlib/doc/src/gb_trees.xml index 94f40c28bd..65c866efbe 100644 --- a/lib/stdlib/doc/src/gb_trees.xml +++ b/lib/stdlib/doc/src/gb_trees.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2001</year><year>2010</year> + <year>2001</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -57,20 +57,22 @@ trees. Behaviour is logarithmic (as it should be).</p> </section> - <section> - <title>DATA TYPES</title> - <code type="none"> -gb_tree() = a GB tree</code> - </section> + <datatypes> + <datatype> + <name><marker id="type-gb_tree">gb_tree()</marker></name> + <desc><p>A GB tree.</p></desc> + </datatype> + <datatype> + <name name="iter"/> + <desc><p>A GB tree iterator.</p></desc> + </datatype> + </datatypes> <funcs> <func> - <name>balance(Tree1) -> Tree2</name> + <name name="balance" arity="1"/> <fsummary>Rebalance a tree</fsummary> - <type> - <v>Tree1 = Tree2 = gb_tree()</v> - </type> <desc> - <p>Rebalances <c>Tree1</c>. Note that this is rarely necessary, + <p>Rebalances <c><anno>Tree1</anno></c>. Note that this is rarely necessary, but may be motivated when a large number of nodes have been deleted from the tree without further insertions. Rebalancing could then be forced in order to minimise lookup times, since @@ -78,139 +80,97 @@ gb_tree() = a GB tree</code> </desc> </func> <func> - <name>delete(Key, Tree1) -> Tree2</name> + <name name="delete" arity="2"/> <fsummary>Remove a node from a tree</fsummary> - <type> - <v>Key = term()</v> - <v>Tree1 = Tree2 = gb_tree()</v> - </type> <desc> - <p>Removes the node with key <c>Key</c> from <c>Tree1</c>; + <p>Removes the node with key <c><anno>Key</anno></c> from <c><anno>Tree1</anno></c>; returns new tree. Assumes that the key is present in the tree, crashes otherwise.</p> </desc> </func> <func> - <name>delete_any(Key, Tree1) -> Tree2</name> + <name name="delete_any" arity="2"/> <fsummary>Remove a (possibly non-existing) node from a tree</fsummary> - <type> - <v>Key = term()</v> - <v>Tree1 = Tree2 = gb_tree()</v> - </type> <desc> - <p>Removes the node with key <c>Key</c> from <c>Tree1</c> if + <p>Removes the node with key <c><anno>Key</anno></c> from <c><anno>Tree1</anno></c> if the key is present in the tree, otherwise does nothing; returns new tree.</p> </desc> </func> <func> - <name>empty() -> Tree</name> + <name name="empty" arity="0"/> <fsummary>Return an empty tree</fsummary> - <type> - <v>Tree = gb_tree()</v> - </type> <desc> <p>Returns a new empty tree</p> </desc> </func> <func> - <name>enter(Key, Val, Tree1) -> Tree2</name> + <name name="enter" arity="3"/> <fsummary>Insert or update key with value in a tree</fsummary> - <type> - <v>Key = Val = term()</v> - <v>Tree1 = Tree2 = gb_tree()</v> - </type> <desc> - <p>Inserts <c>Key</c> with value <c>Val</c> into <c>Tree1</c> if + <p>Inserts <c><anno>Key</anno></c> with value <c><anno>Val</anno></c> into <c><anno>Tree1</anno></c> if the key is not present in the tree, otherwise updates - <c>Key</c> to value <c>Val</c> in <c>Tree1</c>. Returns the + <c><anno>Key</anno></c> to value <c><anno>Val</anno></c> in <c><anno>Tree1</anno></c>. Returns the new tree.</p> </desc> </func> <func> - <name>from_orddict(List) -> Tree</name> + <name name="from_orddict" arity="1"/> <fsummary>Make a tree from an orddict</fsummary> - <type> - <v>List = [{Key, Val}]</v> - <v> Key = Val = term()</v> - <v>Tree = gb_tree()</v> - </type> <desc> - <p>Turns an ordered list <c>List</c> of key-value tuples into a + <p>Turns an ordered list <c><anno>List</anno></c> of key-value tuples into a tree. The list must not contain duplicate keys.</p> </desc> </func> <func> - <name>get(Key, Tree) -> Val</name> + <name name="get" arity="2"/> <fsummary>Look up a key in a tree, if present</fsummary> - <type> - <v>Key = Val = term()</v> - <v>Tree = gb_tree()</v> - </type> <desc> - <p>Retrieves the value stored with <c>Key</c> in <c>Tree</c>. + <p>Retrieves the value stored with <c><anno>Key</anno></c> in <c><anno>Tree</anno></c>. Assumes that the key is present in the tree, crashes otherwise.</p> </desc> </func> <func> - <name>lookup(Key, Tree) -> {value, Val} | none</name> + <name name="lookup" arity="2"/> <fsummary>Look up a key in a tree</fsummary> - <type> - <v>Key = Val = term()</v> - <v>Tree = gb_tree()</v> - </type> <desc> - <p>Looks up <c>Key</c> in <c>Tree</c>; returns - <c>{value, Val}</c>, or <c>none</c> if <c>Key</c> is not + <p>Looks up <c><anno>Key</anno></c> in <c><anno>Tree</anno></c>; returns + <c>{value, <anno>Val</anno>}</c>, or <c>none</c> if <c><anno>Key</anno></c> is not present.</p> </desc> </func> <func> - <name>insert(Key, Val, Tree1) -> Tree2</name> + <name name="insert" arity="3"/> <fsummary>Insert a new key and value in a tree</fsummary> - <type> - <v>Key = Val = term()</v> - <v>Tree1 = Tree2 = gb_tree()</v> - </type> <desc> - <p>Inserts <c>Key</c> with value <c>Val</c> into <c>Tree1</c>; + <p>Inserts <c><anno>Key</anno></c> with value <c><anno>Val</anno></c> into <c><anno>Tree1</anno></c>; returns the new tree. Assumes that the key is not present in the tree, crashes otherwise.</p> </desc> </func> <func> - <name>is_defined(Key, Tree) -> bool()</name> + <name name="is_defined" arity="2"/> <fsummary>Test for membership of a tree</fsummary> - <type> - <v>Tree = gb_tree()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>Key</c> is present in <c>Tree</c>, + <p>Returns <c>true</c> if <c><anno>Key</anno></c> is present in <c><anno>Tree</anno></c>, otherwise <c>false</c>.</p> </desc> </func> <func> - <name>is_empty(Tree) -> bool()</name> + <name name="is_empty" arity="1"/> <fsummary>Test for empty tree</fsummary> - <type> - <v>Tree = gb_tree()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>Tree</c> is an empty tree, and + <p>Returns <c>true</c> if <c><anno>Tree</anno></c> is an empty tree, and <c>false</c> otherwise.</p> </desc> </func> <func> - <name>iterator(Tree) -> Iter</name> + <name name="iterator" arity="1"/> <fsummary>Return an iterator for a tree</fsummary> - <type> - <v>Tree = gb_tree()</v> - <v>Iter = term()</v> - </type> <desc> <p>Returns an iterator that can be used for traversing the - entries of <c>Tree</c>; see <c>next/1</c>. The implementation + entries of <c><anno>Tree</anno></c>; see <c>next/1</c>. The implementation of this is very efficient; traversing the whole tree using <c>next/1</c> is only slightly slower than getting the list of all elements using <c>to_list/1</c> and traversing that. @@ -220,141 +180,99 @@ gb_tree() = a GB tree</code> </desc> </func> <func> - <name>keys(Tree) -> [Key]</name> + <name name="keys" arity="1"/> <fsummary>Return a list of the keys in a tree</fsummary> - <type> - <v>Tree = gb_tree()</v> - <v>Key = term()</v> - </type> <desc> - <p>Returns the keys in <c>Tree</c> as an ordered list.</p> + <p>Returns the keys in <c><anno>Tree</anno></c> as an ordered list.</p> </desc> </func> <func> - <name>largest(Tree) -> {Key, Val}</name> + <name name="largest" arity="1"/> <fsummary>Return largest key and value</fsummary> - <type> - <v>Tree = gb_tree()</v> - <v>Key = Val = term()</v> - </type> <desc> - <p>Returns <c>{Key, Val}</c>, where <c>Key</c> is the largest - key in <c>Tree</c>, and <c>Val</c> is the value associated + <p>Returns <c>{<anno>Key</anno>, <anno>Val</anno>}</c>, where <c><anno>Key</anno></c> is the largest + key in <c><anno>Tree</anno></c>, and <c><anno>Val</anno></c> is the value associated with this key. Assumes that the tree is nonempty.</p> </desc> </func> <func> - <name>map(Function, Tree1) -> Tree2</name> + <name name="map" arity="2"/> <fsummary>Return largest key and value</fsummary> - <type> - <v>Function = fun(K, V1) -> V2</v> - <v>Tree1 = Tree2 = gb_tree()</v> - </type> - <desc><p>maps the function F(K, V1) -> V2 to all key-value pairs - of the tree Tree1 and returns a new tree Tree2 with the same set of keys - as Tree1 and the new set of values V2.</p> + <desc><p>Maps the function F(<anno>K</anno>, <anno>V1</anno>) -> <anno>V2</anno> to all key-value pairs + of the tree <c><anno>Tree1</anno></c> and returns a new tree <c><anno>Tree2</anno></c> with the same set of keys + as <c><anno>Tree1</anno></c> and the new set of values <c><anno>V2</anno></c>.</p> </desc> </func> <func> - <name>next(Iter1) -> {Key, Val, Iter2} | none</name> + <name name="next" arity="1"/> <fsummary>Traverse a tree with an iterator</fsummary> - <type> - <v>Iter1 = Iter2 = Key = Val = term()</v> - </type> <desc> - <p>Returns <c>{Key, Val, Iter2}</c> where <c>Key</c> is the - smallest key referred to by the iterator <c>Iter1</c>, and - <c>Iter2</c> is the new iterator to be used for + <p>Returns <c>{<anno>Key</anno>, <anno>Val</anno>, <anno>Iter2</anno>}</c> where <c><anno>Key</anno></c> is the + smallest key referred to by the iterator <c><anno>Iter1</anno></c>, and + <c><anno>Iter2</anno></c> is the new iterator to be used for traversing the remaining nodes, or the atom <c>none</c> if no nodes remain.</p> </desc> </func> <func> - <name>size(Tree) -> int()</name> + <name name="size" arity="1"/> <fsummary>Return the number of nodes in a tree</fsummary> - <type> - <v>Tree = gb_tree()</v> - </type> <desc> - <p>Returns the number of nodes in <c>Tree</c>.</p> + <p>Returns the number of nodes in <c><anno>Tree</anno></c>.</p> </desc> </func> <func> - <name>smallest(Tree) -> {Key, Val}</name> + <name name="smallest" arity="1"/> <fsummary>Return smallest key and value</fsummary> - <type> - <v>Tree = gb_tree()</v> - <v>Key = Val = term()</v> - </type> <desc> - <p>Returns <c>{Key, Val}</c>, where <c>Key</c> is the smallest - key in <c>Tree</c>, and <c>Val</c> is the value associated + <p>Returns <c>{<anno>Key</anno>, <anno>Val</anno>}</c>, where <c><anno>Key</anno></c> is the smallest + key in <c><anno>Tree</anno></c>, and <c><anno>Val</anno></c> is the value associated with this key. Assumes that the tree is nonempty.</p> </desc> </func> <func> - <name>take_largest(Tree1) -> {Key, Val, Tree2}</name> + <name name="take_largest" arity="1"/> <fsummary>Extract largest key and value</fsummary> - <type> - <v>Tree1 = Tree2 = gb_tree()</v> - <v>Key = Val = term()</v> - </type> <desc> - <p>Returns <c>{Key, Val, Tree2}</c>, where <c>Key</c> is the - largest key in <c>Tree1</c>, <c>Val</c> is the value - associated with this key, and <c>Tree2</c> is this tree with + <p>Returns <c>{<anno>Key</anno>, <anno>Val</anno>, <anno>Tree2</anno>}</c>, where <c><anno>Key</anno></c> is the + largest key in <c><anno>Tree1</anno></c>, <c><anno>Val</anno></c> is the value + associated with this key, and <c><anno>Tree2</anno></c> is this tree with the corresponding node deleted. Assumes that the tree is nonempty.</p> </desc> </func> <func> - <name>take_smallest(Tree1) -> {Key, Val, Tree2}</name> + <name name="take_smallest" arity="1"/> <fsummary>Extract smallest key and value</fsummary> - <type> - <v>Tree1 = Tree2 = gb_tree()</v> - <v>Key = Val = term()</v> - </type> <desc> - <p>Returns <c>{Key, Val, Tree2}</c>, where <c>Key</c> is the - smallest key in <c>Tree1</c>, <c>Val</c> is the value - associated with this key, and <c>Tree2</c> is this tree with + <p>Returns <c>{<anno>Key</anno>, <anno>Val</anno>, <anno>Tree2</anno>}</c>, where <c><anno>Key</anno></c> is the + smallest key in <c><anno>Tree1</anno></c>, <c><anno>Val</anno></c> is the value + associated with this key, and <c><anno>Tree2</anno></c> is this tree with the corresponding node deleted. Assumes that the tree is nonempty.</p> </desc> </func> <func> - <name>to_list(Tree) -> [{Key, Val}]</name> + <name name="to_list" arity="1"/> <fsummary>Convert a tree into a list</fsummary> - <type> - <v>Tree = gb_tree()</v> - <v>Key = Val = term()</v> - </type> <desc> <p>Converts a tree into an ordered list of key-value tuples.</p> </desc> </func> <func> - <name>update(Key, Val, Tree1) -> Tree2</name> + <name name="update" arity="3"/> <fsummary>Update a key to new value in a tree</fsummary> - <type> - <v>Key = Val = term()</v> - <v>Tree1 = Tree2 = gb_tree()</v> - </type> <desc> - <p>Updates <c>Key</c> to value <c>Val</c> in <c>Tree1</c>; + <p>Updates <c><anno>Key</anno></c> to value <c><anno>Val</anno></c> in <c><anno>Tree1</anno></c>; returns the new tree. Assumes that the key is present in the tree.</p> </desc> </func> <func> - <name>values(Tree) -> [Val]</name> + <name name="values" arity="1"/> <fsummary>Return a list of the values in a tree</fsummary> - <type> - <v>Tree = gb_tree()</v> - <v>Val = term()</v> - </type> <desc> - <p>Returns the values in <c>Tree</c> as an ordered list, sorted + <p>Returns the values in <c><anno>Tree</anno></c> as an ordered list, sorted by their corresponding keys. Duplicates are not removed.</p> </desc> </func> diff --git a/lib/stdlib/doc/src/gen_event.xml b/lib/stdlib/doc/src/gen_event.xml index df09294de6..24bcb419fe 100644 --- a/lib/stdlib/doc/src/gen_event.xml +++ b/lib/stdlib/doc/src/gen_event.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2009</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -13,12 +13,12 @@ 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. - + </legalnotice> <title>gen_event</title> @@ -100,6 +100,20 @@ gen_event:stop -----> Module:terminate/2 the specified event manager does not exist or if bad arguments are given.</p> </description> + <datatypes> + <datatype> + <name name="handler"/> + </datatype> + <datatype> + <name name="handler_args"/> + </datatype> + <datatype> + <name name="add_handler_ret"/> + </datatype> + <datatype> + <name name="del_handler_ret"/> + </datatype> + </datatypes> <funcs> <func> <name>start_link() -> Result</name> @@ -630,12 +644,66 @@ gen_event:stop -----> Module:terminate/2 <p>The function should return the updated internal state.</p> </desc> </func> + <func> + <name>Module:format_status(Opt, [PDict, State]) -> Status</name> + <fsummary>Optional function for providing a term describing the + current event handler state.</fsummary> + <type> + <v>Opt = normal | terminate</v> + <v>PDict = [{Key, Value}]</v> + <v>State = term()</v> + <v>Status = term()</v> + </type> + <desc> + <note> + <p>This callback is optional, so event handler modules need + not export it. If a handler does not export this function, + the gen_event module uses the handler state directly for + the purposes described below.</p> + </note> + <p>This function is called by a gen_event process when:</p> + <list typed="bulleted"> + <item>One + of <seealso marker="sys#get_status/1">sys:get_status/1,2</seealso> + is invoked to get the gen_event status. <c>Opt</c> is set + to the atom <c>normal</c> for this case.</item> + <item>The event handler terminates abnormally and gen_event + logs an error. <c>Opt</c> is set to the + atom <c>terminate</c> for this case.</item> + </list> + <p>This function is useful for customising the form and + appearance of the event handler state for these cases. An + event handler callback module wishing to customise + the <c>sys:get_status/1,2</c> return value as well as how + its state appears in termination error logs exports an + instance of <c>format_status/2</c> that returns a term + describing the current state of the event handler.</p> + <p><c>PDict</c> is the current value of the gen_event's + process dictionary.</p> + <p><c>State</c> is the internal state of the event + handler.</p> + <p>The function should return <c>Status</c>, a term that + customises the details of the current state of the event + handler. Any term is allowed for <c>Status</c>. The + gen_event module uses <c>Status</c> as follows:</p> + <list typed="bulleted"> + <item>When <c>sys:get_status/1,2</c> is called, gen_event + ensures that its return value contains <c>Status</c> in + place of the event handler's actual state term.</item> + <item>When an event handler terminates abnormally, gen_event + logs <c>Status</c> in place of the event handler's actual + state term.</item> + </list> + <p>One use for this function is to return compact alternative + state representations to avoid having large state terms + printed in logfiles.</p> + </desc> + </func> </funcs> <section> <title>SEE ALSO</title> - <p><seealso marker="supervisor">supervisor(3)</seealso>, + <p><seealso marker="supervisor">supervisor(3)</seealso>, <seealso marker="sys">sys(3)</seealso></p> </section> </erlref> - diff --git a/lib/stdlib/doc/src/gen_fsm.xml b/lib/stdlib/doc/src/gen_fsm.xml index 739cd0bffd..d15383c621 100644 --- a/lib/stdlib/doc/src/gen_fsm.xml +++ b/lib/stdlib/doc/src/gen_fsm.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2009</year> + <year>1996</year><year>2010</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -13,12 +13,12 @@ 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. - + </legalnotice> <title>gen_fsm</title> @@ -730,33 +730,58 @@ gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4 </desc> </func> <func> - <name>Module:format_status(normal, [PDict, StateData]) -> Status</name> + <name>Module:format_status(Opt, [PDict, StateData]) -> Status</name> <fsummary>Optional function for providing a term describing the current gen_fsm status.</fsummary> <type> + <v>Opt = normal | terminate</v> <v>PDict = [{Key, Value}]</v> <v>StateData = term()</v> - <v>Status = [term()]</v> + <v>Status = term()</v> </type> <desc> - <p><em>This callback is optional, so callback modules need not - export it. The gen_fsm module provides a default - implementation of this function that returns the callback - module state data.</em></p> - <p>This function is called by a gen_fsm process when one - of <seealso marker="sys#get_status/1">sys:get_status/1,2</seealso> - is invoked to get the gen_fsm status. A callback module - wishing to customise the <c>sys:get_status/1,2</c> return - value exports an instance of <c>format_status/2</c> that - returns a term describing the current status of the - gen_fsm.</p> + <note> + <p>This callback is optional, so callback modules need not + export it. The gen_fsm module provides a default + implementation of this function that returns the callback + module state data.</p> + </note> + <p>This function is called by a gen_fsm process when:</p> + <list typed="bulleted"> + <item>One + of <seealso marker="sys#get_status/1">sys:get_status/1,2</seealso> + is invoked to get the gen_fsm status. <c>Opt</c> is set to + the atom <c>normal</c> for this case.</item> + <item>The gen_fsm terminates abnormally and logs an + error. <c>Opt</c> is set to the atom <c>terminate</c> for + this case.</item> + </list> + <p>This function is useful for customising the form and + appearance of the gen_fsm status for these cases. A callback + module wishing to customise the <c>sys:get_status/1,2</c> + return value as well as how its status appears in + termination error logs exports an instance + of <c>format_status/2</c> that returns a term describing the + current status of the gen_fsm.</p> <p><c>PDict</c> is the current value of the gen_fsm's process dictionary.</p> <p><c>StateData</c> is the internal state data of the gen_fsm.</p> - <p>The function should return <c>Status</c>, a list of one or - more terms that customise the details of the current state - and status of the gen_fsm.</p> + <p>The function should return <c>Status</c>, a term that + customises the details of the current state and status of + the gen_fsm. There are no restrictions on the + form <c>Status</c> can take, but for + the <c>sys:get_status/1,2</c> case (when <c>Opt</c> + is <c>normal</c>), the recommended form for + the <c>Status</c> value is <c>[{data, [{"StateData", + Term}]}]</c> where <c>Term</c> provides relevant details of + the gen_fsm state data. Following this recommendation isn't + required, but doing so will make the callback module status + consistent with the rest of the <c>sys:get_status/1,2</c> + return value.</p> + <p>One use for this function is to return compact alternative + state data representations to avoid having large state terms + printed in logfiles.</p> </desc> </func> </funcs> @@ -770,4 +795,3 @@ gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4 <seealso marker="sys">sys(3)</seealso></p> </section> </erlref> - diff --git a/lib/stdlib/doc/src/gen_server.xml b/lib/stdlib/doc/src/gen_server.xml index 30c04d1d52..1045766e01 100644 --- a/lib/stdlib/doc/src/gen_server.xml +++ b/lib/stdlib/doc/src/gen_server.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2009</year> + <year>1996</year><year>2010</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -13,12 +13,12 @@ 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. - + </legalnotice> <title>gen_server</title> @@ -599,32 +599,57 @@ gen_server:abcast -----> Module:handle_cast/2 </desc> </func> <func> - <name>Module:format_status(normal, [PDict, State]) -> Status</name> + <name>Module:format_status(Opt, [PDict, State]) -> Status</name> <fsummary>Optional function for providing a term describing the current gen_server status.</fsummary> <type> + <v>Opt = normal | terminate</v> <v>PDict = [{Key, Value}]</v> <v>State = term()</v> - <v>Status = [term()]</v> + <v>Status = term()</v> </type> <desc> - <p><em>This callback is optional, so callback modules need not - export it. The gen_server module provides a default - implementation of this function that returns the callback - module state.</em></p> - <p>This function is called by a gen_server process when one + <note> + <p>This callback is optional, so callback modules need not + export it. The gen_server module provides a default + implementation of this function that returns the callback + module state.</p> + </note> + <p>This function is called by a gen_server process when:</p> + <list typed="bulleted"> + <item>One of <seealso marker="sys#get_status/1">sys:get_status/1,2</seealso> - is invoked to get the gen_server status. A callback module - wishing to customise the <c>sys:get_status/1,2</c> return - value exports an instance of <c>format_status/2</c> that - returns a term describing the current status of the - gen_server.</p> + is invoked to get the gen_server status. <c>Opt</c> is set + to the atom <c>normal</c> for this case.</item> + <item>The gen_server terminates abnormally and logs an + error. <c>Opt</c> is set to the atom <c>terminate</c> for this + case.</item> + </list> + <p>This function is useful for customising the form and + appearance of the gen_server status for these cases. A + callback module wishing to customise + the <c>sys:get_status/1,2</c> return value as well as how + its status appears in termination error logs exports an + instance of <c>format_status/2</c> that returns a term + describing the current status of the gen_server.</p> <p><c>PDict</c> is the current value of the gen_server's process dictionary.</p> <p><c>State</c> is the internal state of the gen_server.</p> - <p>The function should return <c>Status</c>, a list of one or - more terms that customise the details of the current state - and status of the gen_server.</p> + <p>The function should return <c>Status</c>, a term that + customises the details of the current state and status of + the gen_server. There are no restrictions on the + form <c>Status</c> can take, but for + the <c>sys:get_status/1,2</c> case (when <c>Opt</c> + is <c>normal</c>), the recommended form for + the <c>Status</c> value is <c>[{data, [{"State", + Term}]}]</c> where <c>Term</c> provides relevant details of + the gen_server state. Following this recommendation isn't + required, but doing so will make the callback module status + consistent with the rest of the <c>sys:get_status/1,2</c> + return value.</p> + <p>One use for this function is to return compact alternative + state representations to avoid having large state terms + printed in logfiles.</p> </desc> </func> </funcs> diff --git a/lib/stdlib/doc/src/io.xml b/lib/stdlib/doc/src/io.xml index efbb1fc078..af9c75d546 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>2010</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -43,7 +43,7 @@ <p>As of R13A, data supplied to the <seealso marker="#put_chars/2">put_chars</seealso> function should be in the - <c>chardata()</c> format described below. This means that programs + <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> @@ -64,72 +64,84 @@ </description> - <section> - <title>DATA TYPES</title> - <code type="none"> -io_device() - as returned by file:open/2, a process handling IO protocols</code> - - <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() + <datatypes> + <datatype> + <name name="device"/> + <desc> + <p>Either <c>standard_io</c>, <c>standard_error</c>, a + registered name, or a pid handling IO protocols (returned from + <seealso marker="file#open/2">file:open/2</seealso>).</p> + </desc> + </datatype> + <datatype> + <name name="opt_pair"/> + </datatype> + <datatype> + <name name="expand_fun"/> + </datatype> + <datatype> + <name name="encoding"/> + </datatype> + <datatype> + <name name="setopt"/> + </datatype> + <datatype> + <name name="format"/> + </datatype> + <datatype> + <name name="line"/> + </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> + </datatype> + </datatypes> -charlist() = [unicode_char() | unicode_binary() | charlist()] - a unicode_binary is allowed as the tail of the list</code> - </section> <funcs> <func> - <name>columns([IoDevice]) -> {ok,int()} | {error, enotsup}</name> + <name name="columns" arity="0"/> + <name name="columns" arity="1"/> <fsummary>Get the number of columns of a device</fsummary> - <type> - <v>IoDevice = io_device()</v> - </type> <desc> <p>Retrieves the number of columns of the - <c>IoDevice</c> (i.e. the width of a terminal). The function + <c><anno>IoDevice</anno></c> (i.e. the width of a terminal). The function only succeeds for terminal devices, for all other devices the function returns <c>{error, enotsup}</c></p> </desc> </func> <func> - <name>put_chars([IoDevice,] IoData) -> ok</name> + <name name="put_chars" arity="1"/> + <name name="put_chars" arity="2"/> <fsummary>Write a list of characters</fsummary> - <type> - <v>IoDevice = io_device()</v> - <v>IoData = chardata()</v> - </type> <desc> - <p>Writes the characters of <c>IoData</c> to the io_server() - (<c>IoDevice</c>).</p> + <p>Writes the characters of <c><anno>CharData</anno></c> to the io_server() + (<c><anno>IoDevice</anno></c>).</p> </desc> </func> <func> - <name>nl([IoDevice]) -> ok</name> + <name name="nl" arity="0"/> + <name name="nl" arity="1"/> <fsummary>Write a newline</fsummary> - <type> - <v>IoDevice = io_device()</v> - </type> <desc> - <p>Writes new line to the standard output (<c>IoDevice</c>).</p> + <p>Writes new line to the standard output (<c><anno>IoDevice</anno></c>).</p> </desc> </func> <func> - <name>get_chars([IoDevice,] Prompt, Count) -> Data | eof</name> + <name name="get_chars" arity="2"/> + <name name="get_chars" arity="3"/> <fsummary>Read a specified number of characters</fsummary> - <type> - <v>IoDevice = io_device()</v> - <v>Prompt = atom() | string()</v> - <v>Count = int()</v> - <v>Data = [ unicode_char() ] | unicode_binary()</v> - </type> <desc> - <p>Reads <c>Count</c> characters from standard input - (<c>IoDevice</c>), prompting it with <c>Prompt</c>. It + <p>Reads <c><anno>Count</anno></c> characters from standard input + (<c><anno>IoDevice</anno></c>), prompting it with <c><anno>Prompt</anno></c>. It returns:</p> <taglist> - <tag><c>Data</c></tag> + <tag><c><anno>Data</anno></c></tag> <item> <p>The input characters. If the device supports Unicode, the data may represent codepoints larger than 255 (the @@ -141,7 +153,7 @@ charlist() = [unicode_char() | unicode_binary() | charlist()] <item> <p>End of file was encountered.</p> </item> - <tag><c>{error,Reason}</c></tag> + <tag><c>{error,<anno>Reason</anno>}</c></tag> <item> <p>Other (rare) error condition, for instance <c>{error,estale}</c> if reading from an NFS file system.</p> @@ -150,18 +162,14 @@ charlist() = [unicode_char() | unicode_binary() | charlist()] </desc> </func> <func> - <name>get_line([IoDevice,] Prompt) -> Data | eof | {error,Reason}</name> + <name name="get_line" arity="1"/> + <name name="get_line" arity="2"/> <fsummary>Read a line</fsummary> - <type> - <v>IoDevice = io_device()</v> - <v>Prompt = atom() | string()</v> - <v>Data = [ unicode_char() ] | unicode_binary()</v> - </type> <desc> - <p>Reads a line from the standard input (<c>IoDevice</c>), - prompting it with <c>Prompt</c>. It returns:</p> + <p>Reads a line from the standard input (<c><anno>IoDevice</anno></c>), + prompting it with <c><anno>Prompt</anno></c>. It returns:</p> <taglist> - <tag><c>Data</c></tag> + <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, @@ -174,7 +182,7 @@ charlist() = [unicode_char() | unicode_binary() | charlist()] <item> <p>End of file was encountered.</p> </item> - <tag><c>{error,Reason}</c></tag> + <tag><c>{error,<anno>Reason</anno>}</c></tag> <item> <p>Other (rare) error condition, for instance <c>{error,estale}</c> if reading from an NFS file system.</p> @@ -183,14 +191,9 @@ charlist() = [unicode_char() | unicode_binary() | charlist()] </desc> </func> <func> - <name>getopts([IoDevice]) -> Opts</name> + <name name="getopts" arity="0"/> + <name name="getopts" arity="1"/> <fsummary>Get the supported options and values from an I/O-server</fsummary> - <type> - <v>IoDevice = io_device()</v> - <v>Opts = [Opt]</v> - <v> Opt = {atom(),Value}</v> - <v> Value = term()</v> - </type> <desc> <p>This function requests all available options and their current values for a specific io_device(). Example:</p> <pre> @@ -210,17 +213,11 @@ charlist() = [unicode_char() | unicode_binary() | charlist()] </desc> </func> <func> - <name>setopts([IoDevice,] Opts) -> ok | {error, Reason}</name> + <name name="setopts" arity="1"/> + <name name="setopts" arity="2"/> <fsummary>Set options</fsummary> - <type> - <v>IoDevice = io_device()</v> - <v>Opts = [Opt]</v> - <v> Opt = atom() | {atom(),Value}</v> - <v> Value = term()</v> - <v>Reason = term()</v> - </type> <desc> - <p>Set options for the io_device() (<c>IoDevice</c>).</p> + <p>Set options for the 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 @@ -229,17 +226,17 @@ charlist() = [unicode_char() | unicode_binary() | charlist()] <p>The options and values supported by the current OTP io_devices are:</p> <taglist> - <tag><c>binary, list or {binary, bool()}</c></tag> + <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> </item> - <tag><c>{echo, bool()}</c></tag> + <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> </item> - <tag><c>{expand_fun, fun()}</c></tag> + <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 @@ -281,33 +278,24 @@ charlist() = [unicode_char() | unicode_binary() | charlist()] </desc> </func> <func> - <name>write([IoDevice,] Term) -> ok</name> + <name name="write" arity="1"/> + <name name="write" arity="2"/> <fsummary>Write a term</fsummary> - <type> - <v>IoDevice = io_device()</v> - <v>Term = term()</v> - </type> <desc> - <p>Writes the term <c>Term</c> to the standard output - (<c>IoDevice</c>).</p> + <p>Writes the term <c><anno>Term</anno></c> to the standard output + (<c><anno>IoDevice</anno></c>).</p> </desc> </func> <func> - <name>read([IoDevice,] Prompt) -> Result</name> + <name name="read" arity="1"/> + <name name="read" arity="2"/> <fsummary>Read a term</fsummary> - <type> - <v>IoDevice = io_device()</v> - <v>Prompt = atom() | string()</v> - <v>Result = {ok, Term} | eof | {error, ErrorInfo}</v> - <v> Term = term()</v> - <v> ErrorInfo -- see section Error Information below</v> - </type> <desc> - <p>Reads a term <c>Term</c> from the standard input - (<c>IoDevice</c>), prompting it with <c>Prompt</c>. It + <p>Reads a term <c><anno>Term</anno></c> from the standard input + (<c><anno>IoDevice</anno></c>), prompting it with <c><anno>Prompt</anno></c>. It returns:</p> <taglist> - <tag><c>{ok, Term}</c></tag> + <tag><c>{ok, <anno>Term</anno>}</c></tag> <item> <p>The parsing was successful.</p> </item> @@ -315,7 +303,7 @@ charlist() = [unicode_char() | unicode_binary() | charlist()] <item> <p>End of file was encountered.</p> </item> - <tag><c>{error, ErrorInfo}</c></tag> + <tag><c>{error, <anno>ErrorInfo</anno>}</c></tag> <item> <p>The parsing failed.</p> </item> @@ -323,31 +311,22 @@ charlist() = [unicode_char() | unicode_binary() | charlist()] </desc> </func> <func> - <name>read(IoDevice, Prompt, StartLine) -> Result</name> + <name name="read" arity="3"/> <fsummary>Read a term</fsummary> - <type> - <v>IoDevice = io_device()</v> - <v>Prompt = atom() | string()</v> - <v>StartLine = int()</v> - <v>Result = {ok, Term, EndLine} | {eof, EndLine} | {error, ErrorInfo, EndLine}</v> - <v> Term = term()</v> - <v> EndLine = int()</v> - <v> ErrorInfo -- see section Error Information below</v> - </type> <desc> - <p>Reads a term <c>Term</c> from <c>IoDevice</c>, prompting it - with <c>Prompt</c>. Reading starts at line number - <c>StartLine</c>. It returns:</p> + <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> <taglist> - <tag><c>{ok, Term, EndLine}</c></tag> + <tag><c>{ok, Term, <anno>EndLine</anno>}</c></tag> <item> <p>The parsing was successful.</p> </item> - <tag><c>{eof, EndLine}</c></tag> + <tag><c>{eof, <anno>EndLine</anno>}</c></tag> <item> <p>End of file was encountered.</p> </item> - <tag><c>{error, ErrorInfo, EndLine}</c></tag> + <tag><c>{error, <anno>ErrorInfo</anno>, <anno>ErrorLine</anno>}</c></tag> <item> <p>The parsing failed.</p> </item> @@ -355,22 +334,19 @@ charlist() = [unicode_char() | unicode_binary() | charlist()] </desc> </func> <func> - <name>fwrite(Format) -></name> - <name>fwrite([IoDevice,] Format, Data) -> ok</name> - <name>format(Format) -></name> - <name>format([IoDevice,] Format, Data) -> ok</name> + <name name="fwrite" arity="1"/> + <name name="fwrite" arity="2"/> + <name name="fwrite" arity="3"/> + <name name="format" arity="1"/> + <name name="format" arity="2"/> + <name name="format" arity="3"/> <fsummary>Write formatted output</fsummary> - <type> - <v>IoDevice = io_device()</v> - <v>Format = atom() | string() | binary()</v> - <v>Data = [term()]</v> - </type> <desc> - <p>Writes the items in <c>Data</c> (<c>[]</c>) on the standard - output (<c>IoDevice</c>) in accordance with <c>Format</c>. - <c>Format</c> contains plain characters which are copied to + <p>Writes the items in <c><anno>Data</anno></c> (<c>[]</c>) on the standard + output (<c><anno>IoDevice</anno></c>) in accordance with <c><anno>Format</anno></c>. + <c><anno>Format</anno></c> contains plain characters which are copied to the output device, and control sequences for formatting, see - below. If <c>Format</c> is an atom or a binary, it is first + below. If <c><anno>Format</anno></c> is an atom or a binary, it is first converted to a list with the aid of <c>atom_to_list/1</c> or <c>binary_to_list/1</c>.</p> <pre> @@ -463,10 +439,10 @@ ok</pre> <item> <p>Prints the argument with the <c>string</c> syntax. The argument is, if no Unicode translation modifier is present, an - <seealso marker="erts:erlang#iolist_definition">I/O list</seealso>, a binary, or an atom. If the Unicode translation modifier ('t') is in effect, the argument is chardata(), meaning that binaries are in UTF-8. The characters - are printed without quotes. In this format, the printed - argument is truncated to the given precision and field - width.</p> + <seealso marker="erts:erlang#iolist_definition">I/O list</seealso>, 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 + 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> @@ -475,6 +451,8 @@ ok</pre> ok 4> <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> +|{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> @@ -660,22 +638,15 @@ ok </desc> </func> <func> - <name>fread([IoDevice,] Prompt, Format) -> Result</name> + <name name="fread" arity="2"/> + <name name="fread" arity="3"/> <fsummary>Read formatted input</fsummary> - <type> - <v>IoDevice = io_device()</v> - <v>Prompt = atom() | string()</v> - <v>Format = string()</v> - <v>Result = {ok, Terms} | eof | {error, What}</v> - <v> Terms = [term()]</v> - <v> What = term()</v> - </type> <desc> - <p>Reads characters from the standard input (<c>IoDevice</c>), - prompting it with <c>Prompt</c>. Interprets the characters in - accordance with <c>Format</c>. <c>Format</c> contains control + <p>Reads characters from the standard input (<c><anno>IoDevice</anno></c>), + prompting it with <c><anno>Prompt</anno></c>. Interprets the characters in + accordance with <c><anno>Format</anno></c>. <c><anno>Format</anno></c> contains control sequences which directs the interpretation of the input.</p> - <p><c>Format</c> may contain:</p> + <p><c><anno>Format</anno></c> may contain:</p> <list type="bulleted"> <item> <p>White space characters (SPACE, TAB and NEWLINE) which @@ -789,19 +760,19 @@ Prompt> <input><Character beyond latin1 range not printable in this medium> </taglist> <p>It returns:</p> <taglist> - <tag><c>{ok, Terms}</c></tag> + <tag><c>{ok, <anno>Terms</anno>}</c></tag> <item> - <p>The read was successful and <c>Terms</c> is the list + <p>The read was successful and <c><anno>Terms</anno></c> is the list of successfully matched and read items.</p> </item> <tag><c>eof</c></tag> <item> <p>End of file was encountered.</p> </item> - <tag><c>{error, What}</c></tag> + <tag><c>{error, <anno>What</anno>}</c></tag> <item> <p>The read operation failed and the parameter - <c>What</c> gives a hint about the error.</p> + <c><anno>What</anno></c> gives a hint about the error.</p> </item> </taglist> </item> @@ -820,31 +791,21 @@ enter><input>:</input> <input>alan</input> <input>:</input> <input>joe</in </desc> </func> <func> - <name>rows([IoDevice]) -> {ok,int()} | {error, enotsup}</name> + <name name="rows" arity="0"/> + <name name="rows" arity="1"/> <fsummary>Get the number of rows of a device</fsummary> - <type> - <v>IoDevice = io_device()</v> - </type> <desc> <p>Retrieves the number of rows of the - <c>IoDevice</c> (i.e. the height of a terminal). The function + <c><anno>IoDevice</anno></c> (i.e. the height of a terminal). The function only succeeds for terminal devices, for all other devices the function returns <c>{error, enotsup}</c></p> </desc> </func> <func> - <name>scan_erl_exprs(Prompt) -></name> - <name>scan_erl_exprs([IoDevice,] Prompt, StartLine) -> Result</name> + <name name="scan_erl_exprs" arity="1"/> + <name name="scan_erl_exprs" arity="2"/> + <name name="scan_erl_exprs" arity="3"/> <fsummary>Read and tokenize Erlang expressions</fsummary> - <type> - <v>IoDevice = io_device()</v> - <v>Prompt = atom() | string()</v> - <v>StartLine = int()</v> - <v>Result = {ok, Tokens, EndLine} | {eof, EndLine} | {error, ErrorInfo, EndLine}</v> - <v> Tokens -- see erl_scan(3)</v> - <v> EndLine = int()</v> - <v> ErrorInfo -- see section Error Information below</v> - </type> <desc> <p>Reads data from the standard input (<c>IoDevice</c>), prompting it with <c>Prompt</c>. Reading starts at line number @@ -860,7 +821,7 @@ enter><input>:</input> <input>alan</input> <input>:</input> <input>joe</in <item> <p>End of file was encountered.</p> </item> - <tag><c>{error, ErrorInfo, EndLine}</c></tag> + <tag><c>{error, ErrorInfo, ErrorLine}</c></tag> <item> <p>An error occurred.</p> </item> @@ -876,22 +837,14 @@ enter><input>1.0er.</input> </desc> </func> <func> - <name>scan_erl_form(Prompt) -></name> - <name>scan_erl_form([IoDevice,] Prompt, StartLine) -> Result</name> + <name name="scan_erl_form" arity="1"/> + <name name="scan_erl_form" arity="2"/> + <name name="scan_erl_form" arity="3"/> <fsummary>Read and tokenize an Erlang form</fsummary> - <type> - <v>IoDevice = io_device()</v> - <v>Prompt = atom() | string()</v> - <v>StartLine = int()</v> - <v>Result = {ok, Tokens, EndLine} | {eof, EndLine} | {error, ErrorInfo, EndLine}</v> - <v> Tokens -- see erl_scan(3)</v> - <v> EndLine = int()</v> - <v> ErrorInfo -- see section Error Information below</v> - </type> <desc> - <p>Reads data from the standard input (<c>IoDevice</c>), - prompting it with <c>Prompt</c>. Starts reading at line number - <c>StartLine</c> (1). The data is tokenized as if it were an + <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 Erlang form - one of the valid Erlang expressions in an Erlang source file - until a final <c>'.'</c> is reached. This last token is also returned. The return values are the @@ -899,26 +852,19 @@ enter><input>1.0er.</input> </desc> </func> <func> - <name>parse_erl_exprs(Prompt) -></name> - <name>parse_erl_exprs([IoDevice,] Prompt, StartLine) -> Result</name> + <name name="parse_erl_exprs" arity="1"/> + <name name="parse_erl_exprs" arity="2"/> + <name name="parse_erl_exprs" arity="3"/> + <type name="parse_ret"/> <fsummary>Read, tokenize and parse Erlang expressions</fsummary> - <type> - <v>IoDevice = io_device()</v> - <v>Prompt = atom() | string()</v> - <v>StartLine = int()</v> - <v>Result = {ok, Expr_list, EndLine} | {eof, EndLine} | {error, ErrorInfo, EndLine}</v> - <v> Expr_list -- see erl_parse(3)</v> - <v> EndLine = int()</v> - <v> ErrorInfo -- see section Error Information below</v> - </type> <desc> - <p>Reads data from the standard input (<c>IoDevice</c>), - prompting it with <c>Prompt</c>. Starts reading at line number - <c>StartLine</c> (1). The data is tokenized and parsed as if + <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> <taglist> - <tag><c>{ok, Expr_list, EndLine}</c></tag> + <tag><c>{ok, ExprList, EndLine}</c></tag> <item> <p>The parsing was successful.</p> </item> @@ -926,7 +872,7 @@ enter><input>1.0er.</input> <item> <p>End of file was encountered.</p> </item> - <tag><c>{error, ErrorInfo, EndLine}</c></tag> + <tag><c>{error, ErrorInfo, ErrorLine}</c></tag> <item> <p>An error occurred.</p> </item> @@ -942,22 +888,15 @@ enter><input>abc("hey".</input> </desc> </func> <func> - <name>parse_erl_form(Prompt) -></name> - <name>parse_erl_form([IoDevice,] Prompt, StartLine) -> Result</name> + <name name="parse_erl_form" arity="1"/> + <name name="parse_erl_form" arity="2"/> + <name name="parse_erl_form" arity="3"/> + <type name="parse_form_ret"/> <fsummary>Read, tokenize and parse an Erlang form</fsummary> - <type> - <v>IoDevice = io_device()</v> - <v>Prompt = atom() | string()</v> - <v>StartLine = int()</v> - <v>Result = {ok, AbsForm, EndLine} | {eof, EndLine} | {error, ErrorInfo, EndLine}</v> - <v> AbsForm -- see erl_parse(3)</v> - <v> EndLine = int()</v> - <v> ErrorInfo -- see section Error Information below</v> - </type> <desc> - <p>Reads data from the standard input (<c>IoDevice</c>), - prompting it with <c>Prompt</c>. Starts reading at line number - <c>StartLine</c> (1). The data is tokenized and parsed as if + <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 an Erlang form - one of the valid Erlang expressions in an Erlang source file - until a final '.' is reached. It returns:</p> @@ -970,7 +909,7 @@ enter><input>abc("hey".</input> <item> <p>End of file was encountered.</p> </item> - <tag><c>{error, ErrorInfo, EndLine}</c></tag> + <tag><c>{error, ErrorInfo, ErrorLine}</c></tag> <item> <p>An error occurred.</p> </item> diff --git a/lib/stdlib/doc/src/io_lib.xml b/lib/stdlib/doc/src/io_lib.xml index 399f968c5f..506c1792f1 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>2009</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -38,14 +38,22 @@ flattening deep lists.</p> </description> - <section> - <title>DATA TYPES</title> - <code type="none"> -chars() = [char() | chars()]</code> - </section> + <datatypes> + <datatype> + <name name="chars"/> + </datatype> + <datatype> + <name name="continuation"/> + <desc><p>A continuation as returned by <seealso marker="#fread/3"><c>fread/3</c></seealso>.</p> + </desc> + </datatype> + <datatype> + <name name="depth"/> + </datatype> + </datatypes> <funcs> <func> - <name>nl() -> chars()</name> + <name name="nl" arity="0"/> <fsummary>Write a newline</fsummary> <desc> <p>Returns a character list which represents a new line @@ -53,16 +61,12 @@ chars() = [char() | chars()]</code> </desc> </func> <func> - <name>write(Term) -></name> - <name>write(Term, Depth) -> chars()</name> + <name name="write" arity="1"/> + <name name="write" arity="2"/> <fsummary>Write a term</fsummary> - <type> - <v>Term = term()</v> - <v>Depth = int()</v> - </type> <desc> - <p>Returns a character list which represents <c>Term</c>. The - <c>Depth</c> (-1) argument controls the depth of the + <p>Returns a character list which represents <c><anno>Term</anno></c>. The + <c><anno>Depth</anno></c> (-1) argument controls the depth of the structures written. When the specified depth is reached, everything below this level is replaced by "...". For example:</p> @@ -74,36 +78,26 @@ chars() = [char() | chars()]</code> </desc> </func> <func> - <name>print(Term) -></name> - <name>print(Term, Column, LineLength, Depth) -> chars()</name> + <name name="print" arity="1"/> + <name name="print" arity="4"/> <fsummary>Pretty print a term</fsummary> - <type> - <v>Term = term()</v> - <v>Column = LineLenght = Depth = int()</v> - </type> <desc> <p>Also returns a list of characters which represents - <c>Term</c>, but breaks representations which are longer than + <c><anno>Term</anno></c>, but breaks representations which are longer than one line into many lines and indents each line sensibly. It also tries to detect and output lists of printable characters - as strings. <c>Column</c> is the starting column (1), - <c>LineLength</c> the maximum line length (80), and - <c>Depth</c> (-1) the maximum print depth.</p> + as strings. <c><anno>Column</anno></c> is the starting column (1), + <c><anno>LineLength</anno></c> the maximum line length (80), and + <c><anno>Depth</anno></c> (-1) the maximum print depth.</p> </desc> </func> <func> - <name>fwrite(Format, Data) -></name> - <name>format(Format, Data) -> chars() | UnicodeList</name> + <name name="fwrite" arity="2"/> + <name name="format" arity="2"/> <fsummary>Write formatted output</fsummary> - <type> - <v>Format = atom() | string() | binary()</v> - <v>Data = [term()]</v> - <v>UnicodeList = [Unicode]</v> - <v>Unicode = int() representing valid unicode codepoint</v> - </type> <desc> - <p>Returns a character list which represents <c>Data</c> - formatted in accordance with <c>Format</c>. See + <p>Returns a character list which represents <c><anno>Data</anno></c> + formatted in accordance with <c><anno>Format</anno></c>. See <seealso marker="io#fwrite/1">io:fwrite/1,2,3</seealso> for a detailed description of the available formatting options. A fault is generated if there is an error in the format string or @@ -119,42 +113,32 @@ chars() = [char() | chars()]</code> </desc> </func> <func> - <name>fread(Format, String) -> Result</name> + <name name="fread" arity="2"/> <fsummary>Read formatted input</fsummary> - <type> - <v>Format = String = string()</v> - <v>Result = {ok, InputList, LeftOverChars} | {more, RestFormat, Nchars, InputStack} | {error, What}</v> - <v> InputList = chars()</v> - <v> LeftOverChars = string()</v> - <v> RestFormat = string()</v> - <v> Nchars = int()</v> - <v> InputStack = chars()</v> - <v> What = term()</v> - </type> <desc> - <p>Tries to read <c>String</c> in accordance with the control - sequences in <c>Format</c>. See + <p>Tries to read <c><anno>String</anno></c> in accordance with the control + sequences in <c><anno>Format</anno></c>. See <seealso marker="io#fread/3">io:fread/3</seealso> for a detailed description of the available formatting options. It is - assumed that <c>String</c> contains whole lines. It returns:</p> + assumed that <c><anno>String</anno></c> contains whole lines. It returns:</p> <taglist> - <tag><c>{ok, InputList, LeftOverChars}</c></tag> + <tag><c>{ok, <anno>InputList</anno>, <anno>LeftOverChars</anno>}</c></tag> <item> - <p>The string was read. <c>InputList</c> is the list of + <p>The string was read. <c><anno>InputList</anno></c> is the list of successfully matched and read items, and - <c>LeftOverChars</c> are the input characters not used.</p> + <c><anno>LeftOverChars</anno></c> are the input characters not used.</p> </item> - <tag><c>{more, RestFormat, Nchars, InputStack}</c></tag> + <tag><c>{more, <anno>RestFormat</anno>, <anno>Nchars</anno>, <anno>InputStack</anno>}</c></tag> <item> <p>The string was read, but more input is needed in order - to complete the original format string. <c>RestFormat</c> - is the remaining format string, <c>NChars</c> the number - of characters scanned, and <c>InputStack</c> is the + to complete the original format string. <c><anno>RestFormat</anno></c> + is the remaining format string, <c><anno>Nchars</anno></c> the number + of characters scanned, and <c><anno>InputStack</anno></c> is the reversed list of inputs matched up to that point.</p> </item> - <tag><c>{error, What}</c></tag> + <tag><c>{error, <anno>What</anno>}</c></tag> <item> - <p>The read operation failed and the parameter <c>What</c> + <p>The read operation failed and the parameter <c><anno>What</anno></c> gives a hint about the error.</p> </item> </taglist> @@ -165,17 +149,8 @@ chars() = [char() | chars()]</code> </desc> </func> <func> - <name>fread(Continuation, String, Format) -> Return</name> + <name name="fread" arity="3"/> <fsummary>Re-entrant formatted reader</fsummary> - <type> - <v>Continuation = see below</v> - <v>String = Format = string()</v> - <v>Return = {done, Result, LeftOverChars} | {more, Continuation}</v> - <v> Result = {ok, InputList} | eof | {error, What}</v> - <v> InputList = chars()</v> - <v> What = term()()</v> - <v> LeftOverChars = string()</v> - </type> <desc> <p>This is the re-entrant formatted reader. The continuation of the first call to the functions must be <c>[]</c>. Refer to @@ -184,114 +159,92 @@ chars() = [char() | chars()]</code> re-entrant input scheme works.</p> <p>The function returns:</p> <taglist> - <tag><c>{done, Result, LeftOverChars}</c></tag> + <tag><c>{done, <anno>Result</anno>, <anno>LeftOverChars</anno>}</c></tag> <item> <p>The input is complete. The result is one of the following:</p> <taglist> - <tag><c>{ok, InputList}</c></tag> + <tag><c>{ok, <anno>InputList</anno>}</c></tag> <item> - <p>The string was read. <c>InputList</c> is the list of + <p>The string was read. <c><anno>InputList</anno></c> is the list of successfully matched and read items, and - <c>LeftOverChars</c> are the remaining characters.</p> + <c><anno>LeftOverChars</anno></c> are the remaining characters.</p> </item> <tag><c>eof</c></tag> <item> <p>End of file has been encountered. - <c>LeftOverChars</c> are the input characters not + <c><anno>LeftOverChars</anno></c> are the input characters not used.</p> </item> - <tag><c>{error, What}</c></tag> + <tag><c>{error, <anno>What</anno>}</c></tag> <item> - <p>An error occurred and the parameter <c>What</c> gives + <p>An error occurred and the parameter <c><anno>What</anno></c> gives a hint about the error.</p> </item> </taglist> </item> - <tag><c>{more, Continuation}</c></tag> + <tag><c>{more, <anno>Continuation</anno>}</c></tag> <item> <p>More data is required to build a term. - <c>Continuation</c> must be passed to <c>fread/3</c>, + <c><anno>Continuation</anno></c> must be passed to <c>fread/3</c>, when more data becomes available.</p> </item> </taglist> </desc> </func> <func> - <name>write_atom(Atom) -> chars()</name> + <name name="write_atom" arity="1"/> <fsummary>Write an atom</fsummary> - <type> - <v>Atom = atom()</v> - </type> <desc> <p>Returns the list of characters needed to print the atom - <c>Atom</c>.</p> + <c><anno>Atom</anno></c>.</p> </desc> </func> <func> - <name>write_string(String) -> chars()</name> + <name name="write_string" arity="1"/> <fsummary>Write a string</fsummary> - <type> - <v>String = string()</v> - </type> <desc> - <p>Returns the list of characters needed to print <c>String</c> + <p>Returns the list of characters needed to print <c><anno>String</anno></c> as a string.</p> </desc> </func> <func> - <name>write_char(Integer) -> chars()</name> + <name name="write_char" arity="1"/> <fsummary>Write a character</fsummary> - <type> - <v>Integer = int()</v> - </type> <desc> <p>Returns the list of characters needed to print a character constant in the ISO-latin-1 character set.</p> </desc> </func> <func> - <name>indentation(String, StartIndent) -> int()</name> + <name name="indentation" arity="2"/> <fsummary>Indentation after printing string</fsummary> - <type> - <v>String = string()</v> - <v>StartIndent = int()</v> - </type> <desc> - <p>Returns the indentation if <c>String</c> has been printed, - starting at <c>StartIndent</c>.</p> + <p>Returns the indentation if <c><anno>String</anno></c> has been printed, + starting at <c><anno>StartIndent</anno></c>.</p> </desc> </func> <func> - <name>char_list(Term) -> bool()</name> + <name name="char_list" arity="1"/> <fsummary>Test for a list of characters</fsummary> - <type> - <v>Term = term()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>Term</c> is a flat list of + <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> <func> - <name>deep_char_list(Term) -> bool()</name> + <name name="deep_char_list" arity="1"/> <fsummary>Test for a deep list of characters</fsummary> - <type> - <v>Term = term()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>Term</c> is a, possibly deep, list + <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>printable_list(Term) -> bool()</name> + <name name="printable_list" arity="1"/> <fsummary>Test for a list of printable ISO-latin-1 characters</fsummary> - <type> - <v>Term = term()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>Term</c> is a flat list of + <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> diff --git a/lib/stdlib/doc/src/io_protocol.xml b/lib/stdlib/doc/src/io_protocol.xml index 201787f7b5..3e8ab1affc 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>2009</year> + <year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -79,7 +79,7 @@ sends the reply to.</item> 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 ref() or something else to differentiate among +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 @@ -195,7 +195,7 @@ latin1, Module, Function, Args} respectively. </p> below).</p> <p>The function will be called with the data the io_server finds on - it's device, returning {done, Result, RestChars} when enough data is + 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 @@ -741,7 +741,7 @@ 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, -as it's final result return anything, so only functions actually +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 codepoints and the binaries should be in UTF-8, if the encoding tag diff --git a/lib/stdlib/doc/src/lib.xml b/lib/stdlib/doc/src/lib.xml index 046add48e8..19fb827cbf 100644 --- a/lib/stdlib/doc/src/lib.xml +++ b/lib/stdlib/doc/src/lib.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2009</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -37,27 +37,23 @@ </description> <funcs> <func> - <name>flush_receive() -> void()</name> + <name name="flush_receive" arity="0"/> <fsummary>Flush messages</fsummary> <desc> <p>Flushes the message buffer of the current process.</p> </desc> </func> <func> - <name>error_message(Format, Args) -> ok</name> + <name name="error_message" arity="2"/> <fsummary>Print error message</fsummary> - <type> - <v>Format = atom() | string() | binary()</v> - <v>Args = [term()]</v> - </type> <desc> - <p>Prints error message <c>Args</c> in accordance with - <c>Format</c>. Similar to <c>io:format/2</c>, see + <p>Prints error message <c><anno>Args</anno></c> in accordance with + <c><anno>Format</anno></c>. Similar to <c>io:format/2</c>, see <seealso marker="io#fwrite/1">io(3)</seealso>.</p> </desc> </func> <func> - <name>progname() -> atom()</name> + <name name="progname" arity="0"/> <fsummary>Return name of Erlang start script</fsummary> <desc> <p>Returns the name of the script that started the current @@ -65,37 +61,24 @@ </desc> </func> <func> - <name>nonl(String1) -> String2</name> + <name name="nonl" arity="1"/> <fsummary>Remove last newline</fsummary> - <type> - <v>String1 = String2 = string()</v> - </type> <desc> <p>Removes the last newline character, if any, in - <c>String1</c>.</p> + <c><anno>String1</anno></c>.</p> </desc> </func> <func> - <name>send(To, Msg)</name> + <name name="send" arity="2"/> <fsummary>Send a message</fsummary> - <type> - <v>To = pid() | Name | {Name,Node}</v> - <v> Name = Node = atom()</v> - <v>Msg = term()</v> - </type> <desc> <p>This function to makes it possible to send a message using the <c>apply/3</c> BIF.</p> </desc> </func> <func> - <name>sendw(To, Msg)</name> + <name name="sendw" arity="2"/> <fsummary>Send a message and wait for an answer</fsummary> - <type> - <v>To = pid() | Name | {Name,Node}</v> - <v> Name = Node = atom()</v> - <v>Msg = term()</v> - </type> <desc> <p>As <c>send/2</c>, but waits for an answer. It is implemented as follows:</p> diff --git a/lib/stdlib/doc/src/lists.xml b/lib/stdlib/doc/src/lists.xml index 855a7e0244..6f3ed7af98 100644 --- a/lib/stdlib/doc/src/lists.xml +++ b/lib/stdlib/doc/src/lists.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2010</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -48,7 +48,7 @@ <item><p>if x <c>F</c> y and y <c>F</c> x then x = y (<c>F</c> is antisymmetric);</p> </item> - <item><p>if x <c>F</c> y and and y <c>F</c> z then x <c>F</c> z + <item><p>if x <c>F</c> y and y <c>F</c> z then x <c>F</c> z (<c>F</c> is transitive);</p> </item> <item><p>x <c>F</c> y or y <c>F</c> x (<c>F</c> is total).</p> @@ -60,58 +60,41 @@ </description> <funcs> <func> - <name>all(Pred, List) -> bool()</name> + <name name="all" arity="2"/> <fsummary>Return true if all elements in the list satisfy<c>Pred</c></fsummary> - <type> - <v>Pred = fun(Elem) -> bool()</v> - <v> Elem = term()</v> - <v>List = [term()]</v> - </type> <desc> - <p>Returns <c>true</c> if <c>Pred(Elem)</c> returns - <c>true</c> for all elements <c>Elem</c> in <c>List</c>, + <p>Returns <c>true</c> if <c><anno>Pred</anno>(<anno>Elem</anno>)</c> returns + <c>true</c> for all elements <c><anno>Elem</anno></c> in <c><anno>List</anno></c>, otherwise <c>false</c>.</p> </desc> </func> <func> - <name>any(Pred, List) -> bool()</name> + <name name="any" arity="2"/> <fsummary>Return true if any of the elements in the list satisfies<c>Pred</c></fsummary> - <type> - <v>Pred = fun(Elem) -> bool()</v> - <v> Elem = term()</v> - <v>List = [term()]</v> - </type> <desc> - <p>Returns <c>true</c> if <c>Pred(Elem)</c> returns - <c>true</c> for at least one element <c>Elem</c> in - <c>List</c>.</p> + <p>Returns <c>true</c> if <c><anno>Pred</anno>(<anno>Elem</anno>)</c> returns + <c>true</c> for at least one element <c><anno>Elem</anno></c> in + <c><anno>List</anno></c>.</p> </desc> </func> <func> - <name>append(ListOfLists) -> List1</name> + <name name="append" arity="1"/> <fsummary>Append a list of lists</fsummary> - <type> - <v>ListOfLists = [List]</v> - <v>List = List1 = [term()]</v> - </type> <desc> <p>Returns a list in which all the sub-lists of - <c>ListOfLists</c> have been appended. For example:</p> + <c><anno>ListOfLists</anno></c> have been appended. For example:</p> <pre> > <input>lists:append([[1, 2, 3], [a, b], [4, 5, 6]]).</input> [1,2,3,a,b,4,5,6]</pre> </desc> </func> <func> - <name>append(List1, List2) -> List3</name> + <name name="append" arity="2"/> <fsummary>Append two lists</fsummary> - <type> - <v>List1 = List2 = List3 = [term()]</v> - </type> <desc> - <p>Returns a new list <c>List3</c> which is made from - the elements of <c>List1</c> followed by the elements of - <c>List2</c>. For example:</p> + <p>Returns a new list <c><anno>List3</anno></c> which is made from + the elements of <c><anno>List1</anno></c> followed by the elements of + <c><anno>List2</anno></c>. For example:</p> <pre> > <input>lists:append("abc", "def").</input> "abcdef"</pre> @@ -119,15 +102,11 @@ </desc> </func> <func> - <name>concat(Things) -> string()</name> + <name name="concat" arity="1"/> <fsummary>Concatenate a list of atoms</fsummary> - <type> - <v>Things = [Thing]</v> - <v> Thing = atom() | integer() | float() | string()</v> - </type> <desc> <p>Concatenates the text representation of the elements - of <c>Things</c>. The elements of <c>Things</c> can be atoms, + of <c><anno>Things</anno></c>. The elements of <c><anno>Things</anno></c> can be atoms, integers, floats or strings.</p> <pre> > <input>lists:concat([doc, '/', file, '.', 3]).</input> @@ -135,92 +114,64 @@ </desc> </func> <func> - <name>delete(Elem, List1) -> List2</name> + <name name="delete" arity="2"/> <fsummary>Delete an element from a list</fsummary> - <type> - <v>Elem = term()</v> - <v>List1 = List2 = [term()]</v> - </type> <desc> - <p>Returns a copy of <c>List1</c> where the first element - matching <c>Elem</c> is deleted, if there is such an + <p>Returns a copy of <c><anno>List1</anno></c> where the first element + matching <c><anno>Elem</anno></c> is deleted, if there is such an element.</p> </desc> </func> <func> - <name>dropwhile(Pred, List1) -> List2</name> + <name name="dropwhile" arity="2"/> <fsummary>Drop elements from a list while a predicate is true</fsummary> - <type> - <v>Pred = fun(Elem) -> bool()</v> - <v> Elem = term()</v> - <v>List1 = List2 = [term()]</v> - </type> <desc> - <p>Drops elements <c>Elem</c> from <c>List1</c> while - <c>Pred(Elem)</c> returns <c>true</c> and returns + <p>Drops elements <c><anno>Elem</anno></c> from <c><anno>List1</anno></c> while + <c><anno>Pred</anno>(<anno>Elem</anno>)</c> returns <c>true</c> and returns the remaining list.</p> </desc> </func> <func> - <name>duplicate(N, Elem) -> List</name> + <name name="duplicate" arity="2"/> <fsummary>Make N copies of element</fsummary> - <type> - <v>N = int()</v> - <v>Elem = term()</v> - <v>List = [term()]</v> - </type> <desc> - <p>Returns a list which contains N copies of the term - <c>Elem</c>. For example:</p> + <p>Returns a list which contains <c><anno>N</anno></c> copies of the term + <c><anno>Elem</anno></c>. For example:</p> <pre> > <input>lists:duplicate(5, xx).</input> [xx,xx,xx,xx,xx]</pre> </desc> </func> <func> - <name>filter(Pred, List1) -> List2</name> + <name name="filter" arity="2"/> <fsummary>Choose elements which satisfy a predicate</fsummary> - <type> - <v>Pred = fun(Elem) -> bool()</v> - <v> Elem = term()</v> - <v>List1 = List2 = [term()]</v> - </type> <desc> - <p><c>List2</c> is a list of all elements <c>Elem</c> in - <c>List1</c> for which <c>Pred(Elem)</c> returns + <p><c><anno>List2</anno></c> is a list of all elements <c><anno>Elem</anno></c> in + <c><anno>List1</anno></c> for which <c><anno>Pred</anno>(<anno>Elem</anno>)</c> returns <c>true</c>.</p> </desc> </func> <func> - <name>flatlength(DeepList) -> int()</name> + <name name="flatlength" arity="1"/> <fsummary>Length of flattened deep list</fsummary> - <type> - <v>DeepList = [term() | DeepList]</v> - </type> <desc> - <p>Equivalent to <c>length(flatten(DeepList))</c>, but more + <p>Equivalent to <c>length(flatten(<anno>DeepList</anno>))</c>, but more efficient.</p> </desc> </func> <func> - <name>flatmap(Fun, List1) -> List2</name> + <name name="flatmap" arity="2"/> <fsummary>Map and flatten in one pass</fsummary> - <type> - <v>Fun = fun(A) -> [B]</v> - <v>List1 = [A]</v> - <v>List2 = [B]</v> - <v> A = B = term()</v> - </type> <desc> - <p>Takes a function from <c>A</c>s to lists of <c>B</c>s, and a - list of <c>A</c>s (<c>List1</c>) and produces a list of - <c>B</c>s by applying the function to every element in - <c>List1</c> and appending the resulting lists.</p> + <p>Takes a function from <c><anno>A</anno></c>s to lists of <c><anno>B</anno></c>s, and a + list of <c><anno>A</anno></c>s (<c><anno>List1</anno></c>) and produces a list of + <c><anno>B</anno></c>s by applying the function to every element in + <c><anno>List1</anno></c> and appending the resulting lists.</p> <p>That is, <c>flatmap</c> behaves as if it had been defined as follows:</p> <code type="none"> flatmap(Fun, List1) -> - append(map(Fun, List1))</code> + append(map(Fun, List1)).</code> <p>Example:</p> <pre> > <input>lists:flatmap(fun(X)->[X,X] end, [a,b,c]).</input> @@ -228,43 +179,29 @@ flatmap(Fun, List1) -> </desc> </func> <func> - <name>flatten(DeepList) -> List</name> + <name name="flatten" arity="1"/> <fsummary>Flatten a deep list</fsummary> - <type> - <v>DeepList = [term() | DeepList]</v> - <v>List = [term()]</v> - </type> <desc> - <p>Returns a flattened version of <c>DeepList</c>.</p> + <p>Returns a flattened version of <c><anno>DeepList</anno></c>.</p> </desc> </func> <func> - <name>flatten(DeepList, Tail) -> List</name> + <name name="flatten" arity="2"/> <fsummary>Flatten a deep list</fsummary> - <type> - <v>DeepList = [term() | DeepList]</v> - <v>Tail = List = [term()]</v> - </type> <desc> - <p>Returns a flattened version of <c>DeepList</c> with the tail - <c>Tail</c> appended.</p> + <p>Returns a flattened version of <c><anno>DeepList</anno></c> with the tail + <c><anno>Tail</anno></c> appended.</p> </desc> </func> <func> - <name>foldl(Fun, Acc0, List) -> Acc1</name> + <name name="foldl" arity="3"/> <fsummary>Fold a function over a list</fsummary> - <type> - <v>Fun = fun(Elem, AccIn) -> AccOut</v> - <v> Elem = term()</v> - <v>Acc0 = Acc1 = AccIn = AccOut = term()</v> - <v>List = [term()]</v> - </type> <desc> - <p>Calls <c>Fun(Elem, AccIn)</c> on successive elements <c>A</c> - of <c>List</c>, starting with <c>AccIn == Acc0</c>. - <c>Fun/2</c> must return a new accumulator which is passed to + <p>Calls <c><anno>Fun</anno>(<anno>Elem</anno>, <anno>AccIn</anno>)</c> on successive elements <c>A</c> + of <c><anno>List</anno></c>, starting with <c><anno>AccIn</anno> == <anno>Acc0</anno></c>. + <c><anno>Fun</anno>/2</c> must return a new accumulator which is passed to the next call. The function returns the final value of - the accumulator. <c>Acc0</c> is returned if the list is empty. + the accumulator. <c><anno>Acc0</anno></c> is returned if the list is empty. For example:</p> <pre> > <input>lists:foldl(fun(X, Sum) -> X + Sum end, 0, [1,2,3,4,5]).</input> @@ -274,14 +211,8 @@ flatmap(Fun, List1) -> </desc> </func> <func> - <name>foldr(Fun, Acc0, List) -> Acc1</name> + <name name="foldr" arity="3"/> <fsummary>Fold a function over a list</fsummary> - <type> - <v>Fun = fun(Elem, AccIn) -> AccOut</v> - <v> Elem = term()</v> - <v>Acc0 = Acc1 = AccIn = AccOut = term()</v> - <v>List = [term()]</v> - </type> <desc> <p>Like <c>foldl/3</c>, but the list is traversed from right to left. For example:</p> @@ -297,33 +228,23 @@ flatmap(Fun, List1) -> </desc> </func> <func> - <name>foreach(Fun, List) -> void()</name> + <name name="foreach" arity="2"/> <fsummary>Apply a function to each element of a list</fsummary> - <type> - <v>Fun = fun(Elem) -> void()</v> - <v> Elem = term()</v> - <v>List = [term()]</v> - </type> <desc> - <p>Calls <c>Fun(Elem)</c> for each element <c>Elem</c> in - <c>List</c>. This function is used for its side effects and + <p>Calls <c><anno>Fun</anno>(<anno>Elem</anno>)</c> for each element <c><anno>Elem</anno></c> in + <c><anno>List</anno></c>. This function is used for its side effects and the evaluation order is defined to be the same as the order of the elements in the list.</p> </desc> </func> <func> - <name>keydelete(Key, N, TupleList1) -> TupleList2</name> + <name name="keydelete" arity="3"/> <fsummary>Delete an element from a list of tuples</fsummary> - <type> - <v>Key = term()</v> - <v>N = 1..tuple_size(Tuple)</v> - <v>TupleList1 = TupleList2 = [Tuple]</v> - <v> Tuple = tuple()</v> - </type> + <type_desc variable="N">1..tuple_size(Tuple)</type_desc> <desc> - <p>Returns a copy of <c>TupleList1</c> where the first - occurrence of a tuple whose <c>N</c>th element compares equal to - <c>Key</c> is deleted, if there is such a tuple.</p> + <p>Returns a copy of <c><anno>TupleList1</anno></c> where the first + occurrence of a tuple whose <c><anno>N</anno></c>th element compares equal to + <c><anno>Key</anno></c> is deleted, if there is such a tuple.</p> </desc> </func> <func> @@ -343,19 +264,14 @@ flatmap(Fun, List1) -> </desc> </func> <func> - <name>keymap(Fun, N, TupleList1) -> TupleList2</name> + <name name="keymap" arity="3"/> <fsummary>Map a function over a list of tuples</fsummary> - <type> - <v>Fun = fun(Term1) -> Term2</v> - <v> Term1 = Term2 = term()</v> - <v>N = 1..tuple_size(Tuple)</v> - <v>TupleList1 = TupleList2 = [tuple()]</v> - </type> + <type_desc variable="N">1..tuple_size(Tuple)</type_desc> <desc> <p>Returns a list of tuples where, for each tuple in - <c>TupleList1</c>, the <c>N</c>th element <c>Term1</c> of the tuple + <c><anno>TupleList1</anno></c>, the <c><anno>N</anno></c>th element <c><anno>Term1</anno></c> of the tuple has been replaced with the result of calling - <c>Fun(Term1)</c>.</p> + <c><anno>Fun</anno>(<anno>Term1</anno>)</c>.</p> <p>Examples:</p> <pre> > <input>Fun = fun(Atom) -> atom_to_list(Atom) end.</input> @@ -365,7 +281,7 @@ flatmap(Fun, List1) -> </desc> </func> <func> - <name>keymember(Key, N, TupleList) -> bool()</name> + <name>keymember(Key, N, TupleList) -> boolean()</name> <fsummary>Test for membership of a list of tuples</fsummary> <type> <v>Key = term()</v> @@ -380,37 +296,28 @@ flatmap(Fun, List1) -> </desc> </func> <func> - <name>keymerge(N, TupleList1, TupleList2) -> TupleList3</name> + <name name="keymerge" arity="3"/> <fsummary>Merge two key-sorted lists of tuples</fsummary> - <type> - <v>N = 1..tuple_size(Tuple)</v> - <v>TupleList1 = TupleList2 = TupleList3 = [Tuple]</v> - <v> Tuple = tuple()</v> - </type> + <type_desc variable="N">1..tuple_size(Tuple)</type_desc> <desc> - <p>Returns the sorted list formed by merging <c>TupleList1</c> - and <c>TupleList2</c>. The merge is performed on - the <c>N</c>th element of each tuple. Both <c>TupleList1</c> and - <c>TupleList2</c> must be key-sorted prior to evaluating this + <p>Returns the sorted list formed by merging <c><anno>TupleList1</anno></c> + and <c><anno>TupleList2</anno></c>. The merge is performed on + the <c><anno>N</anno></c>th element of each tuple. Both <c><anno>TupleList1</anno></c> and + <c><anno>TupleList2</anno></c> must be key-sorted prior to evaluating this function. When two tuples compare equal, the tuple from - <c>TupleList1</c> is picked before the tuple from - <c>TupleList2</c>.</p> + <c><anno>TupleList1</anno></c> is picked before the tuple from + <c><anno>TupleList2</anno></c>.</p> </desc> </func> <func> - <name>keyreplace(Key, N, TupleList1, NewTuple) -> TupleList2</name> + <name name="keyreplace" arity="4"/> <fsummary>Replace an element in a list of tuples</fsummary> - <type> - <v>Key = term()</v> - <v>N = 1..tuple_size(Tuple)</v> - <v>TupleList1 = TupleList2 = [Tuple]</v> - <v>NewTuple = Tuple = tuple()</v> - </type> + <type_desc variable="N">1..tuple_size(Tuple)</type_desc> <desc> - <p>Returns a copy of <c>TupleList1</c> where the first - occurrence of a <c>T</c> tuple whose <c>N</c>th element - compares equal to <c>Key</c> is replaced with - <c>NewTuple</c>, if there is such a tuple <c>T</c>.</p> + <p>Returns a copy of <c><anno>TupleList1</anno></c> where the first + occurrence of a <c>T</c> tuple whose <c><anno>N</anno></c>th element + compares equal to <c><anno>Key</anno></c> is replaced with + <c><anno>NewTuple</anno></c>, if there is such a tuple <c>T</c>.</p> </desc> </func> <func> @@ -433,97 +340,65 @@ flatmap(Fun, List1) -> </desc> </func> <func> - <name>keysort(N, TupleList1) -> TupleList2</name> + <name name="keysort" arity="2"/> <fsummary>Sort a list of tuples</fsummary> - <type> - <v>N = 1..tuple_size(Tuple)</v> - <v>TupleList1 = TupleList2 = [Tuple]</v> - <v> Tuple = tuple()</v> - </type> + <type_desc variable="N">1..tuple_size(Tuple)</type_desc> <desc> <p>Returns a list containing the sorted elements of the list - <c>TupleList1</c>. Sorting is performed on the <c>N</c>th - element of the tuples.</p> + <c><anno>TupleList1</anno></c>. Sorting is performed on the <c><anno>N</anno></c>th + element of the tuples. The sort is stable.</p> </desc> </func> <func> - <name>keystore(Key, N, TupleList1, NewTuple) -> TupleList2</name> + <name name="keystore" arity="4"/> <fsummary>Store an element in a list of tuples</fsummary> - <type> - <v>Key = term()</v> - <v>N = 1..tuple_size(Tuple)</v> - <v>TupleList1 = TupleList2 = [Tuple]</v> - <v>NewTuple = Tuple = tuple()</v> - </type> - <desc> - <p>Returns a copy of <c>TupleList1</c> where the first - occurrence of a tuple <c>T</c> whose <c>N</c>th element - compares equal to <c>Key</c> is replaced with - <c>NewTuple</c>, if there is such a tuple <c>T</c>. If there - is no such tuple <c>T</c> a copy of <c>TupleList1</c> where - [<c>NewTuple</c>] has been appended to the end is + <type_desc variable="N">1..tuple_size(Tuple)</type_desc> + <desc> + <p>Returns a copy of <c><anno>TupleList1</anno></c> where the first + occurrence of a tuple <c>T</c> whose <c><anno>N</anno></c>th element + compares equal to <c><anno>Key</anno></c> is replaced with + <c><anno>NewTuple</anno></c>, if there is such a tuple <c>T</c>. If there + is no such tuple <c>T</c> a copy of <c><anno>TupleList1</anno></c> where + [<c><anno>NewTuple</anno></c>] has been appended to the end is returned.</p> </desc> </func> <func> - <name>keytake(Key, N, TupleList1) -> {value, Tuple, TupleList2} - | false</name> + <name name="keytake" arity="3"/> <fsummary>Extract an element from a list of tuples</fsummary> - <type> - <v>Key = term()</v> - <v>N = 1..tuple_size(Tuple)</v> - <v>TupleList1 = TupleList2 = [Tuple]</v> - <v>Tuple = tuple()</v> - </type> + <type_desc variable="N">1..tuple_size(Tuple)</type_desc> <desc> - <p>Searches the list of tuples <c>TupleList1</c> for a tuple - whose <c>N</c>th element compares equal to <c>Key</c>. - Returns <c>{value, Tuple, TupleList2}</c> if such a tuple is - found, otherwise <c>false</c>. <c>TupleList2</c> is a copy - of <c>TupleList1</c> where the first occurrence of - <c>Tuple</c> has been removed.</p> + <p>Searches the list of tuples <c><anno>TupleList1</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>, <anno>TupleList2</anno>}</c> if such a tuple is + found, otherwise <c>false</c>. <c><anno>TupleList2</anno></c> is a copy + of <c><anno>TupleList1</anno></c> where the first occurrence of + <c><anno>Tuple</anno></c> has been removed.</p> </desc> </func> <func> - <name>last(List) -> Last</name> + <name name="last" arity="1"/> <fsummary>Return last element in a list</fsummary> - <type> - <v>List = [term()], length(List) > 0</v> - <v>Last = term()</v> - </type> <desc> - <p>Returns the last element in <c>List</c>.</p> + <p>Returns the last element in <c><anno>List</anno></c>.</p> </desc> </func> <func> - <name>map(Fun, List1) -> List2</name> + <name name="map" arity="2"/> <fsummary>Map a function over a list</fsummary> - <type> - <v>Fun = fun(A) -> B</v> - <v>List1 = [A]</v> - <v>List2 = [B]</v> - <v> A = B = term()</v> - </type> <desc> - <p>Takes a function from <c>A</c>s to <c>B</c>s, and a list of - <c>A</c>s and produces a list of <c>B</c>s by applying + <p>Takes a function from <c><anno>A</anno></c>s to <c><anno>B</anno></c>s, and a list of + <c><anno>A</anno></c>s and produces a list of <c><anno>B</anno></c>s by applying the function to every element in the list. This function is used to obtain the return values. The evaluation order is implementation dependent.</p> </desc> </func> <func> - <name>mapfoldl(Fun, Acc0, List1) -> {List2, Acc1}</name> + <name name="mapfoldl" arity="3"/> <fsummary>Map and fold in one pass</fsummary> - <type> - <v>Fun = fun(A, AccIn) -> {B, AccOut}</v> - <v>Acc0 = Acc1 = AccIn = AccOut = term()</v> - <v>List1 = [A]</v> - <v>List2 = [B]</v> - <v> A = B = term()</v> - </type> <desc> - <p><c>mapfold</c> combines the operations of <c>map/2</c> and + <p><c>mapfoldl</c> combines the operations of <c>map/2</c> and <c>foldl/3</c> into one pass. An example, summing the elements in a list and double them at the same time:</p> <pre> @@ -533,35 +408,24 @@ flatmap(Fun, List1) -> </desc> </func> <func> - <name>mapfoldr(Fun, Acc0, List1) -> {List2, Acc1}</name> + <name name="mapfoldr" arity="3"/> <fsummary>Map and fold in one pass</fsummary> - <type> - <v>Fun = fun(A, AccIn) -> {B, AccOut}</v> - <v>Acc0 = Acc1 = AccIn = AccOut = term()</v> - <v>List1 = [A]</v> - <v>List2 = [B]</v> - <v> A = B = term()</v> - </type> <desc> - <p><c>mapfold</c> combines the operations of <c>map/2</c> and + <p><c>mapfoldr</c> combines the operations of <c>map/2</c> and <c>foldr/3</c> into one pass.</p> </desc> </func> <func> - <name>max(List) -> Max</name> + <name name="max" arity="1"/> <fsummary>Return maximum element of a list</fsummary> - <type> - <v>List = [term()], length(List) > 0</v> - <v>Max = term()</v> - </type> <desc> - <p>Returns the first element of <c>List</c> that compares + <p>Returns the first element of <c><anno>List</anno></c> that compares greater than or equal to all other elements of - <c>List</c>.</p> + <c><anno>List</anno></c>.</p> </desc> </func> <func> - <name>member(Elem, List) -> bool()</name> + <name>member(Elem, List) -> boolean()</name> <fsummary>Test for membership of a list</fsummary> <type> <v>Elem = term()</v> @@ -573,112 +437,84 @@ flatmap(Fun, List1) -> </desc> </func> <func> - <name>merge(ListOfLists) -> List1</name> + <name name="merge" arity="1"/> <fsummary>Merge a list of sorted lists</fsummary> - <type> - <v>ListOfLists = [List]</v> - <v>List = List1 = [term()]</v> - </type> <desc> <p>Returns the sorted list formed by merging all the sub-lists - of <c>ListOfLists</c>. All sub-lists must be sorted prior to + of <c><anno>ListOfLists</anno></c>. All sub-lists must be sorted prior to evaluating this function. When two elements compare equal, the element from the sub-list with the lowest position in - <c>ListOfLists</c> is picked before the other element.</p> + <c><anno>ListOfLists</anno></c> is picked before the other element.</p> </desc> </func> <func> - <name>merge(List1, List2) -> List3</name> + <name name="merge" arity="2"/> <fsummary>Merge two sorted lists</fsummary> - <type> - <v>List1 = List2 = List3 = [term()]</v> - </type> <desc> - <p>Returns the sorted list formed by merging <c>List1</c> and - <c>List2</c>. Both <c>List1</c> and <c>List2</c> must be + <p>Returns the sorted list formed by merging <c><anno>List1</anno></c> and + <c><anno>List2</anno></c>. Both <c><anno>List1</anno></c> and <c><anno>List2</anno></c> must be sorted prior to evaluating this function. When two elements - compare equal, the element from <c>List1</c> is picked - before the element from <c>List2</c>.</p> + compare equal, the element from <c><anno>List1</anno></c> is picked + before the element from <c><anno>List2</anno></c>.</p> </desc> </func> <func> - <name>merge(Fun, List1, List2) -> List3</name> + <name name="merge" arity="3"/> <fsummary>Merge two sorted list</fsummary> - <type> - <v>Fun = fun(A, B) -> bool()</v> - <v>List1 = [A]</v> - <v>List2 = [B]</v> - <v>List3 = [A | B]</v> - <v> A = B = term()</v> - </type> <desc> - <p>Returns the sorted list formed by merging <c>List1</c> and - <c>List2</c>. Both <c>List1</c> and <c>List2</c> must be + <p>Returns the sorted list formed by merging <c><anno>List1</anno></c> and + <c><anno>List2</anno></c>. Both <c><anno>List1</anno></c> and <c><anno>List2</anno></c> must be sorted according to the <seealso marker="#ordering_function">ordering function</seealso> - <c>Fun</c> prior to evaluating this function. <c>Fun(A, - B)</c> should return <c>true</c> if <c>A</c> compares less - than or equal to <c>B</c> in the ordering, <c>false</c> + <c><anno>Fun</anno></c> prior to evaluating this function. <c><anno>Fun</anno>(<anno>A</anno>, + <anno>B</anno>)</c> should return <c>true</c> if <c><anno>A</anno></c> compares less + than or equal to <c><anno>B</anno></c> in the ordering, <c>false</c> otherwise. When two elements compare equal, the element from - <c>List1</c> is picked before the element from - <c>List2</c>.</p> + <c><anno>List1</anno></c> is picked before the element from + <c><anno>List2</anno></c>.</p> </desc> </func> <func> - <name>merge3(List1, List2, List3) -> List4</name> + <name name="merge3" arity="3"/> <fsummary>Merge three sorted lists</fsummary> - <type> - <v>List1 = List2 = List3 = List4 = [term()]</v> - </type> <desc> - <p>Returns the sorted list formed by merging <c>List1</c>, - <c>List2</c> and <c>List3</c>. All of <c>List1</c>, - <c>List2</c> and <c>List3</c> must be sorted prior to + <p>Returns the sorted list formed by merging <c><anno>List1</anno></c>, + <c><anno>List2</anno></c> and <c><anno>List3</anno></c>. All of <c><anno>List1</anno></c>, + <c><anno>List2</anno></c> and <c><anno>List3</anno></c> must be sorted prior to evaluating this function. When two elements compare equal, - the element from <c>List1</c>, if there is such an element, + the element from <c><anno>List1</anno></c>, if there is such an element, is picked before the other element, otherwise the element - from <c>List2</c> is picked before the element from - <c>List3</c>.</p> + from <c><anno>List2</anno></c> is picked before the element from + <c><anno>List3</anno></c>.</p> </desc> </func> <func> - <name>min(List) -> Min</name> + <name name="min" arity="1"/> <fsummary>Return minimum element of a list</fsummary> - <type> - <v>List = [term()], length(List) > 0</v> - <v>Min = term()</v> - </type> <desc> - <p>Returns the first element of <c>List</c> that compares + <p>Returns the first element of <c><anno>List</anno></c> that compares less than or equal to all other elements of - <c>List</c>.</p> + <c><anno>List</anno></c>.</p> </desc> </func> <func> - <name>nth(N, List) -> Elem</name> + <name name="nth" arity="2"/> <fsummary>Return the Nth element of a list</fsummary> - <type> - <v>N = 1..length(List)</v> - <v>List = [term()]</v> - <v>Elem = term()</v> - </type> + <type_desc variable="N">1..length(List)</type_desc> <desc> - <p>Returns the <c>N</c>th element of <c>List</c>. For example:</p> + <p>Returns the <c><anno>N</anno></c>th element of <c><anno>List</anno></c>. For example:</p> <pre> > <input>lists:nth(3, [a, b, c, d, e]).</input> c</pre> </desc> </func> <func> - <name>nthtail(N, List1) -> Tail</name> + <name name="nthtail" arity="2"/> <fsummary>Return the Nth tail of a list</fsummary> - <type> - <v>N = 0..length(List1)</v> - <v>List1 = Tail = [term()]</v> - </type> + <type_desc variable="N">0..length(List)</type_desc> <desc> - <p>Returns the <c>N</c>th tail of <c>List</c>, that is, the sublist of - <c>List</c> starting at <c>N+1</c> and continuing up to + <p>Returns the <c><anno>N</anno></c>th tail of <c><anno>List</anno></c>, that is, the sublist of + <c><anno>List</anno></c> starting at <c><anno>N</anno>+1</c> and continuing up to the end of the list. For example:</p> <pre> > <input>lists:nthtail(3, [a, b, c, d, e]).</input> @@ -692,18 +528,13 @@ c</pre> </desc> </func> <func> - <name>partition(Pred, List) -> {Satisfying, NonSatisfying}</name> + <name name="partition" arity="2"/> <fsummary>Partition a list into two lists based on a predicate</fsummary> - <type> - <v>Pred = fun(Elem) -> bool()</v> - <v> Elem = term()</v> - <v>List = Satisfying = NonSatisfying = [term()]</v> - </type> <desc> - <p>Partitions <c>List</c> into two lists, where the first list - contains all elements for which <c>Pred(Elem)</c> returns + <p>Partitions <c><anno>List</anno></c> into two lists, where the first list + contains all elements for which <c><anno>Pred</anno>(<anno>Elem</anno>)</c> returns <c>true</c>, and the second list contains all elements for - which <c>Pred(Elem)</c> returns <c>false</c>.</p> + which <c><anno>Pred</anno>(<anno>Elem</anno>)</c> returns <c>false</c>.</p> <p>Examples:</p> <pre> > <input>lists:partition(fun(A) -> A rem 2 == 1 end, [1,2,3,4,5,6,7]).</input> @@ -715,24 +546,18 @@ c</pre> </desc> </func> <func> - <name>prefix(List1, List2) -> bool()</name> + <name name="prefix" arity="2"/> <fsummary>Test for list prefix</fsummary> - <type> - <v>List1 = List2 = [term()]</v> - </type> <desc> - <p>Returns <c>true</c> if <c>List1</c> is a prefix of - <c>List2</c>, otherwise <c>false</c>.</p> + <p>Returns <c>true</c> if <c><anno>List1</anno></c> is a prefix of + <c><anno>List2</anno></c>, otherwise <c>false</c>.</p> </desc> </func> <func> - <name>reverse(List1) -> List2</name> + <name name="reverse" arity="1"/> <fsummary>Reverse a list</fsummary> - <type> - <v>List1 = List2 = [term()]</v> - </type> <desc> - <p>Returns a list with the top level elements in <c>List1</c> + <p>Returns a list with the elements in <c><anno>List1</anno></c> in reverse order.</p> </desc> </func> @@ -743,7 +568,7 @@ c</pre> <v>List1 = Tail = List2 = [term()]</v> </type> <desc> - <p>Returns a list with the top level elements in <c>List1</c> + <p>Returns a list with the elements in <c>List1</c> in reverse order, with the tail <c>Tail</c> appended. For example:</p> <pre> @@ -752,22 +577,18 @@ c</pre> </desc> </func> <func> - <name>seq(From, To) -> Seq</name> - <name>seq(From, To, Incr) -> Seq</name> + <name name="seq" arity="2"/> + <name name="seq" arity="3"/> <fsummary>Generate a sequence of integers</fsummary> - <type> - <v>From = To = Incr = int()</v> - <v>Seq = [int()]</v> - </type> <desc> - <p>Returns a sequence of integers which starts with <c>From</c> - and contains the successive results of adding <c>Incr</c> to - the previous element, until <c>To</c> has been reached or - passed (in the latter case, <c>To</c> is not an element of - the sequence). <c>Incr</c> defaults to 1.</p> - <p>Failure: If <c><![CDATA[To<From-Incr]]></c> and <c>Incr</c> - is positive, or if <c>To>From-Incr</c> and <c>Incr</c> is - negative, or if <c>Incr==0</c> and <c>From/=To</c>.</p> + <p>Returns a sequence of integers which starts with <c><anno>From</anno></c> + and contains the successive results of adding <c><anno>Incr</anno></c> to + the previous element, until <c><anno>To</anno></c> has been reached or + passed (in the latter case, <c><anno>To</anno></c> is not an element of + the sequence). <c><anno>Incr</anno></c> defaults to 1.</p> + <p>Failure: If <c><anno>To</anno><<anno>From</anno>-<anno>Incr</anno></c> and <c><anno>Incr</anno></c> + is positive, or if <c><anno>To</anno>><anno>From</anno>-<anno>Incr</anno></c> and <c><anno>Incr</anno></c> is + negative, or if <c><anno>Incr</anno>==0</c> and <c><anno>From</anno>/=<anno>To</anno></c>.</p> <p>The following equalities hold for all sequences:</p> <code type="none"> length(lists:seq(From, To)) == To-From+1 @@ -787,60 +608,44 @@ length(lists:seq(From, To, Incr)) == (To-From+Incr) div Incr</code> </desc> </func> <func> - <name>sort(List1) -> List2</name> + <name name="sort" arity="1"/> <fsummary>Sort a list</fsummary> - <type> - <v>List1 = List2 = [term()]</v> - </type> <desc> <p>Returns a list containing the sorted elements of - <c>List1</c>.</p> + <c><anno>List1</anno></c>.</p> </desc> </func> <func> - <name>sort(Fun, List1) -> List2</name> + <name name="sort" arity="2"/> <fsummary>Sort a list</fsummary> - <type> - <v>Fun = fun(Elem1, Elem2) -> bool()</v> - <v> Elem1 = Elem2 = term()</v> - <v>List1 = List2 = [term()]</v> - </type> <desc> <p>Returns a list containing the sorted elements of - <c>List1</c>, according to the <seealso + <c><anno>List1</anno></c>, according to the <seealso marker="#ordering_function">ordering function</seealso> - <c>Fun</c>. <c>Fun(A, B)</c> should return <c>true</c> if - <c>A</c> compares less than or equal to <c>B</c> in the + <c><anno>Fun</anno></c>. <c><anno>Fun</anno>(<anno>A</anno>, <anno>B</anno>)</c> should return <c>true</c> if + <c><anno>A</anno></c> compares less than or equal to <c><anno>B</anno></c> in the ordering, <c>false</c> otherwise.</p> </desc> </func> <func> - <name>split(N, List1) -> {List2, List3}</name> + <name name="split" arity="2"/> <fsummary>Split a list into two lists</fsummary> - <type> - <v>N = 0..length(List1)</v> - <v>List1 = List2 = List3 = [term()]</v> - </type> + <type_desc variable="N">0..length(List1)</type_desc> <desc> - <p>Splits <c>List1</c> into <c>List2</c> and <c>List3</c>. - <c>List2</c> contains the first <c>N</c> elements and - <c>List3</c> the rest of the elements (the <c>N</c>th tail).</p> + <p>Splits <c><anno>List1</anno></c> into <c><anno>List2</anno></c> and <c><anno>List3</anno></c>. + <c><anno>List2</anno></c> contains the first <c><anno>N</anno></c> elements and + <c><anno>List3</anno></c> the rest of the elements (the <c><anno>N</anno></c>th tail).</p> </desc> </func> <func> - <name>splitwith(Pred, List) -> {List1, List2}</name> + <name name="splitwith" arity="2"/> <fsummary>Split a list into two lists based on a predicate</fsummary> - <type> - <v>Pred = fun(Elem) -> bool()</v> - <v> Elem = term()</v> - <v>List = List1 = List2 = [term()]</v> - </type> <desc> - <p>Partitions <c>List</c> into two lists according to - <c>Pred</c>. <c>splitwith/2</c> behaves as if it is defined + <p>Partitions <c><anno>List</anno></c> into two lists according to + <c><anno>Pred</anno></c>. <c>splitwith/2</c> behaves as if it is defined as follows:</p> <code type="none"> -splitwith(Pred, List) -> +splitwith(Pred, List) -> {takewhile(Pred, List), dropwhile(Pred, List)}.</code> <p>Examples:</p> <pre> @@ -853,31 +658,23 @@ splitwith(Pred, List) -> </desc> </func> <func> - <name>sublist(List1, Len) -> List2</name> + <name name="sublist" arity="2"/> <fsummary>Return a sub-list of a certain length, starting at the first position</fsummary> - <type> - <v>List1 = List2 = [term()]</v> - <v>Len = int()</v> - </type> <desc> - <p>Returns the sub-list of <c>List1</c> starting at position 1 - and with (max) <c>Len</c> elements. It is not an error for - <c>Len</c> to exceed the length of the list -- in that case + <p>Returns the sub-list of <c><anno>List1</anno></c> starting at position 1 + and with (max) <c><anno>Len</anno></c> elements. It is not an error for + <c><anno>Len</anno></c> to exceed the length of the list -- in that case the whole list is returned.</p> </desc> </func> <func> - <name>sublist(List1, Start, Len) -> List2</name> + <name name="sublist" arity="3"/> <fsummary>Return a sub-list starting at a given position and with a given number of elements</fsummary> - <type> - <v>List1 = List2 = [term()]</v> - <v>Start = 1..(length(List1)+1)</v> - <v>Len = int()</v> - </type> + <type_desc variable="Start">1..(length(List1)+1)</type_desc> <desc> - <p>Returns the sub-list of <c>List1</c> starting at <c>Start</c> - and with (max) <c>Len</c> elements. It is not an error for - <c>Start+Len</c> to exceed the length of the list.</p> + <p>Returns the sub-list of <c><anno>List1</anno></c> starting at <c><anno>Start</anno></c> + and with (max) <c><anno>Len</anno></c> elements. It is not an error for + <c><anno>Start</anno>+<anno>Len</anno></c> to exceed the length of the list.</p> <pre> > <input>lists:sublist([1,2,3,4], 2, 2).</input> [2,3] @@ -888,15 +685,12 @@ splitwith(Pred, List) -> </desc> </func> <func> - <name>subtract(List1, List2) -> List3</name> + <name name="subtract" arity="2"/> <fsummary>Subtract the element in one list from another list</fsummary> - <type> - <v>List1 = List2 = List3 = [term()]</v> - </type> <desc> - <p>Returns a new list <c>List3</c> which is a copy of - <c>List1</c>, subjected to the following procedure: for each - element in <c>List2</c>, its first occurrence in <c>List1</c> + <p>Returns a new list <c><anno>List3</anno></c> which is a copy of + <c><anno>List1</anno></c>, subjected to the following procedure: for each + element in <c><anno>List2</anno></c>, its first occurrence in <c><anno>List1</anno></c> is deleted. For example:</p> <pre> > <input>lists:subtract("123212", "212").</input> @@ -911,151 +705,112 @@ splitwith(Pred, List) -> </desc> </func> <func> - <name>suffix(List1, List2) -> bool()</name> + <name name="suffix" arity="2"/> <fsummary>Test for list suffix</fsummary> <desc> - <p>Returns <c>true</c> if <c>List1</c> is a suffix of - <c>List2</c>, otherwise <c>false</c>.</p> + <p>Returns <c>true</c> if <c><anno>List1</anno></c> is a suffix of + <c><anno>List2</anno></c>, otherwise <c>false</c>.</p> </desc> </func> <func> - <name>sum(List) -> number()</name> + <name name="sum" arity="1"/> <fsummary>Return sum of elements in a list</fsummary> - <type> - <v>List = [number()]</v> - </type> <desc> - <p>Returns the sum of the elements in <c>List</c>.</p> + <p>Returns the sum of the elements in <c><anno>List</anno></c>.</p> </desc> </func> <func> - <name>takewhile(Pred, List1) -> List2</name> + <name name="takewhile" arity="2"/> <fsummary>Take elements from a list while a predicate is true</fsummary> - <type> - <v>Pred = fun(Elem) -> bool()</v> - <v> Elem = term()</v> - <v>List1 = List2 = [term()]</v> - </type> <desc> - <p>Takes elements <c>Elem</c> from <c>List1</c> while - <c>Pred(Elem)</c> returns <c>true</c>, that is, + <p>Takes elements <c><anno>Elem</anno></c> from <c><anno>List1</anno></c> while + <c><anno>Pred</anno>(<anno>Elem</anno>)</c> returns <c>true</c>, that is, the function returns the longest prefix of the list for which all elements satisfy the predicate.</p> </desc> </func> <func> - <name>ukeymerge(N, TupleList1, TupleList2) -> TupleList3</name> + <name name="ukeymerge" arity="3"/> <fsummary>Merge two key-sorted lists of tuples, removing duplicates</fsummary> - <type> - <v>N = 1..tuple_size(Tuple)</v> - <v>TupleList1 = TupleList2 = TupleList3 = [Tuple]</v> - <v> Tuple = tuple()</v> - </type> + <type_desc variable="N">1..tuple_size(Tuple)</type_desc> <desc> - <p>Returns the sorted list formed by merging <c>TupleList1</c> - and <c>TupleList2</c>. The merge is performed on the - <c>N</c>th element of each tuple. Both <c>TupleList1</c> and - <c>TupleList2</c> must be key-sorted without duplicates + <p>Returns the sorted list formed by merging <c><anno>TupleList1</anno></c> + and <c><anno>TupleList2</anno></c>. The merge is performed on the + <c><anno>N</anno></c>th element of each tuple. Both <c><anno>TupleList1</anno></c> and + <c><anno>TupleList2</anno></c> must be key-sorted without duplicates prior to evaluating this function. When two tuples compare - equal, the tuple from <c>TupleList1</c> is picked and the - one from <c>TupleList2</c> deleted.</p> + equal, the tuple from <c><anno>TupleList1</anno></c> is picked and the + one from <c><anno>TupleList2</anno></c> deleted.</p> </desc> </func> <func> - <name>ukeysort(N, TupleList1) -> TupleList2</name> + <name name="ukeysort" arity="2"/> <fsummary>Sort a list of tuples, removing duplicates</fsummary> - <type> - <v>N = 1..tuple_size(Tuple)</v> - <v>TupleList1 = TupleList2 = [Tuple]</v> - <v> Tuple = tuple()</v> - </type> + <type_desc variable="N">1..tuple_size(Tuple)</type_desc> <desc> <p>Returns a list containing the sorted elements of the list - <c>TupleList1</c> where all but the first tuple of the + <c><anno>TupleList1</anno></c> where all but the first tuple of the tuples comparing equal have been deleted. Sorting is - performed on the <c>N</c>th element of the tuples.</p> + performed on the <c><anno>N</anno></c>th element of the tuples.</p> </desc> </func> <func> - <name>umerge(ListOfLists) -> List1</name> + <name name="umerge" arity="1"/> <fsummary>Merge a list of sorted lists, removing duplicates</fsummary> - <type> - <v>ListOfLists = [List]</v> - <v>List = List1 = [term()]</v> - </type> <desc> <p>Returns the sorted list formed by merging all the sub-lists - of <c>ListOfLists</c>. All sub-lists must be sorted and + of <c><anno>ListOfLists</anno></c>. All sub-lists must be sorted and contain no duplicates prior to evaluating this function. When two elements compare equal, the element from the - sub-list with the lowest position in <c>ListOfLists</c> is + sub-list with the lowest position in <c><anno>ListOfLists</anno></c> is picked and the other one deleted.</p> </desc> </func> <func> - <name>umerge(List1, List2) -> List3</name> + <name name="umerge" arity="2"/> <fsummary>Merge two sorted lists, removing duplicates</fsummary> - <type> - <v>List1 = List2 = List3 = [term()]</v> - </type> <desc> - <p>Returns the sorted list formed by merging <c>List1</c> and - <c>List2</c>. Both <c>List1</c> and <c>List2</c> must be + <p>Returns the sorted list formed by merging <c><anno>List1</anno></c> and + <c><anno>List2</anno></c>. Both <c><anno>List1</anno></c> and <c><anno>List2</anno></c> must be sorted and contain no duplicates prior to evaluating this function. When two elements compare equal, the element from - <c>List1</c> is picked and the one from <c>List2</c> + <c><anno>List1</anno></c> is picked and the one from <c><anno>List2</anno></c> deleted.</p> </desc> </func> <func> - <name>umerge(Fun, List1, List2) -> List3</name> + <name name="umerge" arity="3"/> <fsummary>Merge two sorted lists, removing duplicates</fsummary> - <type> - <v>Fun = fun(A, B) -> bool()</v> - <v>List1 = [A]</v> - <v>List2 = [B]</v> - <v>List3 = [A | B]</v> - <v> A = B = term()</v> - </type> <desc> - <p>Returns the sorted list formed by merging <c>List1</c> and - <c>List2</c>. Both <c>List1</c> and <c>List2</c> must be + <p>Returns the sorted list formed by merging <c><anno>List1</anno></c> and + <c><anno>List2</anno></c>. Both <c><anno>List1</anno></c> and <c><anno>List2</anno></c> must be sorted according to the <seealso marker="#ordering_function">ordering function</seealso> <c>Fun</c> and contain no duplicates prior to evaluating - this function. <c>Fun(A, B)</c> should return <c>true</c> if - <c>A</c> compares less than or equal to <c>B</c> in the + this function. <c><anno>Fun</anno>(<anno>A</anno>, <anno>B</anno>)</c> should return <c>true</c> if + <c><anno>A</anno></c> compares less than or equal to <c><anno>B</anno></c> in the ordering, <c>false</c> otherwise. When two elements compare equal, the element from - <c>List1</c> is picked and the one from <c>List2</c> + <c><anno>List1</anno></c> is picked and the one from <c><anno>List2</anno></c> deleted.</p> </desc> </func> <func> - <name>umerge3(List1, List2, List3) -> List4</name> + <name name="umerge3" arity="3"/> <fsummary>Merge three sorted lists, removing duplicates</fsummary> - <type> - <v>List1 = List2 = List3 = List4 = [term()]</v> - </type> <desc> - <p>Returns the sorted list formed by merging <c>List1</c>, - <c>List2</c> and <c>List3</c>. All of <c>List1</c>, - <c>List2</c> and <c>List3</c> must be sorted and contain no + <p>Returns the sorted list formed by merging <c><anno>List1</anno></c>, + <c><anno>List2</anno></c> and <c><anno>List3</anno></c>. All of <c><anno>List1</anno></c>, + <c><anno>List2</anno></c> and <c><anno>List3</anno></c> must be sorted and contain no duplicates prior to evaluating this function. When two - elements compare equal, the element from <c>List1</c> is + elements compare equal, the element from <c><anno>List1</anno></c> is picked if there is such an element, otherwise the element - from <c>List2</c> is picked, and the other one deleted.</p> + from <c><anno>List2</anno></c> is picked, and the other one deleted.</p> </desc> </func> <func> - <name>unzip(List1) -> {List2, List3}</name> + <name name="unzip" arity="1"/> <fsummary>Unzip a list of two-tuples into two lists</fsummary> - <type> - <v>List1 = [{X, Y}]</v> - <v>List2 = [X]</v> - <v>List3 = [Y]</v> - <v> X = Y = term()</v> - </type> <desc> <p>"Unzips" a list of two-tuples into two lists, where the first list contains the first element of each tuple, and the second @@ -1063,15 +818,8 @@ splitwith(Pred, List) -> </desc> </func> <func> - <name>unzip3(List1) -> {List2, List3, List4}</name> + <name name="unzip3" arity="1"/> <fsummary>Unzip a list of three-tuples into three lists</fsummary> - <type> - <v>List1 = [{X, Y, Z}]</v> - <v>List2 = [X]</v> - <v>List3 = [Y]</v> - <v>List4 = [Z]</v> - <v> X = Y = Z = term()</v> - </type> <desc> <p>"Unzips" a list of three-tuples into three lists, where the first list contains the first element of each tuple, @@ -1080,44 +828,30 @@ splitwith(Pred, List) -> </desc> </func> <func> - <name>usort(List1) -> List2</name> + <name name="usort" arity="1"/> <fsummary>Sort a list, removing duplicates</fsummary> - <type> - <v>List1 = List2 = [term()]</v> - </type> <desc> <p>Returns a list containing the sorted elements of - <c>List1</c> where all but the first element of the elements + <c><anno>List1</anno></c> where all but the first element of the elements comparing equal have been deleted.</p> </desc> </func> <func> - <name>usort(Fun, List1) -> List2</name> + <name name="usort" arity="2"/> <fsummary>Sort a list, removing duplicates</fsummary> - <type> - <v>Fun = fun(Elem1, Elem2) -> bool()</v> - <v> Elem1 = Elem2 = term()</v> - <v>List1 = List2 = [term()]</v> - </type> <desc> <p>Returns a list which contains the sorted elements of - <c>List1</c> where all but the first element of the elements + <c><anno>List1</anno></c> where all but the first element of the elements comparing equal according to the <seealso marker="#ordering_function">ordering function</seealso> - <c>Fun</c> have been deleted. <c>Fun(A, B)</c> should return + <c><anno>Fun</anno></c> have been deleted. <c><anno>Fun</anno>(A, B)</c> should return <c>true</c> if <c>A</c> compares less than or equal to <c>B</c> in the ordering, <c>false</c> otherwise.</p> </desc> </func> <func> - <name>zip(List1, List2) -> List3</name> + <name name="zip" arity="2"/> <fsummary>Zip two lists into a list of two-tuples</fsummary> - <type> - <v>List1 = [X]</v> - <v>List2 = [Y]</v> - <v>List3 = [{X, Y}]</v> - <v> X = Y = term()</v> - </type> <desc> <p>"Zips" two lists of equal length into one list of two-tuples, where the first element of each tuple is taken from the first @@ -1126,15 +860,8 @@ splitwith(Pred, List) -> </desc> </func> <func> - <name>zip3(List1, List2, List3) -> List4</name> + <name name="zip3" arity="3"/> <fsummary>Zip three lists into a list of three-tuples</fsummary> - <type> - <v>List1 = [X]</v> - <v>List2 = [Y]</v> - <v>List3 = [Z]</v> - <v>List3 = [{X, Y, Z}]</v> - <v> X = Y = Z = term()</v> - </type> <desc> <p>"Zips" three lists of equal length into one list of three-tuples, where the first element of each tuple is taken @@ -1145,20 +872,13 @@ splitwith(Pred, List) -> </desc> </func> <func> - <name>zipwith(Combine, List1, List2) -> List3</name> + <name name="zipwith" arity="3"/> <fsummary>Zip two lists into one list according to a fun</fsummary> - <type> - <v>Combine = fun(X, Y) -> T</v> - <v>List1 = [X]</v> - <v>List2 = [Y]</v> - <v>List3 = [T]</v> - <v> X = Y = T = term()</v> - </type> <desc> <p>Combine the elements of two lists of equal length into one - list. For each pair <c>X, Y</c> of list elements from the two + list. For each pair <c><anno>X</anno>, <anno>Y</anno></c> of list elements from the two lists, the element in the result list will be - <c>Combine(X, Y)</c>.</p> + <c><anno>Combine</anno>(<anno>X</anno>, <anno>Y</anno>)</c>.</p> <p><c>zipwith(fun(X, Y) -> {X,Y} end, List1, List2)</c> is equivalent to <c>zip(List1, List2)</c>.</p> <p>Example:</p> @@ -1168,21 +888,13 @@ splitwith(Pred, List) -> </desc> </func> <func> - <name>zipwith3(Combine, List1, List2, List3) -> List4</name> + <name name="zipwith3" arity="4"/> <fsummary>Zip three lists into one list according to a fun</fsummary> - <type> - <v>Combine = fun(X, Y, Z) -> T</v> - <v>List1 = [X]</v> - <v>List2 = [Y]</v> - <v>List3 = [Z]</v> - <v>List4 = [T]</v> - <v> X = Y = Z = T = term()</v> - </type> <desc> <p>Combine the elements of three lists of equal length into one - list. For each triple <c>X, Y, Z</c> of list elements from + list. For each triple <c><anno>X</anno>, <anno>Y</anno>, <anno>Z</anno></c> of list elements from the three lists, the element in the result list will be - <c>Combine(X, Y, Z)</c>.</p> + <c><anno>Combine</anno>(<anno>X</anno>, <anno>Y</anno>, <anno>Z</anno>)</c>.</p> <p><c>zipwith3(fun(X, Y, Z) -> {X,Y,Z} end, List1, List2, List3)</c> is equivalent to <c>zip3(List1, List2, List3)</c>.</p> <p>Examples:</p> <pre> diff --git a/lib/stdlib/doc/src/log_mf_h.xml b/lib/stdlib/doc/src/log_mf_h.xml index 198a55a63b..f2b09b58eb 100644 --- a/lib/stdlib/doc/src/log_mf_h.xml +++ b/lib/stdlib/doc/src/log_mf_h.xml @@ -5,7 +5,7 @@ <header> <copyright> <year>1996</year> - <year>2007</year> + <year>2011</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> @@ -43,28 +43,27 @@ report files <c>1, 2, ....</c>. </p> </description> + <datatypes> + <datatype> + <name name="args"/> + <desc><p>Term to be sent to <seealso marker="gen_event#add_handler/3"> + gen_event:add_handler/3</seealso>.</p></desc> + </datatype> + </datatypes> <funcs> <func> - <name>init(Dir, MaxBytes, MaxFiles)</name> - <name>init(Dir, MaxBytes, MaxFiles, Pred) -> Args</name> + <name name="init" arity="3"/> + <name name="init" arity="4"/> <fsummary>Initiate the event handler</fsummary> - <type> - <v>Dir = string()</v> - <v>MaxBytes = integer()</v> - <v>MaxFiles = 0 < integer() < 256</v> - <v>Pred = fun(Event) -> boolean()</v> - <v>Event = term()</v> - <v>Args = args()</v> - </type> <desc> <p>Initiates the event handler. This function returns - <c>Args</c>, which should be used in a call to - <c>gen_event:add_handler(EventMgr, log_mf_h, Args)</c>. + <c><anno>Args</anno></c>, which should be used in a call to + <c>gen_event:add_handler(EventMgr, log_mf_h, <anno>Args</anno>)</c>. </p> - <p><c>Dir</c> specifies which directory to use for the log - files. <c>MaxBytes</c> specifies the size of each individual - file. <c>MaxFiles</c> specifies how many files are - used. <c>Pred</c> is a predicate function used to filter the + <p><c><anno>Dir</anno></c> specifies which directory to use for the log + files. <c><anno>MaxBytes</anno></c> specifies the size of each individual + file. <c><anno>MaxFiles</anno></c> specifies how many files are + used. <c><anno>Pred</anno></c> is a predicate function used to filter the events. If no predicate function is specified, all events are logged.</p> </desc> diff --git a/lib/stdlib/doc/src/math.xml b/lib/stdlib/doc/src/math.xml index 990a6b4024..518457d5d8 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>2007</year> + <year>2011</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> @@ -45,7 +45,7 @@ </description> <funcs> <func> - <name>pi() -> float()</name> + <name name="pi" arity="0"/> <fsummary>A useful number</fsummary> <desc> <p>A useful number.</p> diff --git a/lib/stdlib/doc/src/ms_transform.xml b/lib/stdlib/doc/src/ms_transform.xml index 9f178b426c..f81f8bda96 100644 --- a/lib/stdlib/doc/src/ms_transform.xml +++ b/lib/stdlib/doc/src/ms_transform.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2002</year><year>2009</year> + <year>2002</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -13,12 +13,12 @@ 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. - + </legalnotice> <title>ms_transform</title> @@ -245,7 +245,7 @@ ets:select(emp_tab, ets:fun2ms( fun(#emp{empno = [$0 | Rest] }) -> {[$0|Rest],[$1|Rest]} end)). </code> - <p>As a matter of fact, this query hit's the feature of partially bound + <p>As a matter of fact, this query hits the feature of partially bound keys in the table type <c>ordered_set</c>, so that not the whole table need be searched, only the part of the table containing keys beginning with <c>0</c> is in fact looked into. </p> @@ -599,12 +599,9 @@ ets:select(Table, [{{'$1',test,'$2'},[],['$_']}]).</code> </description> <funcs> <func> - <name>parse_transform(Forms,_Options) -> Forms</name> + <name name="parse_transform" arity="2"/> <fsummary>Transforms Erlang abstract format containing calls to ets/dbg:fun2ms into literal match specifications.</fsummary> - <type> - <v>Forms = Erlang abstract code format, see the erl_parse module description </v> - <v>_Options = Option list, required but not used</v> - </type> + <type_desc variable="Options">Option list, required but not used.</type_desc> <desc> <p>Implements the actual transformation at compile time. This function is called by the compiler to do the source code @@ -617,29 +614,21 @@ ets:select(Table, [{{'$1',test,'$2'},[],['$_']}]).</code> </desc> </func> <func> - <name>transform_from_shell(Dialect,Clauses,BoundEnvironment) -> term()</name> + <name name="transform_from_shell" arity="3"/> <fsummary>Used when transforming fun's created in the shell into match_specifications.</fsummary> - <type> - <v>Dialect = ets | dbg</v> - <v>Clauses = Erlang abstract form for a single fun</v> - <v>BoundEnvironment = [{atom(), term()}, ...], list of variable bindings in the shell environment</v> - </type> + <type_desc variable="BoundEnvironment">List of variable bindings in the shell environment.</type_desc> <desc> <p>Implements the actual transformation when the <c>fun2ms</c> functions are called from the shell. In this case the abstract form is for one single fun (parsed by the Erlang shell), and all imported variables should be in the key-value list passed - as <c>BoundEnvironment</c>. The result is a term, normalized, + as <c><anno>BoundEnvironment</anno></c>. The result is a term, normalized, i.e. not in abstract format.</p> </desc> </func> <func> - <name>format_error(Errcode) -> ErrMessage</name> + <name name="format_error" arity="1"/> <fsummary>Error formatting function as required by the parse_transform interface.</fsummary> - <type> - <v>Errcode = term()</v> - <v>ErrMessage = string()</v> - </type> <desc> <p>Takes an error code returned by one of the other functions in the module and creates a textual description of the diff --git a/lib/stdlib/doc/src/notes.xml b/lib/stdlib/doc/src/notes.xml index c55eafc8b8..60c0b91212 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>2010</year> + <year>2004</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -30,6 +30,829 @@ </header> <p>This document describes the changes made to the STDLIB application.</p> +<section><title>STDLIB 1.17.4</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> The default value <c>undefined</c> was added to + records field types in such a way that the result was not + always a well-formed type. This bug has been fixed. </p> + <p> + Own Id: OTP-9147</p> + </item> + <item> + <p> + Update index file atomically</p> + <p> + Since the log_mf_h index file might be read by other + processes than the error handler (e.g. by the rb tool), + this file should be updated atomically. This will avoid + hitting the time gap between opening the file in write + mode (and thus emptying the file) and the actual update + with the new contents. To do this, a temporary file is + written, and the file:rename/1 used to replace the real + index file.</p> + <p> + Own Id: OTP-9148</p> + </item> + <item> + <p> + Fixed various typos across the documentation (Thanks to + Tuncer Ayaz)</p> + <p> + Own Id: OTP-9154</p> + </item> + <item> + <p> + Supervisors should not save child-specs for temporary + processes when they terminate as they should not be + restarted. Saving the temporary child spec will result in + that you can not start a new temporary process with the + same child spec as an already terminated temporary + process. Since R14B02 you can not restart a temporary + temporary process as arguments are no longer saved, it + has however always been semantically incorrect to restart + a temporary process. Thanks to Filipe David Manana for + reporting this and suggesting a solution.</p> + <p> + Own Id: OTP-9167 Aux Id: OTP-9064 </p> + </item> + <item> + <p> + Various small documentation fixes (Thanks to Bernard + Duggan)</p> + <p> + Own Id: OTP-9172</p> + </item> + <item> + <p> + Fix format_status bug for unregistered gen_event + processes</p> + <p> + Port the gen_fsm code for format_status to gen_event in + order to prevent a lists:concat([...,pid()]) crash when + calling sys:get_status/1 on an unregistered gen_event + process.</p> + <p> + Refactor format_status header code from gen_* behaviours + to module gen.</p> + <p> + Extend the format_status tests in gen_event_SUITE to + cover format_status bugs with anonymous gen_event + processes. (Thanks To Geoff Cant)</p> + <p> + Own Id: OTP-9218</p> + </item> + <item> + <p> + List of pids changed to 'set' in supervisor for dynamic + temporary children. Accessing the list would not scale + well when adding/deleting many children. (Thanks to + Evgeniy Khramtsov)</p> + <p> + Own Id: OTP-9242</p> + </item> + <item> + <p> + Change pool module to attempt to attach to nodes that are + already running</p> + <p> + The pool module prints out an error message and takes no + further action for nodes that are already running. This + patch changes that behavior so that if the return from + slave:start/3 is {already_running, Node} then an attempt + to attach to the node is still made. This makes sense + because the node has been specified by the user in the + .hosts.erlang file indicating a wish for the node to be + part of the pool and a manual attach can be successfully + made after the pool is started.(Thanks to Kelly + McLaughlin)</p> + <p> + Own Id: OTP-9244</p> + </item> + <item> + <p> + unicode: document 16#FFFE and 16#FFFF (non chars)(Thanks + to Tuncer Ayaz)</p> + <p> + Own Id: OTP-9256</p> + </item> + <item> + <p> + re: remove gratuitous "it " in manpage (Thanks to Tuncer + Ayaz)</p> + <p> + Own Id: OTP-9307</p> + </item> + <item> + <p> A bug in erl_eval(3) has been fixed. </p> + <p> + Own Id: OTP-9322</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Add <c>timer:tc/1</c> and remove the catch in <c>tc/2</c> + and <c>tc/3</c>. The time measuring functions will thus + no longer trap exits, errors or throws caused by the + measured function.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-9169</p> + </item> + <item> + <p> + Allow supervisor:terminate_child(SupRef,Pid) for + simple_one_for_one supervisors</p> + <p> + supervisor:terminate_child/2 was earlier not allowed if + the supervisor used restart strategy simple_one_for_one. + This is now changed so that children of this type of + supervisors can be terminated by specifying the child's + Pid.</p> + <p> + (Thanks to Vance Shipley.)</p> + <p> + Own Id: OTP-9201</p> + </item> + <item> + <p> Types and specifications have been added. </p> + <p> + Own Id: OTP-9267</p> + </item> + <item> + <p> Erlang types and specifications are used for + documentation. </p> + <p> + Own Id: OTP-9271</p> + </item> + <item> + <p>Allow Dets tablenames to be arbitrary terms.</p> + <p> + Own Id: OTP-9282</p> + </item> + <item> + <p> A specification that could cause problems for + Dialyzer has been fixed. An opaque type in erl_eval has + been turned in to a ordinary type. This is a temporary + fix. </p> + <p> + Own Id: OTP-9333</p> + </item> + </list> + </section> + +</section> + +<section><title>STDLIB 1.17.3</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Two bugs in io:format for ~F.~Ps has been corrected. When + length(S) >= abs(F) > P, the precision P was incorrectly + ignored. When F == P > lenght(S) the result was + incorrectly left adjusted. Bug found by Ali Yakout who + also provided a fix.</p> + <p> + Own Id: OTP-8989 Aux Id: seq11741 </p> + </item> + <item> + <p>Fix exception generation in the io module + <p> + Some functions did not generate correct badarg exception + on a badarg exception.</p></p> + <p> + Own Id: OTP-9045</p> + </item> + <item> + <p> + Fixes to the dict and orddict module documentation</p> + <p> + Fixed grammar and one inconsistency (Key - Value instead + of key/value, since everywhere else the former is used). + (thanks to Filipe David Manana)</p> + <p> + Own Id: OTP-9083</p> + </item> + <item> + <p> + Add ISO week number calculation functions to the calendar + module in stdlib</p> + <p> + This new feature adds the missing week number function to + the calendar module of the stdlib application. The + implementation conforms to the ISO 8601 standard. The new + feature has been implemented tested and documented + (thanks to Imre Horvath).</p> + <p> + Own Id: OTP-9087</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Implement the 'MAY' clauses from RFC4648 regarding the + pad character to make mime_decode() and + mime_decode_to_string() functions more tolerant of badly + padded base64. The RFC is quoted below for easy + reference.</p> + <p> + "RFC4648 Section 3.3 with reference to MIME decoding: + Furthermore, such specifications MAY ignore the pad + character, "=", treating it as non-alphabet data, if it + is present before the end of the encoded data. If more + than the allowed number of pad characters is found at the + end of the string (e.g., a base 64 string terminated with + "==="), the excess pad characters MAY also be ignored."</p> + <p> + Own Id: OTP-9020</p> + </item> + <item> + <p> + Supervisors will no longer save start parameters for + temporary processes as they will not be restarted. In the + case of simple_one_for_one workers such as ssl-connection + processes this will substantial reduce the memory + footprint of the supervisor.</p> + <p> + Own Id: OTP-9064</p> + </item> + <item> + <p> + When running escript it is now possible to add the -n + flag and the escript will be compiled using +native.</p> + <p> + Own Id: OTP-9076</p> + </item> + </list> + </section> + +</section> + +<section><title>STDLIB 1.17.2.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Several type specifications for standard libraries were + wrong in the R14B01 release. This is now corrected. The + corrections concern types in re,io,filename and the + module erlang itself.</p> + <p> + Own Id: OTP-9008</p> + </item> + </list> + </section> + +</section> + +<section><title>STDLIB 1.17.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> When several clients accessed a Dets table + simultaneously, one of them calling + <c>dets:insert_new/2</c>, the Dets server could crash. + Alternatively, under the same conditions, <c>ok</c> was + sometimes returned instead of <c>true</c>. (Thanks to + John Hughes.) </p> + <p> + Own Id: OTP-8856</p> + </item> + <item> + <p> When several clients accessed a Dets table + simultaneously, inserted or updated objects were + sometimes lost due to the Dets file being truncated. + (Thanks to John Hughes.) </p> + <p> + Own Id: OTP-8898</p> + </item> + <item> + <p> When several clients accessed a Dets table + simultaneously, modifications of the Dets server's + internal state were sometimes thrown away. The symptoms + are diverse: error with reason <c>bad_object</c>; + inserted objects not returned by <c>lookup()</c>; et + cetera. (Thanks to John Hughes.) </p> + <p> + Own Id: OTP-8899</p> + </item> + <item> + <p> If a Dets table was closed after calling + <c>bchunk/2</c>, <c>match/1,3</c>, + <c>match_object/1,3</c>, or <c>select/1,3</c> and then + opened again, a subsequent call using the returned + continuation would normally return a reply. This bug has + fixed; now the call fails with reason <c>badarg</c>. </p> + <p> + Own Id: OTP-8903</p> + </item> + <item> + <p> Cover did not collect coverage data for files such as + Yecc parses containing include directives. The bug has + been fixed by modifying <c>epp</c>, the Erlang Code + Preprocessor. </p> + <p> + Own Id: OTP-8911</p> + </item> + <item> + <p> If a Dets table with fewer slots than keys was opened + and then closed after just a lookup, the contents were no + longer well-formed. This bug has been fixed. (Thanks to + Matthew Evans.) </p> + <p> + Own Id: OTP-8923</p> + </item> + <item> + <p> + In a supervisor, when it terminates a child, if that + child happens to have exited fractionally early, with + normal, the supervisor reports this as an error. This + should not be reported as an error.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-8938 Aux Id: seq11615 </p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + The documentation filelib:wildcard/1,2 now describes the + character set syntax for wildcards.</p> + <p> + Own Id: OTP-8879 Aux Id: seq11683 </p> + </item> + <item> + <p>Buffer overflows have been prevented in <c>erlc</c>, + <c>dialyzer</c>, <c>typer</c>, <c>run_test</c>, + <c>heart</c>, <c>escript</c>, and <c>erlexec</c>.</p> + (Thanks to Michael Santos.) + <p> + Own Id: OTP-8892</p> + </item> + <item> + <p> + Using a float for the number of copies for + <c>string:copies/2</c> resulted in an infinite loop. Now + it will fail with an exception instead. (Thanks to + Michael Santos.)</p> + <p> + Own Id: OTP-8915</p> + </item> + <item> + <p> + New ETS option <c>compressed</c>, to enable a more + compact storage format at the expence of heavier table + operations. For test and evaluation, <c>erl +ec</c> can + be used to force compression on all ETS tables.</p> + <p> + Own Id: OTP-8922 Aux Id: seq11658 </p> + </item> + <item> + <p> The default maximum number of slots of a Dets table + has been changed as to be equal to the maximum number of + slots. (Thanks to Richard Carlsson.) </p> + <p> + Own Id: OTP-8959</p> + </item> + </list> + </section> + +</section> + +<section><title>STDLIB 1.17.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>reference() has been substituted for ref() in the + documentation.</p> + <p> + Own Id: OTP-8733</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + The ms_transform now warns if the fun head shadows + surrounding variables (just like the warnings you would + get for an ordinary fun in the same context).</p> + <p> + Own Id: OTP-6759</p> + </item> + <item> + <p> + ets:select_reverse/{1,2,3} are now documented.</p> + <p> + Own Id: OTP-7863</p> + </item> + <item> + <p> + Large parts of the <c>ethread</c> library have been + rewritten. The <c>ethread</c> library is an Erlang + runtime system internal, portable thread library used by + the runtime system itself.</p> + <p> + Most notable improvement is a reader optimized rwlock + implementation which dramatically improve the performance + of read-lock/read-unlock operations on multi processor + systems by avoiding ping-ponging of the rwlock cache + lines. The reader optimized rwlock implementation is used + by miscellaneous rwlocks in the runtime system that are + known to be read-locked frequently, and can be enabled on + ETS tables by passing the <seealso + marker="stdlib:ets#new_2_read_concurrency">{read_concurrency, + true}</seealso> option upon table creation. See the + documentation of <seealso + marker="stdlib:ets#new/2">ets:new/2</seealso> for more + information. The reader optimized rwlock implementation + can be fine tuned when starting the runtime system. For + more information, see the documentation of the <seealso + marker="erts:erl#+rg">+rg</seealso> command line argument + of <c>erl</c>.</p> + <p> + There is also a new implementation of rwlocks that is not + optimized for readers. Both implementations interleaves + readers and writers during contention as opposed to, + e.g., the NPTL (Linux) pthread rwlock implementation + which use either a reader or writer preferred strategy. + The reader/writer preferred strategy is problematic since + it starves threads doing the non-preferred operation.</p> + <p> + The new rwlock implementations in general performs better + in ERTS than common pthread implementations. However, in + some extremely heavily contended cases this is not the + case. Such heavy contention can more or less only appear + on ETS tables. This when multiple processes do very large + amounts of write locked operations simultaneously on the + same table. Such use of ETS is bad regardless of rwlock + implementation, will never scale, and is something we + strongly advise against.</p> + <p> + The new rwlock implementations depend on atomic + operations. If no native atomic implementation is found, + a fallback solution will be used. Using the fallback + implies a performance degradation. That is, it is more + important now than before to build OTP with a native + atomic implementation.</p> + <p> + The <c>ethread</c> library contains native atomic + implementations for, x86 (32 and 64 bit), powerpc (32 + bit), sparc V9 (32 and 64 bit), and tilera (32 bit). On + other hardware gcc's builtin support for atomic memory + access will be used if such exists. If no such support is + found, <c>configure</c> will warn about no atomic + implementation available.</p> + <p> + The <c>ethread</c> library can now also use the + <c>libatomic_ops</c> library for atomic memory accesses. + This makes it possible for the Erlang runtime system to + utilize optimized native atomic operations on more + platforms than before. If <c>configure</c> warns about no + atomic implementation available, try using the + <c>libatomic_ops</c> library. Use the <seealso + marker="doc/installation_guide:INSTALL#How-to-Build-and-Install-ErlangOTP_A-Closer-Look-at-the-individual-Steps_Configuring">--with-libatomic_ops=PATH</seealso> + <c>configure</c> command line argument when specifying + where the <c>libatomic_ops</c> installation is located. + The <c>libatomic_ops</c> library can be downloaded from: + <url + href="http://www.hpl.hp.com/research/linux/atomic_ops/">http://www.hpl.hp.com/research/linux/atomic_ops/</url></p> + <p> + The changed API of the <c>ethread</c> library has also + caused modifications in the Erlang runtime system. + Preparations for the to come "delayed deallocation" + feature has also been done since it depends on the + <c>ethread</c> library.</p> + <p> + <em>Note</em>: When building for x86, the <c>ethread</c> + library will now use instructions that first appeared on + the pentium 4 processor. If you want the runtime system + to be compatible with older processors (back to 486) you + need to pass the <seealso + marker="doc/installation_guide:INSTALL#How-to-Build-and-Install-ErlangOTP_A-Closer-Look-at-the-individual-Steps_Configuring">--enable-ethread-pre-pentium4-compatibility</seealso> + <c>configure</c> command line argument when configuring + the system.</p> + <p> + Own Id: OTP-8544</p> + </item> + <item> + <p> + Some Built In Functions (BIFs) from the module erlang was + never made autoimported for backward compatibility + reasons. As local functions now override autoimports, new + autoimports is no longer a problem, why the following + BIFs are finally made autoimported: monitor/2, monitor/3, + demonitor/2, demonitor/3, error/1, error/2, + integer_to_list/2, list_to_integer/2.</p> + <p> + Own Id: OTP-8763</p> + </item> + </list> + </section> + +</section> + +<section><title>STDLIB 1.17</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>The Erlang code preprocessor (<c>epp</c>) sent extra + messages on the form <c>{eof,Location}</c> to the client + when parsing the <c>file</c> attribute. This bug, + introduced in R11B, has been fixed.</p> + <p> + Own Id: OTP-8470</p> + </item> + <item> + <p>The abstract type 'fun' could not be printed by the + Erlang pretty printer (<c>erl_pp</c>). This bug has been + fixed.</p> + <p> + Own Id: OTP-8473</p> + </item> + <item> + <p>The function <c>erl_scan:reserved_word/1</c> no longer + returns <c>true</c> when given the word <c>spec</c>. This + bug was introduced in STDLIB-1.15.3 (R12B-3).</p> + <p> + Own Id: OTP-8567</p> + </item> + <item> + <p>The documentation of <c>lists:keysort/2</c> states + that the sort is stable.</p> + <p> + Own Id: OTP-8628 Aux Id: seq11576 </p> + </item> + <item> + <p> + The shell's line editing has been improved to more + resemble the behaviour of readline and other shells. + (Thanks to Dave Peticolas)</p> + <p> + Own Id: OTP-8635</p> + </item> + <item> + <p>The Erlang code preprocessor (<c>epp</c>) did not + correctly handle premature end-of-input when defining + macros. This bug, introduced in STDLIB 1.16, has been + fixed.</p> + <p> + Own Id: OTP-8665 Aux Id: OTP-7810 </p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + The module binary from EEP31 (and EEP9) is implemented.</p> + <p> + Own Id: OTP-8217</p> + </item> + <item> + <p>The erlang pretty printer (<c>erl_pp</c>) no longer + quotes atoms in types.</p> + <p> + Own Id: OTP-8501</p> + </item> + <item> + <p>The Erlang code preprocessor (<c>epp</c>) now + considers records with no fields as typed.</p> + <p> + Own Id: OTP-8503</p> + </item> + <item> + <p> + Added function <c>zip:foldl/3</c> to iterate over zip + archives.</p> + <p> + Added functions to create and extract escripts. See + <c>escript:create/2</c> and <c>escript:extract/2</c>.</p> + <p> + The undocumented function <c>escript:foldl/3</c> has been + removed. The same functionality can be achieved with the + more flexible functions <c>escript:extract/2</c> and + <c>zip:foldl/3</c>.</p> + <p> + Record fields has been annotated with type info. Source + files as been adapted to fit within 80 chars and trailing + whitespace has been removed.</p> + <p> + Own Id: OTP-8521</p> + </item> + <item> + <p>The Erlang parser no longer duplicates the singleton + type <c>undefined</c> in the type of record fields + without initial value.</p> + <p> + Own Id: OTP-8522</p> + </item> + <item> + <p>A regular expression with many levels of parenthesis + could cause a buffer overflow. That has been corrected. + (Thanks to Michael Santos.)</p> + <p> + Own Id: OTP-8539</p> + </item> + <item> + <p>When defining macros the closing right parenthesis + before the dot is now mandatory.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-8562</p> + </item> + <item> + <p> + Some properties of a compiled re pattern are defined to + allow for guard tests.</p> + <p> + Own Id: OTP-8577</p> + </item> + <item> + <p>Local and imported functions now override the + auto-imported BIFs when the names clash. The pre R14 + behaviour was that auto-imported BIFs would override + local functions. To avoid that old programs change + behaviour, the following will generate an error:</p> + <list><item><p>Doing a call without explicit module name + to a local function having a name clashing with the name + of an auto-imported BIF that was present (and + auto-imported) before OTP R14A</p></item> + <item><p>Explicitly importing a function having a name + clashing with the name of an autoimported BIF that was + present (and autoimported) before OTP R14A</p></item> + <item><p>Using any form of the old compiler directive + <c>nowarn_bif_clash</c></p></item> </list> <p>If the BIF + was added or auto-imported in OTP R14A or later, + overriding it with an import or a local function will + only result in a warning,</p> <p>To resolve clashes, you + can either use the explicit module name <c>erlang</c> to + call the BIF, or you can remove the auto-import of that + specific BIF by using the new compiler directive + <c>-compile({no_auto_import,[F/A]}).</c>, which makes all + calls to the local or imported function without explicit + module name pass without warnings or errors.</p> <p>The + change makes it possible to add auto-imported BIFs + without breaking or silently changing old code in the + future. However some current code ingeniously utilizing + the old behaviour or the <c>nowarn_bif_clash</c> compiler + directive, might need changing to be accepted by the + compiler.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-8579</p> + </item> + <item> + <p>The undocumented, unsupport, and deprecated function + <c>lists:flat_length/1</c> has been removed.</p> + <p> + Own Id: OTP-8584</p> + </item> + <item> + <p> + A bug in re that could cause certain regular expression + matches never to terminate is corrected. (Thanks to + Michael Santos and Gordon Guthrie.)</p> + <p> + Own Id: OTP-8589</p> + </item> + <item> + <p>Nested records can now be accessed without + parenthesis. See the Reference Manual for examples. + (Thanks to YAMASHINA Hio and Tuncer Ayaz.)</p> + <p> + Own Id: OTP-8597</p> + </item> + <item> + <p><c>receive</c> statements that can only read out a + newly created reference are now specially optimized so + that it will execute in constant time regardless of the + number of messages in the receive queue for the process. + That optimization will benefit calls to + <c>gen_server:call()</c>. (See <c>gen:do_call/4</c> for + an example of a receive statement that will be + optimized.)</p> + <p> + Own Id: OTP-8623</p> + </item> + <item> + <p>The beam_lib:cmp/2 function now compares BEAM files in + stricter way. The BEAM files will be considered different + if there are any changes except in the compilation + information ("CInf") chunk. beam_lib:cmp/2 used to ignore + differences in the debug information (significant for + Dialyzer) and other chunks that did not directly change + the run-time behavior.</p> + <p> + Own Id: OTP-8625</p> + </item> + <item> + <p> + When a gen_server, gen_fsm process, or gen_event + terminates abnormally, sometimes the text representation + of the process state can occupy many lines of the error + log, depending on the definition of the state term. A + mechanism to trim out parts of the state from the log has + been added (using a format_status/2 callback). See the + documentation.</p> + <p> + Own Id: OTP-8630</p> + </item> + <item> + <p> + Calling <c>sys:get_status()</c> for processes that have + globally registered names that were not atoms would cause + a crash. Corrected. (Thanks to Steve Vinoski.)</p> + <p> + Own Id: OTP-8656</p> + </item> + <item> + <p>The Erlang scanner has been augmented with two new + tokens: <c>..</c> and <c>...</c>.</p> + <p> + Own Id: OTP-8657</p> + </item> + <item> + <p>Expressions evaluating to integers can now be used in + types and function specifications where hitherto only + integers were allowed ("Erlang_Integer").</p> + <p> + Own Id: OTP-8664</p> + </item> + <item> + <p>The compiler optimizes record operations better.</p> + <p> + Own Id: OTP-8668</p> + </item> + <item> + <p> + The recently added BIFs erlang:min/2, erlang:max/2 and + erlang:port_command/3 are now auto-imported (as they were + originally intended to be). Due to the recent compiler + change (OTP-8579), the only impact on old code defining + it's own min/2, max/2 or port_command/3 functions will be + a warning, the local functions will still be used. The + warning can be removed by using + -compile({no_auto_import,[min/2,max/2,port_command/3]}). + in the source file.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-8669 Aux Id: OTP-8579 </p> + </item> + <item> + <p> + Now, binary_to_term/2 is auto-imported. This will cause a + compile warning if and only if a module has got a local + function with that name.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-8671</p> + </item> + <item> + <p> + The predefined builtin type tid() has been removed. + Instead, ets:tid() should be used.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-8687</p> + </item> + </list> + </section> + +</section> + <section><title>STDLIB 1.16.5</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -50,7 +873,7 @@ <item> <p>A number of bugs concerning re and unicode are corrected:</p> - <p>re:compile no longer looses unicode option, which also + <p>re:compile no longer loses unicode option, which also fixes bug in re:split.</p> <p>re:replace now handles unicode charlist replacement argument</p> diff --git a/lib/stdlib/doc/src/orddict.xml b/lib/stdlib/doc/src/orddict.xml index 08c808f822..319083d35b 100644 --- a/lib/stdlib/doc/src/orddict.xml +++ b/lib/stdlib/doc/src/orddict.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2000</year><year>2009</year> + <year>2000</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -46,176 +46,123 @@ (<c>==</c>).</p> </description> - <section> - <title>DATA TYPES</title> - <code type="none"> -ordered_dictionary() - as returned by new/0</code> - </section> + <datatypes> + <datatype> + <name name="orddict"/> + <desc><p>As returned by new/0.</p></desc> + </datatype> + </datatypes> + <funcs> <func> - <name>append(Key, Value, Orddict1) -> Orddict2</name> + <name name="append" arity="3"/> <fsummary>Append a value to keys in a dictionary</fsummary> - <type> - <v>Key = Value = term()</v> - <v>Orddict1 = Orddict2 = ordered_dictionary()</v> - </type> <desc> - <p>This function appends a new <c>Value</c> to the current list - of values associated with <c>Key</c>. An exception is - generated if the initial value associated with <c>Key</c> is + <p>This function appends a new <c><anno>Value</anno></c> to the current list + of values associated with <c><anno>Key</anno></c>. An exception is + generated if the initial value associated with <c><anno>Key</anno></c> is not a list of values.</p> </desc> </func> <func> - <name>append_list(Key, ValList, Orddict1) -> Orddict2</name> + <name name="append_list" arity="3"/> <fsummary>Append new values to keys in a dictionary</fsummary> - <type> - <v>ValList = [Value]</v> - <v>Key = Value = term()</v> - <v>Orddict1 = Orddict2 = ordered_dictionary()</v> - </type> <desc> - <p>This function appends a list of values <c>ValList</c> to - the current list of values associated with <c>Key</c>. An + <p>This function appends a list of values <c><anno>ValList</anno></c> to + the current list of values associated with <c><anno>Key</anno></c>. An exception is generated if the initial value associated with - <c>Key</c> is not a list of values.</p> + <c><anno>Key</anno></c> is not a list of values.</p> </desc> </func> <func> - <name>erase(Key, Orddict1) -> Orddict2</name> + <name name="erase" arity="2"/> <fsummary>Erase a key from a dictionary</fsummary> - <type> - <v>Key = term()</v> - <v>Orddict1 = Orddict2 = ordered_dictionary()</v> - </type> <desc> <p>This function erases all items with a given key from a dictionary.</p> </desc> </func> <func> - <name>fetch(Key, Orddict) -> Value</name> + <name name="fetch" arity="2"/> <fsummary>Look-up values in a dictionary</fsummary> - <type> - <v>Key = Value = term()</v> - <v>Orddict = ordered_dictionary()</v> - </type> <desc> - <p>This function returns the value associated with <c>Key</c> - in the dictionary <c>Orddict</c>. <c>fetch</c> assumes that - the <c>Key</c> is present in the dictionary and an exception - is generated if <c>Key</c> is not in the dictionary.</p> + <p>This function returns the value associated with <c><anno>Key</anno></c> + in the dictionary <c><anno>Orddict</anno></c>. <c>fetch</c> assumes that + the <c><anno>Key</anno></c> is present in the dictionary and an exception + is generated if <c><anno>Key</anno></c> is not in the dictionary.</p> </desc> </func> <func> - <name>fetch_keys(Orddict) -> Keys</name> + <name name="fetch_keys" arity="1"/> <fsummary>Return all keys in a dictionary</fsummary> - <type> - <v>Orddict = ordered_dictionary()</v> - <v>Keys = [term()]</v> - </type> <desc> <p>This function returns a list of all keys in the dictionary.</p> </desc> </func> <func> - <name>filter(Pred, Orddict1) -> Orddict2</name> + <name name="filter" arity="2"/> <fsummary>Choose elements which satisfy a predicate</fsummary> - <type> - <v>Pred = fun(Key, Value) -> bool()</v> - <v> Key = Value = term()</v> - <v>Orddict1 = Orddict2 = ordered_dictionary()</v> - </type> <desc> - <p><c>Orddict2</c> is a dictionary of all keys and values in - <c>Orddict1</c> for which <c>Pred(Key, Value)</c> is <c>true</c>.</p> + <p><c><anno>Orddict2</anno></c> is a dictionary of all keys and values in + <c><anno>Orddict1</anno></c> for which <c><anno>Pred</anno>(<anno>Key</anno>, <anno>Value</anno>)</c> is <c>true</c>.</p> </desc> </func> <func> - <name>find(Key, Orddict) -> {ok, Value} | error</name> + <name name="find" arity="2"/> <fsummary>Search for a key in a dictionary</fsummary> - <type> - <v>Key = Value = term()</v> - <v>Orddict = ordered_dictionary()</v> - </type> <desc> <p>This function searches for a key in a dictionary. Returns - <c>{ok, Value}</c> where <c>Value</c> is the value associated - with <c>Key</c>, or <c>error</c> if the key is not present in + <c>{ok, <anno>Value</anno>}</c> where <c><anno>Value</anno></c> is the value associated + with <c><anno>Key</anno></c>, or <c>error</c> if the key is not present in the dictionary.</p> </desc> </func> <func> - <name>fold(Fun, Acc0, Orddict) -> Acc1</name> + <name name="fold" arity="3"/> <fsummary>Fold a function over a dictionary</fsummary> - <type> - <v>Fun = fun(Key, Value, AccIn) -> AccOut</v> - <v>Key = Value = term()</v> - <v>Acc0 = Acc1 = AccIn = AccOut = term()</v> - <v>Orddict = ordered_dictionary()</v> - </type> <desc> - <p>Calls <c>Fun</c> on successive keys and values of - <c>Orddict</c> together with an extra argument <c>Acc</c> - (short for accumulator). <c>Fun</c> must return a new - accumulator which is passed to the next call. <c>Acc0</c> is + <p>Calls <c><anno>Fun</anno></c> on successive keys and values of + <c><anno>Orddict</anno></c> together with an extra argument <c>Acc</c> + (short for accumulator). <c><anno>Fun</anno></c> must return a new + accumulator which is passed to the next call. <c><anno>Acc0</anno></c> is returned if the list is empty. The evaluation order is undefined.</p> </desc> </func> <func> - <name>from_list(List) -> Orddict</name> + <name name="from_list" arity="1"/> <fsummary>Convert a list of pairs to a dictionary</fsummary> - <type> - <v>List = [{Key, Value}]</v> - <v>Orddict = ordered_dictionary()</v> - </type> <desc> - <p>This function converts the key/value list <c>List</c> to a - dictionary.</p> + <p>This function converts the <c><anno>Key</anno></c> - <c><anno>Value</anno></c> list + <c><anno>List</anno></c> to a dictionary.</p> </desc> </func> <func> - <name>is_key(Key, Orddict) -> bool()</name> + <name name="is_key" arity="2"/> <fsummary>Test if a key is in a dictionary</fsummary> - <type> - <v>Key = term()</v> - <v>Orddict = ordered_dictionary()</v> - </type> <desc> - <p>This function tests if <c>Key</c> is contained in - the dictionary <c>Orddict</c>.</p> + <p>This function tests if <c><anno>Key</anno></c> is contained in + the dictionary <c><anno>Orddict</anno></c>.</p> </desc> </func> <func> - <name>map(Fun, Orddict1) -> Orddict2</name> + <name name="map" arity="2"/> <fsummary>Map a function over a dictionary</fsummary> - <type> - <v>Fun = fun(Key, Value1) -> Value2</v> - <v> Key = Value1 = Value2 = term()</v> - <v>Orddict1 = Orddict2 = ordered_dictionary()</v> - </type> <desc> - <p><c>map</c> calls <c>Func</c> on successive keys and values - of <c>Orddict</c> to return a new value for each key. + <p><c>map</c> calls <c><anno>Fun</anno></c> on successive keys and values + of <c><anno>Orddict1</anno></c> to return a new value for each key. The evaluation order is undefined.</p> </desc> </func> <func> - <name>merge(Fun, Orddict1, Orddict2) -> Orddict3</name> + <name name="merge" arity="3"/> <fsummary>Merge two dictionaries</fsummary> - <type> - <v>Fun = fun(Key, Value1, Value2) -> Value</v> - <v> Key = Value1 = Value2 = Value3 = term()</v> - <v>Orddict1 = Orddict2 = Orddict3 = ordered_dictionary()</v> - </type> <desc> - <p><c>merge</c> merges two dictionaries, <c>Orddict1</c> and - <c>Orddict2</c>, to create a new dictionary. All the <c>Key</c> - - <c>Value</c> pairs from both dictionaries are included in + <p><c>merge</c> merges two dictionaries, <c><anno>Orddict1</anno></c> and + <c><anno>Orddict2</anno></c>, to create a new dictionary. All the <c><anno>Key</anno></c> + - <c><anno>Value</anno></c> pairs from both dictionaries are included in the new dictionary. If a key occurs in both dictionaries then - <c>Fun</c> is called with the key and both values to return a + <c><anno>Fun</anno></c> is called with the key and both values to return a new value. <c>merge</c> could be defined as:</p> <code type="none"> merge(Fun, D1, D2) -> @@ -226,75 +173,52 @@ merge(Fun, D1, D2) -> </desc> </func> <func> - <name>new() -> ordered_dictionary()</name> + <name name="new" arity="0"/> <fsummary>Create a dictionary</fsummary> <desc> <p>This function creates a new dictionary.</p> </desc> </func> <func> - <name>size(Orddict) -> int()</name> + <name name="size" arity="1"/> <fsummary>Return the number of elements in an ordered dictionary</fsummary> - <type> - <v>Orddict = ordered_dictionary()</v> - </type> <desc> - <p>Returns the number of elements in an <c>Orddict</c>.</p> + <p>Returns the number of elements in an <c><anno>Orddict</anno></c>.</p> </desc> </func> <func> - <name>store(Key, Value, Orddict1) -> Orddict2</name> + <name name="store" arity="3"/> <fsummary>Store a value in a dictionary</fsummary> - <type> - <v>Key = Value = term()</v> - <v>Orddict1 = Orddict2 = ordered_dictionary()</v> - </type> <desc> - <p>This function stores a <c>Key</c> - <c>Value</c> pair in a - dictionary. If the <c>Key</c> already exists in <c>Orddict1</c>, - the associated value is replaced by <c>Value</c>.</p> + <p>This function stores a <c><anno>Key</anno></c> - <c><anno>Value</anno></c> pair in a + dictionary. If the <c><anno>Key</anno></c> already exists in <c><anno>Orddict1</anno></c>, + the associated value is replaced by <c><anno>Value</anno></c>.</p> </desc> </func> <func> - <name>to_list(Orddict) -> List</name> + <name name="to_list" arity="1"/> <fsummary>Convert a dictionary to a list of pairs</fsummary> - <type> - <v>Orddict = ordered_dictionary()</v> - <v>List = [{Key, Value}]</v> - </type> <desc> <p>This function converts the dictionary to a list representation.</p> </desc> </func> <func> - <name>update(Key, Fun, Orddict1) -> Orddict2</name> + <name name="update" arity="3"/> <fsummary>Update a value in a dictionary</fsummary> - <type> - <v>Key = term()</v> - <v>Fun = fun(Value1) -> Value2</v> - <v> Value1 = Value2 = term()</v> - <v>Orddict1 = Orddict2 = ordered_dictionary()</v> - </type> <desc> - <p>Update the a value in a dictionary by calling <c>Fun</c> on + <p>Update a value in a dictionary by calling <c><anno>Fun</anno></c> on the value to get a new value. An exception is generated if - <c>Key</c> is not present in the dictionary.</p> + <c><anno>Key</anno></c> is not present in the dictionary.</p> </desc> </func> <func> - <name>update(Key, Fun, Initial, Orddict1) -> Orddict2</name> + <name name="update" arity="4"/> <fsummary>Update a value in a dictionary</fsummary> - <type> - <v>Key = Initial = term()</v> - <v>Fun = fun(Value1) -> Value2</v> - <v> Value1 = Value2 = term()</v> - <v>Orddict1 = Orddict2 = ordered_dictionary()</v> - </type> <desc> - <p>Update the a value in a dictionary by calling <c>Fun</c> on - the value to get a new value. If <c>Key</c> is not present - in the dictionary then <c>Initial</c> will be stored as + <p>Update a value in a dictionary by calling <c><anno>Fun</anno></c> on + the value to get a new value. If <c><anno>Key</anno></c> is not present + in the dictionary then <c><anno>Initial</anno></c> will be stored as the first value. For example <c>append/3</c> could be defined as:</p> <code type="none"> @@ -303,17 +227,12 @@ append(Key, Val, D) -> </desc> </func> <func> - <name>update_counter(Key, Increment, Orddict1) -> Orddict2</name> + <name name="update_counter" arity="3"/> <fsummary>Increment a value in a dictionary</fsummary> - <type> - <v>Key = term()</v> - <v>Increment = number()</v> - <v>Orddict1 = Orddict2 = ordered_dictionary()</v> - </type> <desc> - <p>Add <c>Increment</c> to the value associated with <c>Key</c> - and store this value. If <c>Key</c> is not present in - the dictionary then <c>Increment</c> will be stored as + <p>Add <c><anno>Increment</anno></c> to the value associated with <c><anno>Key</anno></c> + and store this value. If <c><anno>Key</anno></c> is not present in + the dictionary then <c><anno>Increment</anno></c> will be stored as the first value.</p> <p>This could be defined as:</p> <code type="none"> diff --git a/lib/stdlib/doc/src/ordsets.xml b/lib/stdlib/doc/src/ordsets.xml index a20ab2d879..1e26fc2022 100644 --- a/lib/stdlib/doc/src/ordsets.xml +++ b/lib/stdlib/doc/src/ordsets.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2009</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -45,202 +45,141 @@ different if and only if they do not compare equal (<c>==</c>).</p> </description> - <section> - <title>DATA TYPES</title> - <code type="none"> -ordered_set() - as returned by new/0</code> - </section> + <datatypes> + <datatype> + <name name="ordset" n_vars="1"/> + <desc><p>As returned by new/0.</p></desc> + </datatype> + </datatypes> <funcs> <func> - <name>new() -> Ordset</name> + <name name="new" arity="0"/> <fsummary>Return an empty set</fsummary> - <type> - <v>Ordset = ordered_set()</v> - </type> <desc> <p>Returns a new empty ordered set.</p> </desc> </func> <func> - <name>is_set(Ordset) -> bool()</name> + <name name="is_set" arity="1"/> <fsummary>Test for an <c>Ordset</c></fsummary> - <type> - <v>Ordset = term()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>Ordset</c> is an ordered set of + <p>Returns <c>true</c> if <c><anno>Ordset</anno></c> is an ordered set of elements, otherwise <c>false</c>.</p> </desc> </func> <func> - <name>size(Ordset) -> int()</name> + <name name="size" arity="1"/> <fsummary>Return the number of elements in a set</fsummary> - <type> - <v>Ordset = term()</v> - </type> <desc> - <p>Returns the number of elements in <c>Ordset</c>.</p> + <p>Returns the number of elements in <c><anno>Ordset</anno></c>.</p> </desc> </func> <func> - <name>to_list(Ordset) -> List</name> + <name name="to_list" arity="1"/> <fsummary>Convert an <c>Ordset</c>into a list</fsummary> - <type> - <v>Ordset = ordered_set()</v> - <v>List = [term()]</v> - </type> <desc> - <p>Returns the elements of <c>Ordset</c> as a list.</p> + <p>Returns the elements of <c><anno>Ordset</anno></c> as a list.</p> </desc> </func> <func> - <name>from_list(List) -> Ordset</name> + <name name="from_list" arity="1"/> <fsummary>Convert a list into an <c>Ordset</c></fsummary> - <type> - <v>List = [term()]</v> - <v>Ordset = ordered_set()</v> - </type> <desc> - <p>Returns an ordered set of the elements in <c>List</c>.</p> + <p>Returns an ordered set of the elements in <c><anno>List</anno></c>.</p> </desc> </func> <func> - <name>is_element(Element, Ordset) -> bool()</name> + <name name="is_element" arity="2"/> <fsummary>Test for membership of an <c>Ordset</c></fsummary> - <type> - <v>Element = term()</v> - <v>Ordset = ordered_set()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>Element</c> is an element of - <c>Ordset</c>, otherwise <c>false</c>.</p> + <p>Returns <c>true</c> if <c><anno>Element</anno></c> is an element of + <c><anno>Ordset</anno></c>, otherwise <c>false</c>.</p> </desc> </func> <func> - <name>add_element(Element, Ordset1) -> Ordset2</name> + <name name="add_element" arity="2"/> <fsummary>Add an element to an <c>Ordset</c></fsummary> - <type> - <v>Element = term()</v> - <v>Ordset1 = Ordset2 = ordered_set()</v> - </type> <desc> - <p>Returns a new ordered set formed from <c>Ordset1</c> with - <c>Element</c> inserted.</p> + <p>Returns a new ordered set formed from <c><anno>Ordset1</anno></c> with + <c><anno>Element</anno></c> inserted.</p> </desc> </func> <func> - <name>del_element(Element, Ordset1) -> Ordset2</name> + <name name="del_element" arity="2"/> <fsummary>Remove an element from an <c>Ordset</c></fsummary> - <type> - <v>Element = term()</v> - <v>Ordset1 = Ordset2 = ordered_set()</v> - </type> <desc> - <p>Returns <c>Ordset1</c>, but with <c>Element</c> removed.</p> + <p>Returns <c><anno>Ordset1</anno></c>, but with <c><anno>Element</anno></c> removed.</p> </desc> </func> <func> - <name>union(Ordset1, Ordset2) -> Ordset3</name> + <name name="union" arity="2"/> <fsummary>Return the union of two <c>Ordsets</c></fsummary> - <type> - <v>Ordset1 = Ordset2 = Ordset3 = ordered_set()</v> - </type> <desc> - <p>Returns the merged (union) set of <c>Ordset1</c> and - <c>Ordset2</c>.</p> + <p>Returns the merged (union) set of <c><anno>Ordset1</anno></c> and + <c><anno>Ordset2</anno></c>.</p> </desc> </func> <func> - <name>union(OrdsetList) -> Ordset</name> + <name name="union" arity="1"/> <fsummary>Return the union of a list of <c>Ordsets</c></fsummary> - <type> - <v>OrdsetList = [ordered_set()]</v> - <v>Ordset = ordered_set()</v> - </type> <desc> <p>Returns the merged (union) set of the list of sets.</p> </desc> </func> <func> - <name>intersection(Ordset1, Ordset2) -> Ordset3</name> + <name name="intersection" arity="2"/> <fsummary>Return the intersection of two <c>Ordsets</c></fsummary> - <type> - <v>Ordset1 = Ordset2 = Ordset3 = ordered_set()</v> - </type> <desc> - <p>Returns the intersection of <c>Ordset1</c> and - <c>Ordset2</c>.</p> + <p>Returns the intersection of <c><anno>Ordset1</anno></c> and + <c><anno>Ordset2</anno></c>.</p> </desc> </func> <func> - <name>intersection(OrdsetList) -> Ordset</name> + <name name="intersection" arity="1"/> <fsummary>Return the intersection of a list of <c>Ordsets</c></fsummary> - <type> - <v>OrdsetList = [ordered_set()]</v> - <v>Ordset = ordered_set()</v> - </type> <desc> <p>Returns the intersection of the non-empty list of sets.</p> </desc> </func> <func> - <name>is_disjoint(Ordset1, Ordset2) -> bool()</name> + <name name="is_disjoint" arity="2"/> <fsummary>Check whether two <c>Ordsets</c> are disjoint</fsummary> - <type> - <v>Ordset1 = Ordset2 = ordered_set()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>Ordset1</c> and - <c>Ordset2</c> are disjoint (have no elements in common), + <p>Returns <c>true</c> if <c><anno>Ordset1</anno></c> and + <c><anno>Ordset2</anno></c> are disjoint (have no elements in common), and <c>false</c> otherwise.</p> </desc> </func> <func> - <name>subtract(Ordset1, Ordset2) -> Ordset3</name> + <name name="subtract" arity="2"/> <fsummary>Return the difference of two <c>Ordsets</c></fsummary> - <type> - <v>Ordset1 = Ordset2 = Ordset3 = ordered_set()</v> - </type> <desc> - <p>Returns only the elements of <c>Ordset1</c> which are not - also elements of <c>Ordset2</c>.</p> + <p>Returns only the elements of <c><anno>Ordset1</anno></c> which are not + also elements of <c><anno>Ordset2</anno></c>.</p> </desc> </func> <func> - <name>is_subset(Ordset1, Ordset2) -> bool()</name> + <name name="is_subset" arity="2"/> <fsummary>Test for subset</fsummary> - <type> - <v>Ordset1 = Ordset2 = ordered_set()</v> - </type> <desc> - <p>Returns <c>true</c> when every element of <c>Ordset</c>1 is - also a member of <c>Ordset2</c>, otherwise <c>false</c>.</p> + <p>Returns <c>true</c> when every element of <c><anno>Ordset1</anno></c> is + also a member of <c><anno>Ordset2</anno></c>, otherwise <c>false</c>.</p> </desc> </func> <func> - <name>fold(Function, Acc0, Ordset) -> Acc1</name> + <name name="fold" arity="3"/> <fsummary>Fold over set elements</fsummary> - <type> - <v>Function = fun (E, AccIn) -> AccOut</v> - <v>Acc0 = Acc1 = AccIn = AccOut = term()</v> - <v>Ordset = ordered_set()</v> - </type> <desc> - <p>Fold <c>Function</c> over every element in <c>Ordset</c> + <p>Fold <c><anno>Function</anno></c> over every element in <c><anno>Ordset</anno></c> returning the final value of the accumulator.</p> </desc> </func> <func> - <name>filter(Pred, Ordset1) -> Set2</name> + <name name="filter" arity="2"/> <fsummary>Filter set elements</fsummary> - <type> - <v>Pred = fun (E) -> bool()</v> - <v>Set1 = Set2 = ordered_set()</v> - </type> <desc> - <p>Filter elements in <c>Set1</c> with boolean function - <c>Fun</c>.</p> + <p>Filter elements in <c><anno>Ordset1</anno></c> with boolean function + <c><anno>Pred</anno></c>.</p> </desc> </func> </funcs> diff --git a/lib/stdlib/doc/src/part_notes_history.xml b/lib/stdlib/doc/src/part_notes_history.xml index 744b009583..5e055ee606 100644 --- a/lib/stdlib/doc/src/part_notes_history.xml +++ b/lib/stdlib/doc/src/part_notes_history.xml @@ -5,7 +5,7 @@ <header> <copyright> <year>2006</year> - <year>2007</year> + <year>2011</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> diff --git a/lib/stdlib/doc/src/pg.xml b/lib/stdlib/doc/src/pg.xml index 66b9702ae0..c56db8c6e6 100644 --- a/lib/stdlib/doc/src/pg.xml +++ b/lib/stdlib/doc/src/pg.xml @@ -5,7 +5,7 @@ <header> <copyright> <year>1996</year> - <year>2007</year> + <year>2011</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> @@ -51,77 +51,56 @@ </description> <funcs> <func> - <name>create(PgName) -> ok | {error, Reason}</name> + <name name="create" arity="1"/> <fsummary>Create an empty group</fsummary> - <type> - <v>PgName = term()</v> - <v>Reason = already_created | term()</v> - </type> <desc> - <p>Creates an empty group named <c>PgName</c> on the current + <p>Creates an empty group named <c><anno>PgName</anno></c> on the current node.</p> </desc> </func> <func> - <name>create(PgName, Node) -> ok | {error, Reason}</name> + <name name="create" arity="2"/> <fsummary>Create an empty group on another node</fsummary> - <type> - <v>PgName = term()</v> - <v>Node = node()</v> - <v>Reason = already_created | term()</v> - </type> <desc> - <p>Creates an empty group named <c>PgName</c> on the node - <c>Node</c>.</p> + <p>Creates an empty group named <c><anno>PgName</anno></c> on the node + <c><anno>Node</anno></c>.</p> </desc> </func> <func> - <name>join(PgName, Pid) -> Members</name> + <name name="join" arity="2"/> <fsummary>Join a pid to a process group</fsummary> - <type> - <v>PgName = term()</v> - <v>Pid = pid()</v> - <v>Members = [pid()]</v> - </type> <desc> - <p>Joins the pid <c>Pid</c> to the process group <c>PgName</c>. + <p>Joins the pid <c><anno>Pid</anno></c> to the process group + <c><anno>PgName</anno></c>. Returns a list of all old members of the group.</p> </desc> </func> <func> - <name>send(PgName, Msg) -> void()</name> + <name name="send" arity="2"/> <fsummary>Send a message to all members of a process group</fsummary> - <type> - <v>PgName = Msg = term()</v> - </type> <desc> <p>Sends the tuple <c>{pg_message, From, PgName, Msg}</c> to - all members of the process group <c>PgName</c>.</p> - <p>Failure: <c>{badarg, {PgName, Msg}}</c> if <c>PgName</c> is + all members of the process group <c><anno>PgName</anno></c>.</p> + <p>Failure: <c>{badarg, {<anno>PgName</anno>, <anno>Msg</anno>}}</c> + if <c><anno>PgName</anno></c> is not a process group (a globally registered name).</p> </desc> </func> <func> - <name>esend(PgName, Msg) -> void()</name> + <name name="esend" arity="2"/> <fsummary>Send a message to all members of a process group, except ourselves</fsummary> - <type> - <v>PgName = Msg = term()</v> - </type> <desc> <p>Sends the tuple <c>{pg_message, From, PgName, Msg}</c> to - all members of the process group <c>PgName</c>, except + all members of the process group <c><anno>PgName</anno></c>, except ourselves.</p> - <p>Failure: <c>{badarg, {PgName, Msg}}</c> if <c>PgName</c> is + <p>Failure: <c>{badarg, {<anno>PgName</anno>, <anno>Msg</anno>}}</c> + if <c><anno>PgName</anno></c> is not a process group (a globally registered name).</p> </desc> </func> <func> - <name>members(PgName) -> Members</name> + <name name="members" arity="1"/> <fsummary>Return a list of all members of a process group</fsummary> - <type> - <v>PgName = term()</v> - <v>Members = [pid()]</v> - </type> <desc> <p>Returns a list of all members of the process group <c>PgName</c>.</p> diff --git a/lib/stdlib/doc/src/pool.xml b/lib/stdlib/doc/src/pool.xml index 2b890352eb..39a79e5dc5 100644 --- a/lib/stdlib/doc/src/pool.xml +++ b/lib/stdlib/doc/src/pool.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2009</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -48,23 +48,19 @@ </description> <funcs> <func> - <name>start(Name) -></name> - <name>start(Name, Args) -> Nodes</name> + <name name="start" arity="1"/> + <name name="start" arity="2"/> <fsummary>>Start a new pool</fsummary> - <type> - <v>Name = atom()</v> - <v>Args = string()</v> - <v>Nodes = [node()]</v> - </type> <desc> <p>Starts a new pool. The file <c>.hosts.erlang</c> is read to find host names where the pool nodes can be started. See section <seealso marker="#files">Files</seealso> below. The start-up procedure fails if the file is not found.</p> <p>The slave nodes are started with <c>slave:start/2,3</c>, - passing along <c>Name</c> and, if provided, <c>Args</c>. - <c>Name</c> is used as the first part of the node names, - <c>Args</c> is used to specify command line arguments. See + passing along <c><anno>Name</anno></c> and, if provided, + <c><anno>Args</anno></c>. + <c><anno>Name</anno></c> is used as the first part of the node names, + <c><anno>Args</anno></c> is used to specify command line arguments. See <seealso marker="slave#start/2">slave(3)</seealso>.</p> <p>Access rights must be set so that all nodes in the pool have the authority to access each other.</p> @@ -73,59 +69,45 @@ </desc> </func> <func> - <name>attach(Node) -> already_attached | attached</name> + <name name="attach" arity="1"/> <fsummary>Ensure that a pool master is running</fsummary> - <type> - <v>Node = node()</v> - </type> <desc> <p>This function ensures that a pool master is running and - includes <c>Node</c> in the pool master's pool of nodes.</p> + includes <c><anno>Node</anno></c> in the pool master's pool of nodes.</p> </desc> </func> <func> - <name>stop() -> stopped</name> + <name name="stop" arity="0"/> <fsummary>Stop the pool and kill all the slave nodes</fsummary> <desc> <p>Stops the pool and kills all the slave nodes.</p> </desc> </func> <func> - <name>get_nodes() -> Nodes</name> + <name name="get_nodes" arity="0"/> <fsummary>Return a list of the current member nodes of the pool</fsummary> - <type> - <v>Nodes = [node()]</v> - </type> <desc> <p>Returns a list of the current member nodes of the pool.</p> </desc> </func> <func> - <name>pspawn(Mod, Fun, Args) -> pid()</name> + <name name="pspawn" arity="3"/> <fsummary>Spawn a process on the pool node with expected lowest future load</fsummary> - <type> - <v>Mod = Fun = atom()</v> - <v>Args = [term()]</v> - </type> <desc> <p>Spawns a process on the pool node which is expected to have the lowest future load.</p> </desc> </func> <func> - <name>pspawn_link(Mod, Fun, Args) -> pid()</name> + <name name="pspawn_link" arity="3"/> <fsummary>Spawn and link to a process on the pool node with expected lowest future load</fsummary> - <type> - <v>Mod = Fun = atom()</v> - <v>Args = [term()]</v> - </type> <desc> <p>Spawn links a process on the pool node which is expected to have the lowest future load.</p> </desc> </func> <func> - <name>get_node() -> node()</name> + <name name="get_node" arity="0"/> <fsummary>Return the node with the expected lowest future load</fsummary> <desc> <p>Returns the node with the expected lowest future load.</p> diff --git a/lib/stdlib/doc/src/proc_lib.xml b/lib/stdlib/doc/src/proc_lib.xml index 791001cb52..abc17c4a91 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>2009</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -60,19 +60,33 @@ information regarding other processes which terminate as a result of this process terminating.</p> </description> + <datatypes> + <datatype> + <name name="spawn_option"/> + <desc> + <p>See <seealso marker="erts:erlang#spawn_opt/4"> + erlang:spawn_opt/2,3,4,5</seealso>.</p> + </desc> + </datatype> + <datatype> + <name name="priority_level"/> + </datatype> + <datatype> + <name name="dict_or_pid"/> + </datatype> + </datatypes> <funcs> <func> - <name>spawn(Fun) -> pid()</name> - <name>spawn(Node, Fun) -> pid()</name> - <name>spawn(Module, Function, Args) -> pid()</name> - <name>spawn(Node, Module, Function, Args) -> pid()</name> + <name name="spawn" arity="1"/> + <name name="spawn" arity="2"/> + <name name="spawn" arity="3"/> + <name name="spawn" arity="4"/> <fsummary>Spawn a new process.</fsummary> - <type> - <v>Node = node()</v> - <v>Fun = fun() -> void()</v> - <v>Module = Function = atom()</v> - <v>Args = [term()]</v> - </type> + <type variable="Node"/> + <type variable="Fun" name_i="1"/> + <type variable="Module"/> + <type variable="Function"/> + <type variable="Args"/> <desc> <p>Spawns a new process and initializes it as described above. The process is spawned using the @@ -80,17 +94,16 @@ </desc> </func> <func> - <name>spawn_link(Fun) -> pid()</name> - <name>spawn_link(Node, Fun) -> pid()</name> - <name>spawn_link(Module, Function, Args) -> pid()</name> - <name>spawn_link(Node, Module, Function, Args) -> pid()</name> + <name name="spawn_link" arity="1"/> + <name name="spawn_link" arity="2"/> + <name name="spawn_link" arity="3"/> + <name name="spawn_link" arity="4"/> <fsummary>Spawn and link to a new process.</fsummary> - <type> - <v>Node = node()</v> - <v>Fun = fun() -> void()</v> - <v>Module = Function = atom()</v> - <v>Args = [term()]</v> - </type> + <type variable="Node"/> + <type variable="Fun" name_i="1"/> + <type variable="Module"/> + <type variable="Function"/> + <type variable="Args"/> <desc> <p>Spawns a new process and initializes it as described above. The process is spawned using the @@ -99,18 +112,17 @@ </desc> </func> <func> - <name>spawn_opt(Fun, SpawnOpts) -> pid()</name> - <name>spawn_opt(Node, Fun, SpawnOpts) -> pid()</name> - <name>spawn_opt(Module, Function, Args, SpawnOpts) -> pid()</name> - <name>spawn_opt(Node, Module, Func, Args, SpawnOpts) -> pid()</name> + <name name="spawn_opt" arity="2"/> + <name name="spawn_opt" arity="3"/> + <name name="spawn_opt" arity="4"/> + <name name="spawn_opt" arity="5"/> <fsummary>Spawn a new process with given options.</fsummary> - <type> - <v>Node = node()</v> - <v>Fun = fun() -> void()</v> - <v>Module = Function = atom()</v> - <v>Args = [term()]</v> - <v>SpawnOpts -- see erlang:spawn_opt/2,3,4,5</v> - </type> + <type variable="Node"/> + <type variable="Fun" name_i="1"/> + <type variable="Module"/> + <type variable="Function"/> + <type variable="Args"/> + <type variable="SpawnOpts"/> <desc> <p>Spawns a new process and initializes it as described above. The process is spawned using the @@ -124,20 +136,13 @@ </desc> </func> <func> - <name>start(Module, Function, Args) -> Ret</name> - <name>start(Module, Function, Args, Time) -> Ret</name> - <name>start(Module, Function, Args, Time, SpawnOpts) -> Ret</name> - <name>start_link(Module, Function, Args) -> Ret</name> - <name>start_link(Module, Function, Args, Time) -> Ret</name> - <name>start_link(Module, Function, Args, Time, SpawnOpts) -> Ret</name> + <name name="start" arity="3"/> + <name name="start" arity="4"/> + <name name="start" arity="5"/> + <name name="start_link" arity="3"/> + <name name="start_link" arity="4"/> + <name name="start_link" arity="5"/> <fsummary>Start a new process synchronously.</fsummary> - <type> - <v>Module = Function = atom()</v> - <v>Args = [term()]</v> - <v>Time = int() >= 0 | infinity</v> - <v>SpawnOpts -- see erlang:spawn_opt/2,3,4,5</v> - <v>Ret = term() | {error, Reason}</v> - </type> <desc> <p>Starts a new process synchronously. Spawns the process and waits for it to start. When the process has started, it @@ -148,13 +153,13 @@ function. At this time, <c>Ret</c> is returned.</p> <p>If the <c>start_link/3,4,5</c> function is used and the process crashes before it has called <c>init_ack/1,2</c>, - <c>{error, Reason}</c> is returned if the calling process + <c>{error, <anno>Reason</anno>}</c> is returned if the calling process traps exits.</p> - <p>If <c>Time</c> is specified as an integer, this function - waits for <c>Time</c> milliseconds for the new process to call + <p>If <c><anno>Time</anno></c> is specified as an integer, this function + waits for <c><anno>Time</anno></c> milliseconds for the new process to call <c>init_ack</c>, or <c>{error, timeout}</c> is returned, and the process is killed.</p> - <p>The <c>SpawnOpts</c> argument, if given, will be passed + <p>The <c><anno>SpawnOpts</anno></c> argument, if given, will be passed as the last argument to the <c>spawn_opt/2,3,4,5</c> BIF.</p> <note> <p>Using the spawn option <c>monitor</c> is currently not @@ -164,17 +169,13 @@ </desc> </func> <func> - <name>init_ack(Parent, Ret) -> void()</name> - <name>init_ack(Ret) -> void()</name> + <name name="init_ack" arity="1"/> + <name name="init_ack" arity="2"/> <fsummary>Used by a process when it has started.</fsummary> - <type> - <v>Parent = pid()</v> - <v>Ret = term()</v> - </type> <desc> <p>This function must used by a process that has been started by a <seealso marker="#start/3">start[_link]/3,4,5</seealso> - function. It tells <c>Parent</c> that the process has + function. It tells <c><anno>Parent</anno></c> that the process has initialized itself, has started, or has failed to initialize itself.</p> <p>The <c>init_ack/1</c> function uses the parent value @@ -205,40 +206,30 @@ init(Parent) -> </desc> </func> <func> - <name>format(CrashReport) -> string()</name> + <name name="format" arity="1"/> <fsummary>Format a crash report.</fsummary> - <type> - <v>CrashReport = term()</v> - </type> <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, CrashReport)</c>. + <c>error_logger:error_report(crash_report, <anno>CrashReport</anno>)</c>. That is, the event to be handled is of the format - <c>{error_report, GL, {Pid, crash_report, CrashReport}}</c> + <c>{error_report, GL, {Pid, crash_report, <anno>CrashReport</anno>}}</c> where <c>GL</c> is the group leader pid of the process <c>Pid</c> which sent the crash report.</p> </desc> </func> <func> - <name>initial_call(Process) -> {Module,Function,Args} | false</name> + <name name="initial_call" arity="1"/> <fsummary>Extract the initial call of a <c>proc_lib</c>spawned process.</fsummary> - <type> - <v>Process = pid() | {X,Y,Z} | ProcInfo</v> - <v> X = Y = Z = int()</v> - <v> ProcInfo = term()</v> - <v>Module = Function = atom()</v> - <v>Args = [atom()]</v> - </type> <desc> <p>Extracts the initial call of a process that was started using one of the spawn or start functions described above. - <c>Process</c> can either be a pid, an integer tuple (from + <c><anno>Process</anno></c> can either be a pid, an integer tuple (from which a pid can be created), or the process information of a process <c>Pid</c> fetched through an <c>erlang:process_info(Pid)</c> function call.</p> - <note><p>The list <c>Args</c> no longer contains the actual arguments, + <note><p>The list <c><anno>Args</anno></c> no longer contains the actual arguments, but the same number of atoms as the number of arguments; the first atom is always <c>'Argument__1'</c>, the second <c>'Argument__2'</c>, and so on. The reason is that the argument list could waste a significant @@ -256,23 +247,15 @@ init(Parent) -> </desc> </func> <func> - <name>translate_initial_call(Process) -> {Module,Function,Arity} | Fun</name> + <name name="translate_initial_call" arity="1"/> <fsummary>Extract and translate the initial call of a <c>proc_lib</c>spawned process.</fsummary> - <type> - <v>Process = pid() | {X,Y,Z} | ProcInfo</v> - <v> X = Y = Z = int()</v> - <v> ProcInfo = term()</v> - <v>Module = Function = atom()</v> - <v>Arity = int()</v> - <v>Fun = fun() -> void()</v> - </type> <desc> <p>This function is used by the <c>c:i/0</c> and <c>c:regs/0</c> functions in order to present process information.</p> <p>Extracts the initial call of a process that was started using one of the spawn or start functions described above, - and translates it to more useful information. <c>Process</c> + and translates it to more useful information. <c><anno>Process</anno></c> can either be a pid, an integer tuple (from which a pid can be created), or the process information of a process <c>Pid</c> fetched through an <c>erlang:process_info(Pid)</c> @@ -280,15 +263,15 @@ init(Parent) -> <p>If the initial call is to one of the system defined behaviors such as <c>gen_server</c> or <c>gen_event</c>, it is translated to more useful information. If a <c>gen_server</c> - is spawned, the returned <c>Module</c> is the name of - the callback module and <c>Function</c> is <c>init</c> + is spawned, the returned <c><anno>Module</anno></c> is the name of + the callback module and <c><anno>Function</anno></c> is <c>init</c> (the function that initiates the new server).</p> <p>A <c>supervisor</c> and a <c>supervisor_bridge</c> are also <c>gen_server</c> processes. In order to return information that this process is a supervisor and the name of the - call-back module, <c>Module</c> is <c>supervisor</c> and - <c>Function</c> is the name of the supervisor callback - module. <c>Arity</c> is <c>1</c> since the <c>init/1</c> + call-back module, <c><anno>Module</anno></c> is <c>supervisor</c> and + <c><anno>Function</anno></c> is the name of the supervisor callback + module. <c><anno>Arity</anno></c> is <c>1</c> since the <c>init/1</c> function is called initially in the callback module.</p> <p>By default, <c>{proc_lib,init_p,5}</c> is returned if no information about the initial call can be found. It is @@ -297,12 +280,8 @@ init(Parent) -> </desc> </func> <func> - <name>hibernate(Module, Function, Args)</name> + <name name="hibernate" arity="3"/> <fsummary>Hibernate a process until a message is sent to it</fsummary> - <type> - <v>Module = Function = atom()</v> - <v>Args = [term()]</v> - </type> <desc> <p>This function does the same as (and does call) the BIF <seealso marker="erts:erlang#erlang:hibernate/3">hibernate/3</seealso>, diff --git a/lib/stdlib/doc/src/proplists.xml b/lib/stdlib/doc/src/proplists.xml index 9f1c5b24ad..225c5e97eb 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>2010</year> + <year>2002</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -48,14 +48,15 @@ words, numbers are compared literally rather than by value, so that, for instance, <c>1</c> and <c>1.0</c> are different keys.</p> </description> + <datatypes> + <datatype> + <name name="property"/> + </datatype> + </datatypes> <funcs> <func> - <name>append_values(Key, List) -> List</name> + <name name="append_values" arity="2"/> <fsummary></fsummary> - <type> - <v>Key = term()</v> - <v>List = [term()]</v> - </type> <desc> <p>Similar to <c>get_all_values/2</c>, but each value is wrapped in a list unless it is already itself a list, and the @@ -65,11 +66,8 @@ </desc> </func> <func> - <name>compact(List) -> List</name> + <name name="compact" arity="1"/> <fsummary></fsummary> - <type> - <v>List = [term()]</v> - </type> <desc> <p>Minimizes the representation of all entries in the list. This is equivalent to <c><![CDATA[[property(P) || P <- List]]]></c>.</p> @@ -77,33 +75,24 @@ </desc> </func> <func> - <name>delete(Key, List) -> List</name> + <name name="delete" arity="2"/> <fsummary></fsummary> - <type> - <v>Key = term()</v> - <v>List = [term()]</v> - </type> <desc> - <p>Deletes all entries associated with <c>Key</c> from - <c>List</c>.</p> + <p>Deletes all entries associated with <c><anno>Key</anno></c> from + <c><anno>List</anno></c>.</p> </desc> </func> <func> - <name>expand(Expansions, List) -> List</name> + <name name="expand" arity="2"/> <fsummary></fsummary> - <type> - <v>Key = term()</v> - <v>Expansions = [{Property,[term()]}]</v> - <v>Property = atom() | tuple()</v> - </type> <desc> <p>Expands particular properties to corresponding sets of - properties (or other terms). For each pair <c>{Property, Expansion}</c> in <c>Expansions</c>, if <c>E</c> is - the first entry in <c>List</c> with the same key as - <c>Property</c>, and <c>E</c> and <c>Property</c> + 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 + <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>Expansion</c>, and any following entries with - the same key are deleted from <c>List</c>.</p> + 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> <p>For example, the following expressions all return <c>[fie, bar, baz, fum]</c>:</p> <code type="none"> expand([{foo, [bar, baz]}], @@ -120,103 +109,75 @@ <p>Note that if the original property term is to be preserved in the result when expanded, it must be included in the expansion list. The inserted terms are not expanded recursively. If - <c>Expansions</c> contains more than one property with the same + <c><anno>Expansions</anno></c> contains more than one property with the same key, only the first occurrence is used.</p> <p>See also: <c>normalize/2</c>.</p> </desc> </func> <func> - <name>get_all_values(Key, List) -> [term()]</name> + <name name="get_all_values" arity="2"/> <fsummary></fsummary> - <type> - <v>Key = term()</v> - <v>List = [term()]</v> - </type> <desc> <p>Similar to <c>get_value/2</c>, but returns the list of values for <em>all</em> entries <c>{Key, Value}</c> in - <c>List</c>. If no such entry exists, the result is the empty + <c><anno>List</anno></c>. If no such entry exists, the result is the empty list.</p> <p>See also: <c>get_value/2</c>.</p> </desc> </func> <func> - <name>get_bool(Key, List) -> bool()</name> + <name name="get_bool" arity="2"/> <fsummary></fsummary> - <type> - <v>Key = term()</v> - <v>List = [term()]</v> - </type> <desc> <p>Returns the value of a boolean key/value option. If - <c>lookup(Key, List)</c> would yield <c>{Key, true}</c>, + <c>lookup(<anno>Key</anno>, <anno>List</anno>)</c> would yield <c>{<anno>Key</anno>, true}</c>, this function returns <c>true</c>; otherwise <c>false</c> is returned.</p> <p>See also: <c>get_value/2</c>, <c>lookup/2</c>.</p> </desc> </func> <func> - <name>get_keys(List) -> [term()]</name> + <name name="get_keys" arity="1"/> <fsummary></fsummary> - <type> - <v>List = [term()]</v> - </type> <desc> - <p>Returns an unordered list of the keys used in <c>List</c>, + <p>Returns an unordered list of the keys used in <c><anno>List</anno></c>, not containing duplicates.</p> </desc> </func> <func> - <name>get_value(Key, List) -> term()</name> + <name name="get_value" arity="2"/> <fsummary></fsummary> - <type> - <v>Key = term()</v> - <v>List = [term()]</v> - </type> <desc> - <p>Equivalent to <c>get_value(Key, List, undefined)</c>.</p> + <p>Equivalent to <c>get_value(<anno>Key</anno>, <anno>List</anno>, undefined)</c>.</p> </desc> </func> <func> - <name>get_value(Key, List, Default) -> term()</name> + <name name="get_value" arity="3"/> <fsummary></fsummary> - <type> - <v>Key = term()</v> - <v>Default = term()</v> - <v>List = [term()]</v> - </type> <desc> <p>Returns the value of a simple key/value property in - <c>List</c>. If <c>lookup(Key, List)</c> would yield - <c>{Key, Value}</c>, this function returns the corresponding - <c>Value</c>, otherwise <c>Default</c> is returned.</p> + <c><anno>List</anno></c>. If <c>lookup(<anno>Key</anno>, <anno>List</anno>)</c> would yield + <c>{<anno>Key</anno>, Value}</c>, this function returns the corresponding + <c>Value</c>, otherwise <c><anno>Default</anno></c> is returned.</p> <p>See also: <c>get_all_values/2</c>, <c>get_bool/2</c>, <c>get_value/2</c>, <c>lookup/2</c>.</p> </desc> </func> <func> - <name>is_defined(Key, List) -> bool()</name> + <name name="is_defined" arity="2"/> <fsummary></fsummary> - <type> - <v>Key = term()</v> - <v>List = [term()]</v> - </type> <desc> - <p>Returns <c>true</c> if <c>List</c> contains at least - one entry associated with <c>Key</c>, otherwise + <p>Returns <c>true</c> if <c><anno>List</anno></c> contains at least + one entry associated with <c><anno>Key</anno></c>, otherwise <c>false</c> is returned.</p> </desc> </func> <func> - <name>lookup(Key, List) -> none | tuple()</name> + <name name="lookup" arity="2"/> <fsummary></fsummary> - <type> - <v>Key = term()</v> - <v>List = [term()]</v> - </type> <desc> - <p>Returns the first entry associated with <c>Key</c> in - <c>List</c>, if one exists, otherwise returns + <p>Returns the first entry associated with <c><anno>Key</anno></c> in + <c><anno>List</anno></c>, if one exists, otherwise returns <c>none</c>. For an atom <c>A</c> in the list, the tuple <c>{A, true}</c> is the entry associated with <c>A</c>.</p> <p>See also: <c>get_bool/2</c>, <c>get_value/2</c>, @@ -224,34 +185,20 @@ </desc> </func> <func> - <name>lookup_all(Key, List) -> [tuple()]</name> + <name name="lookup_all" arity="2"/> <fsummary></fsummary> - <type> - <v>Key = term()</v> - <v>List = [term()]</v> - </type> <desc> - <p>Returns the list of all entries associated with <c>Key</c> - in <c>List</c>. If no such entry exists, the result is the + <p>Returns the list of all entries associated with <c><anno>Key</anno></c> + in <c><anno>List</anno></c>. If no such entry exists, the result is the empty list.</p> <p>See also: <c>lookup/2</c>.</p> </desc> </func> <func> - <name>normalize(List, Stages) -> List</name> + <name name="normalize" arity="2"/> <fsummary></fsummary> - <type> - <v>List = [term()]</v> - <v>Stages = [Operation]</v> - <v>Operation = {aliases, Aliases} | {negations, Negations} | {expand, Expansions}</v> - <v>Aliases = [{Key, Key}]</v> - <v>Negations = [{Key, Key}]</v> - <v>Key = term()</v> - <v>Expansions = [{Property, [term()]}]</v> - <v>Property = atom() | tuple()</v> - </type> <desc> - <p>Passes <c>List</c> through a sequence of + <p>Passes <c><anno>List</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, @@ -270,51 +217,37 @@ </desc> </func> <func> - <name>property(Property) -> Property</name> + <name name="property" arity="1"/> <fsummary></fsummary> - <type> - <v>Property = atom() | tuple()</v> - </type> <desc> <p>Creates a normal form (minimal) representation of a property. If - <c>Property</c> is <c>{Key, true}</c> where <c>Key</c> is + <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>Property</c> is returned.</p> + <c><anno>Property</anno></c> is returned.</p> <p>See also: <c>property/2</c>.</p> </desc> </func> <func> - <name>property(Key, Value) -> Property</name> + <name name="property" arity="2"/> <fsummary></fsummary> - <type> - <v>Key = term()</v> - <v>Value = term()</v> - <v>Property = atom() | tuple()</v> - </type> <desc> <p>Creates a normal form (minimal) representation of a simple - key/value property. Returns <c>Key</c> if <c>Value</c> is - <c>true</c> and <c>Key</c> is an atom, otherwise a tuple - <c>{Key, Value}</c> is returned.</p> + key/value property. Returns <c><anno>Key</anno></c> if <c><anno>Value</anno></c> is + <c>true</c> and <c><anno>Key</anno></c> is an atom, otherwise a tuple + <c>{<anno>Key</anno>, <anno>Value</anno>}</c> is returned.</p> <p>See also: <c>property/1</c>.</p> </desc> </func> <func> - <name>split(List, Keys) -> {Lists, Rest}</name> + <name name="split" arity="2"/> <fsummary></fsummary> - <type> - <v>List = [term()]</v> - <v>Keys = [term()]</v> - <v>Lists = [[term()]]</v> - <v>Rest = [term()]</v> - </type> <desc> - <p>Partitions <c>List</c> into a list of sublists and a - remainder. <c>Lists</c> contains one sublist for each key in - <c>Keys</c>, in the corresponding order. The relative order of + <p>Partitions <c><anno>List</anno></c> into a list of sublists and a + remainder. <c><anno>Lists</anno></c> contains one sublist for each key in + <c><anno>Keys</anno></c>, in the corresponding order. The relative order of the elements in each sublist is preserved from the original - <c>List</c>. <c>Rest</c> contains the elements in - <c>List</c> that are not associated with any of the given keys, + <c><anno>List</anno></c>. <c><anno>Rest</anno></c> contains the elements in + <c><anno>List</anno></c> that are not associated with any of the given keys, also with their original relative order preserved.</p> <p>Example: split([{c, 2}, {e, 1}, a, {c, 3, 4}, d, {b, 5}, b], [a, b, c])</p> @@ -323,19 +256,14 @@ </desc> </func> <func> - <name>substitute_aliases(Aliases, List) -> List</name> + <name name="substitute_aliases" arity="2"/> <fsummary></fsummary> - <type> - <v>Aliases = [{Key, Key}]</v> - <v>Key = term()</v> - <v>List = [term()]</v> - </type> <desc> <p>Substitutes keys of properties. For each entry in - <c>List</c>, if it is associated with some key <c>K1</c> - such that <c>{K1, K2}</c> occurs in <c>Aliases</c>, the - key of the entry is changed to <c>Key2</c>. If the same - <c>K1</c> occurs more than once in <c>Aliases</c>, only + <c><anno>List</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 the first occurrence is used.</p> <p>Example: <c>substitute_aliases([{color, colour}], L)</c> will replace all tuples <c>{color, ...}</c> in <c>L</c> @@ -345,24 +273,19 @@ </desc> </func> <func> - <name>substitute_negations(Negations, List) -> List</name> + <name name="substitute_negations" arity="2"/> <fsummary></fsummary> - <type> - <v>Negations = [{Key, Key}]</v> - <v>Key = term()</v> - <v>List = [term()]</v> - </type> <desc> <p>Substitutes keys of boolean-valued properties and simultaneously negates their values. For each entry in - <c>List</c>, if it is associated with some key <c>K1</c> - such that <c>{K1, K2}</c> occurs in <c>Negations</c>, then + <c><anno>List</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 - than once in <c>Negations</c>, only the first occurrence is + 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> will replace any atom <c>no_foo</c> or tuple @@ -374,13 +297,10 @@ </desc> </func> <func> - <name>unfold(List) -> List</name> + <name name="unfold" arity="1"/> <fsummary></fsummary> - <type> - <v>List = [term()]</v> - </type> <desc> - <p>Unfolds all occurrences of atoms in <c>List</c> to tuples + <p>Unfolds all occurrences of atoms in <c><anno>List</anno></c> to tuples <c>{Atom, true}</c>.</p> </desc> </func> diff --git a/lib/stdlib/doc/src/qlc.xml b/lib/stdlib/doc/src/qlc.xml index da24ee9914..6a45ade447 100644 --- a/lib/stdlib/doc/src/qlc.xml +++ b/lib/stdlib/doc/src/qlc.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2004</year><year>2009</year> + <year>2004</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -45,7 +45,9 @@ tables</em>. Typical QLC tables are ETS, Dets, and Mnesia tables. There is also support for user defined tables, see the <seealso marker="#implementing_a_qlc_table">Implementing a QLC - table</seealso> section. A <em>query</em> is stated using + table</seealso> section. <marker + id="query_list_comprehension"></marker> + A <em>query</em> is stated using <em>Query List Comprehensions</em> (QLCs). The answers to a query are determined by data in QLC tables that fulfill the constraints expressed by the QLCs of the query. QLCs are similar @@ -55,10 +57,11 @@ fact, in the absence of optimizations and options such as <c>cache</c> and <c>unique</c> (see below), every QLC free of QLC tables evaluates to the same list of answers as the - identical ordinary list comprehension. </p> + identical ordinary list comprehension.</p> <p>While ordinary list comprehensions evaluate to lists, calling - <seealso marker="#q">qlc:q/1,2</seealso> returns a <em>Query + <seealso marker="#q">qlc:q/1,2</seealso> returns a <marker + id="query_handle"></marker><em> Query Handle</em>. To obtain all the answers to a query, <seealso marker="#eval">qlc:eval/1,2</seealso> should be called with the query handle as first argument. Query handles are essentially @@ -69,7 +72,8 @@ Code replacement is described in the <seealso marker="doc/reference_manual:code_loading">Erlang Reference Manual</seealso>. The list of answers can also be traversed in - chunks by use of a <em>Query Cursor</em>. Query cursors are + chunks by use of a <marker + id="query_cursor"></marker><em>Query Cursor</em>. Query cursors are created by calling <seealso marker="#cursor">qlc:cursor/1,2</seealso> with a query handle as first argument. Query cursors are essentially Erlang processes. @@ -226,75 +230,6 @@ </section> - <section><title>Common data types</title> - - <list type="bulleted"> - <item><p><c>QueryCursor = {qlc_cursor, term()}</c></p> - </item> - <item><p><c>QueryHandle = {qlc_handle, term()}</c></p> - </item> - <item><p><c>QueryHandleOrList = QueryHandle | list()</c></p> - </item> - <item><p><c>Answers = [Answer]</c></p> - </item> - <item><p><c>Answer = term()</c></p> - </item> - <item><p><c>AbstractExpression = </c> - parse trees - for Erlang expressions, see the <seealso - marker="erts:absform">abstract format</seealso> - documentation in the ERTS User's Guide -</p> - </item> - <item><p><c>MatchExpression = </c> - - match specifications, see the <seealso - marker="erts:match_spec">match specification</seealso> - documentation in the ERTS User's Guide and <seealso - marker="ms_transform">ms_transform(3)</seealso> -</p> - </item> - <item><p><c>SpawnOptions = default | spawn_options()</c></p> - </item> - <item><p><c>SortOptions = [SortOption] | SortOption</c></p> - </item> - <item><p><c>SortOption = {compressed, bool()} - | {no_files, NoFiles} - | {order, Order} - | {size, Size} - | {tmpdir, TempDirectory} - | {unique, bool()} </c> - - see <seealso - marker="file_sorter">file_sorter(3)</seealso> -</p> - </item> - <item><p><c>Order = ascending | descending | OrderFun</c></p> - </item> - <item><p><c>OrderFun = fun(term(), term()) -> bool()</c></p> - </item> - <item><p><c>TempDirectory = "" | filename()</c></p> - </item> - <item><p><c>Size = int() > 0</c></p> - </item> - <item><p><c>NoFiles = int() > 1</c></p> - </item> - <item><p><c>KeyPos = int() > 0 | [int() > 0]</c></p> - </item> - <item><p><c>MaxListSize = int() >= 0</c></p> - </item> - <item><p><c>bool() = true | false</c></p> - </item> - <item><p><c>Cache = ets | list | no</c></p> - </item> - <item><p><c>TmpFileUsage = allowed | not_allowed | info_msg - | warning_msg | error_msg</c></p> - </item> - <item><p><c>filename() = </c> - see <seealso - marker="filename">filename(3)</seealso> -</p> - </item> - <item><p><c>spawn_options() = </c> - see <seealso - marker="erts:erlang">erlang(3)</seealso> -</p> - </item> - - </list> - - </section> - <section><title>Getting started</title> <p><marker id="getting_started"></marker> As already mentioned @@ -679,34 +614,105 @@ ets:match_spec_run(ets:lookup(86033, {2,2}), </section> + <datatypes> + <datatype> + <name name="abstract_expr"></name> + <desc><p>Parse trees for Erlang expression, see the <seealso + marker="erts:absform">abstract format</seealso> + documentation in the ERTS User's Guide.</p></desc> + </datatype> + <datatype> + <name name="answer"></name> + </datatype> + <datatype> + <name name="answers"></name> + </datatype> + <datatype> + <name name="cache"></name> + </datatype> + <datatype> + <name name="match_expression"></name> + <desc><p>Match specification, see the <seealso + marker="erts:match_spec">match specification</seealso> + documentation in the ERTS User's Guide and <seealso + marker="ms_transform">ms_transform(3).</seealso></p></desc> + </datatype> + <datatype> + <name name="no_files"></name> + <desc><p>Actually an integer > 1.</p></desc> + </datatype> + <datatype> + <name name="key_pos"></name> + </datatype> + <datatype> + <name name="max_list_size"></name> + </datatype> + <datatype> + <name name="order"></name> + </datatype> + <datatype> + <name name="order_fun"></name> + </datatype> + <datatype> + <name name="query_cursor"></name> + <desc><p>A <seealso marker="#query_cursor">query cursor</seealso>.</p> + </desc> + </datatype> + <datatype> + <name name="query_handle"></name> + <desc><p>A <seealso marker="#query_handle">query handle</seealso>.</p> + </desc> + </datatype> + <datatype> + <name name="query_handle_or_list"></name> + </datatype> + <datatype> + <name name="query_list_comprehension"></name> + <desc><p>A literal + <seealso marker="#query_list_comprehension">query + list comprehension</seealso>.</p></desc> + </datatype> + <datatype> + <name name="spawn_options"></name> + </datatype> + <datatype> + <name name="sort_options"></name> + </datatype> + <datatype> + <name name="sort_option"></name> + <desc><p>See <seealso + marker="file_sorter">file_sorter(3)</seealso>.</p></desc> + </datatype> + <datatype> + <name name="tmp_directory"></name> + </datatype> + <datatype> + <name name="tmp_file_usage"></name> + <desc><p></p></desc> + </datatype> + </datatypes> + <funcs> <func> - <name>append(QHL) -> QH</name> + <name name="append" arity="1"/> <fsummary>Return a query handle.</fsummary> - <type> - <v>QHL = [QueryHandleOrList]</v> - <v>QH = QueryHandle</v> - </type> <desc> <p>Returns a query handle. When evaluating the query handle - <c>QH</c> all answers to the first query handle in - <c>QHL</c> is returned followed by all answers to the rest - of the query handles in <c>QHL</c>.</p> + <c><anno>QH</anno></c> all answers to the first query handle in + <c><anno>QHL</anno></c> are returned followed by all answers + to the rest of the query handles in <c><anno>QHL</anno></c>.</p> </desc> </func> <func> - <name>append(QH1, QH2) -> QH3</name> + <name name="append" arity="2"/> <fsummary>Return a query handle.</fsummary> - <type> - <v>QH1 = QH2 = QueryHandleOrList</v> - <v>QH3 = QueryHandle</v> - </type> <desc> <p>Returns a query handle. When evaluating the query handle - <c>QH3</c> all answers to <c>QH1</c> are returned followed - by all answers to <c>QH2</c>.</p> + <c><anno>QH3</anno></c> all answers to + <c><anno>QH1</anno></c> are returned followed by all answers + to <c><anno>QH2</anno></c>.</p> <p><c>append(QH1, QH2)</c> is equivalent to <c>append([QH1, QH2])</c>.</p> @@ -714,17 +720,9 @@ ets:match_spec_run(ets:lookup(86033, {2,2}), </func> <func> - <name>cursor(QueryHandleOrList [, Options]) -> QueryCursor</name> + <name name="cursor" arity="1"/> + <name name="cursor" arity="2"/> <fsummary>Create a query cursor.</fsummary> - <type> - <v>Options = [Option] | Option</v> - <v>Option = {cache_all, Cache} | cache_all - | {max_list_size, MaxListSize} - | {spawn_options, SpawnOptions} - | {tmpdir_usage, TmpFileUsage} - | {tmpdir, TempDirectory} - | {unique_all, bool()} | unique_all</v> - </type> <desc> <p><marker id="cursor"></marker>Creates a query cursor and makes the calling process the owner of the cursor. The @@ -746,11 +744,13 @@ ets:match_spec_run(ets:lookup(86033, {2,2}), [{b,1},{b,2}] 4> <input>qlc:delete_cursor(QC).</input> ok</pre> + <p><c>cursor(<anno>QH</anno>)</c> is equivalent to + <c>cursor(<anno>QH</anno>, [])</c>.</p> </desc> </func> <func> - <name>delete_cursor(QueryCursor) -> ok</name> + <name name="delete_cursor" arity="1"/> <fsummary>Delete a query cursor.</fsummary> <desc> <p>Deletes a query cursor. Only the owner of the cursor can @@ -759,19 +759,11 @@ ok</pre> </func> <func> - <name>eval(QueryHandleOrList [, Options]) -> Answers | Error</name> - <name>e(QueryHandleOrList [, Options]) -> Answers</name> + <name name="eval" arity="1"/> + <name name="eval" arity="2"/> + <name name="e" arity="1"/> + <name name="e" arity="2"/> <fsummary>Return all answers to a query.</fsummary> - <type> - <v>Options = [Option] | Option</v> - <v>Option = {cache_all, Cache} | cache_all - | {max_list_size, MaxListSize} - | {tmpdir_usage, TmpFileUsage} - | {tmpdir, TempDirectory} - | {unique_all, bool()} | unique_all</v> - <v>Error = {error, module(), Reason}</v> - <v>Reason = - as returned by file_sorter(3) -</v> - </type> <desc> <p><marker id="eval"></marker>Evaluates a query handle in the calling process and collects all answers in a list.</p> @@ -780,47 +772,39 @@ ok</pre> 1> <input>QH = qlc:q([{X,Y} || X <- [a,b], Y <- [1,2]]),</input> <input>qlc:eval(QH).</input> [{a,1},{a,2},{b,1},{b,2}]</pre> + <p><c>eval(<anno>QH</anno>)</c> is equivalent to + <c>eval(<anno>QH</anno>, [])</c>.</p> </desc> </func> <func> - <name>fold(Function, Acc0, QueryHandleOrList [, Options]) -> - Acc1 | Error</name> + <name name="fold" arity="3"/> + <name name="fold" arity="4"/> <fsummary>Fold a function over the answers to a query.</fsummary> - <type> - <v>Function = fun(Answer, AccIn) -> AccOut</v> - <v>Acc0 = Acc1 = AccIn = AccOut = term()</v> - <v>Options = [Option] | Option</v> - <v>Option = {cache_all, Cache} | cache_all - | {max_list_size, MaxListSize} - | {tmpdir_usage, TmpFileUsage} - | {tmpdir, TempDirectory} - | {unique_all, bool()} | unique_all</v> - <v>Error = {error, module(), Reason}</v> - <v>Reason = - as returned by file_sorter(3) -</v> - </type> <desc> - <p>Calls <c>Function</c> on successive answers to the query - handle together with an extra argument <c>AccIn</c>. The - query handle and the function are evaluated in the calling - process. <c>Function</c> must return a new accumulator which - is passed to the next call. <c>Acc0</c> is returned if there - are no answers to the query handle.</p> + <p>Calls <c><anno>Function</anno></c> on successive answers to + the query handle together with an extra argument + <c><anno>AccIn</anno></c>. The query handle and the function + are evaluated in the calling process. + <c><anno>Function</anno></c> must return a new accumulator + which is passed to the next call. + <c><anno>Acc0</anno></c> is returned if there are no answers + to the query handle.</p> <pre> 1> <input>QH = [1,2,3,4,5,6],</input> <input>qlc:fold(fun(X, Sum) -> X + Sum end, 0, QH).</input> 21</pre> + <p><c>fold(<anno>Function</anno>, <anno>Acc0</anno>, + <anno>QH</anno>)</c> is equivalent to + <c>fold(<anno>Function</anno>, <anno>Acc0</anno>, + <anno>QH</anno>, [])</c>.</p> </desc> </func> <func> - <name>format_error(Error) -> Chars</name> + <name name="format_error" arity="1"/> <fsummary>Return an English description of a an error tuple.</fsummary> - <type> - <v>Error = {error, module(), term()}</v> - <v>Chars = [char() | Chars]</v> - </type> <desc> <p>Returns a descriptive string in English of an error tuple returned by some of the functions of the <c>qlc</c> module @@ -830,25 +814,9 @@ ok</pre> </func> <func> - <name>info(QueryHandleOrList [, Options]) -> Info</name> + <name name="info" arity="1"/> + <name name="info" arity="2"/> <fsummary>Return code describing a query handle.</fsummary> - <type> - <v>Options = [Option] | Option</v> - <v>Option = EvalOption | ReturnOption</v> - <v>EvalOption = {cache_all, Cache} | cache_all - | {max_list_size, MaxListSize} - | {tmpdir_usage, TmpFileUsage} - | {tmpdir, TempDirectory} - | {unique_all, bool()} | unique_all</v> - <v>ReturnOption = {depth, Depth} - | {flat, bool()} - | {format, Format} - | {n_elements, NElements}</v> - <v>Depth = infinity | int() >= 0</v> - <v>Format = abstract_code | string</v> - <v>NElements = infinity | int() > 0</v> - <v>Info = AbstractExpression | string()</v> - </type> <desc> <p><marker id="info"></marker>Returns information about a query handle. The information describes the simplifications @@ -879,18 +847,18 @@ ok</pre> <input>io:format("~s~n", [qlc:info(QH, unique_all)]).</input> begin V1 = - qlc:q([ + qlc:q([ SQV || SQV <- [x,y] ], [{unique,true}]), V2 = - qlc:q([ + qlc:q([ SQV || SQV <- [a,b] ], [{unique,true}]), - qlc:q([ + qlc:q([ {X,Y} || X <- V1, Y <- V2 @@ -913,19 +881,19 @@ end</pre> <input>io:format("~s~n", [qlc:info(Q)]).</input> begin V1 = - qlc:q([ + qlc:q([ P0 || P0 = {W,Y} <- ets:table(17) ]), V2 = - qlc:q([ + qlc:q([ [G1|G2] || G2 <- V1, G1 <- ets:table(16), element(2, G1) =:= element(1, G2) ], [{join,lookup}]), - qlc:q([ + qlc:q([ {X,Z,W} || [{X,Z}|{W,Y}] <- V2 ]) @@ -936,44 +904,43 @@ end</pre> method chosen. A convention is used for lookup join: the first generator (<c>G2</c>) is the one traversed, the second one (<c>G1</c>) is the table where constants are looked up.</p> + + <p><c>info(<anno>QH</anno>)</c> is equivalent to + <c>info(<anno>QH</anno>, [])</c>.</p> </desc> </func> <func> - <name>keysort(KeyPos, QH1 [, SortOptions]) -> QH2</name> + <name name="keysort" arity="2"/> + <name name="keysort" arity="3"/> <fsummary>Return a query handle.</fsummary> - <type> - <v>QH1 = QueryHandleOrList</v> - <v>QH2 = QueryHandle</v> - </type> <desc> <p>Returns a query handle. When evaluating the query handle - <c>QH2</c> the answers to the query handle <c>QH1</c> are - sorted by <seealso + <c><anno>QH2</anno></c> the answers to the query handle + <c><anno>QH1</anno></c> are sorted by <seealso marker="file_sorter">file_sorter:keysort/4</seealso> according to the options.</p> - <p>The sorter will use temporary files only if <c>QH1</c> does - not evaluate to a list and the size of the binary - representation of the answers exceeds <c>Size</c> bytes, - where <c>Size</c> is the value of the <c>size</c> option.</p> + <p>The sorter will use temporary files only if + <c><anno>QH1</anno></c> does not evaluate to a list and the + size of the binary representation of the answers exceeds + <c>Size</c> bytes, where <c>Size</c> is the value of the + <c>size</c> option.</p> + + <p><c>keysort(<anno>KeyPos</anno>, <anno>QH1</anno>)</c> + is equivalent to + <c>keysort(<anno>KeyPos</anno>, <anno>QH1</anno>, [])</c>.</p> </desc> </func> <func> - <name>next_answers(QueryCursor [, NumberOfAnswers]) -> - Answers | Error</name> + <name name="next_answers" arity="1"/> + <name name="next_answers" arity="2"/> <fsummary>Return some or all answers to a query.</fsummary> - <type> - <v>NumberOfAnswers = all_remaining | int() > 0</v> - <v>Error = {error, module(), Reason}</v> - <v>Reason = - as returned by file_sorter(3) -</v> - </type> <desc> <p>Returns some or all of the remaining answers to a query - cursor. Only the owner of <c>Cursor</c> can retrieve - answers.</p> - + cursor. Only the owner of <c><anno>QueryCursor</anno></c> can + retrieve answers.</p> <p>The optional argument <c>NumberOfAnswers</c>determines the maximum number of answers returned. The default value is <c>10</c>. If less than the requested number of answers is @@ -983,21 +950,9 @@ end</pre> </func> <func> - <name>q(QueryListComprehension [, Options]) -> QueryHandle</name> + <name name="q" arity="1"/> + <name name="q" arity="2"/> <fsummary>Return a handle for a query list comprehension.</fsummary> - <type> - <v>QueryListComprehension = - - literal query listcomprehension -</v> - <v>Options = [Option] | Option</v> - <v>Option = {max_lookup, MaxLookup} - | {cache, Cache} | cache - | {join, Join} - | {lookup, Lookup} - | {unique, bool()} | unique</v> - <v>MaxLookup = int() >= 0 | infinity</v> - <v>Join = any | lookup | merge | nested_loop</v> - <v>Lookup = bool() | any</v> - </type> <desc> <p><marker id="q"></marker>Returns a query handle for a query list comprehension. The query list comprehension must be the @@ -1024,7 +979,7 @@ end</pre> <pre> ... -A = [X || {X} <- [{1},{2}]], +A = [X || {X} <- [{1},{2}]], QH = qlc:q(A), ...</pre> @@ -1034,6 +989,9 @@ QH = qlc:q(A), list comprehension"); the shell process stops with a <c>badarg</c> reason.</p> + <p><c>q(<anno>QLC</anno>)</c> is equivalent to + <c>q(<anno>QLC</anno>, [])</c>.</p> + <p>The <c>{cache, ets}</c> option can be used to cache the answers to a query list comprehension. The answers are stored in one ETS table for each cached query list @@ -1092,26 +1050,26 @@ QH = qlc:q(A), <input>io:format("~s~n", [qlc:info(Q)]).</input> begin V1 = - qlc:q([ + qlc:q([ P0 || P0 = {X,Z} <- qlc:keysort(1, [{a,1},{b,4},{c,6}], []) ]), V2 = - qlc:q([ + qlc:q([ P0 || P0 = {W,Y} <- qlc:keysort(2, [{2,a},{3,b},{4,c}], []) ]), V3 = - qlc:q([ + qlc:q([ [G1|G2] || G1 <- V1, G2 <- V2, element(1, G1) == element(2, G2) ], [{join,merge},{cache,list}]), - qlc:q([ + qlc:q([ {A,X,Z,W} || A <- [a,b,c], [{X,Z}|{W,Y}] <- V3, @@ -1170,7 +1128,7 @@ ets:match_spec_run( elements of the key {X, Y} are compared separately.</p> <p>The <c>{lookup, true}</c> option can be used to ensure - that the <c>qlc</c> module will look up constants in some + that the <c>qlc</c> module will look up constants in some QLC table. If there are more than one QLC table among the generators' list expressions, constants have to be looked up in at least one @@ -1190,7 +1148,7 @@ ets:match_spec_run( <c>{join, nested_loop}</c> invokes the method of matching every pair of objects from two handles. The last method is mostly very slow. The evaluation of the query - fails if the <c>qlc</c> module cannot carry out the chosen + fails if the <c>qlc</c> module cannot carry out the chosen join method. The default value is <c>any</c> which means that some fast join method will be used if possible.</p> @@ -1198,47 +1156,33 @@ ets:match_spec_run( </func> <func> - <name>sort(QH1 [, SortOptions]) -> QH2</name> + <name name="sort" arity="1"/> + <name name="sort" arity="2"/> <fsummary>Return a query handle.</fsummary> - <type> - <v>QH1 = QueryHandleOrList</v> - <v>QH2 = QueryHandle</v> - </type> <desc> <p>Returns a query handle. When evaluating the query handle - <c>QH2</c> the answers to the query handle <c>QH1</c> are - sorted by <seealso + <c><anno>QH2</anno></c> the answers to the query handle + <c><anno>QH1</anno></c> are sorted by <seealso marker="file_sorter">file_sorter:sort/3</seealso> according to the options.</p> - <p>The sorter will use temporary files only if <c>QH1</c> does - not evaluate to a list and the size of the binary - representation of the answers exceeds <c>Size</c> bytes, - where <c>Size</c> is the value of the <c>size</c> option.</p> + <p>The sorter will use temporary files only if + <c><anno>QH1</anno></c> does not evaluate to a list and the + size of the binary representation of the answers exceeds + <c>Size</c> bytes, where <c>Size</c> is the value of the + <c>size</c> option.</p> + + <p><c>sort(<anno>QH1</anno>)</c> is equivalent to + <c>sort(<anno>QH1</anno>, [])</c>.</p> + </desc> </func> <func> - <name>string_to_handle(QueryString [, Options [, Bindings]]) -> - QueryHandle | Error</name> + <name name="string_to_handle" arity="1"/> + <name name="string_to_handle" arity="2"/> + <name name="string_to_handle" arity="3"/> <fsummary>Return a handle for a query list comprehension.</fsummary> - <type> - <v>QueryString = string()</v> - <v>Options = [Option] | Option</v> - <v>Option = {max_lookup, MaxLookup} - | {cache, Cache} | cache - | {join, Join} - | {lookup, Lookup} - | {unique, bool()} | unique</v> - <v>MaxLookup = int() >= 0 | infinity</v> - <v>Join = any | lookup | merge | nested_loop</v> - <v>Lookup = bool() | any</v> - <v>Bindings = - as returned by - erl_eval:bindings/1 -</v> - <v>Error = {error, module(), Reason}</v> - <v>Reason = - ErrorInfo as returned by - erl_scan:string/1 or erl_parse:parse_exprs/1 -</v> - </type> <desc> <p>A string version of <c>qlc:q/1,2</c>. When the query handle is evaluated the fun created by the parse transform is @@ -1253,57 +1197,24 @@ ets:match_spec_run( <input>qlc:eval(QH).</input> [2,3,4]</pre> + <p><c>string_to_handle(<anno>QueryString</anno>)</c> + is equivalent to + <c>string_to_handle(<anno>QueryString</anno>, [])</c>.</p> + + <p><c>string_to_handle(<anno>QueryString</anno>, + <anno>Options</anno>)</c> + is equivalent to + <c>string_to_handle(<anno>QueryString</anno>, + <anno>Options</anno>, erl_eval:new_bindings())</c>.</p> + <p>This function is probably useful mostly when called from outside of Erlang, for instance from a driver written in C.</p> </desc> </func> <func> - <name>table(TraverseFun, Options) -> QueryHandle</name> + <name name="table" arity="2"/> <fsummary>Return a query handle for a table.</fsummary> - <type> - <v>TraverseFun = TraverseFun0 | TraverseFun1</v> - <v>TraverseFun0 = fun() -> TraverseResult</v> - <v>TraverseFun1 = fun(MatchExpression) -> TraverseResult</v> - <v>TraverseResult = Objects | term()</v> - <v>Objects = [] | [term() | ObjectList]</v> - <v>ObjectList = TraverseFun0 | Objects</v> - <v>Options = [Option] | Option</v> - <v>Option = {format_fun, FormatFun} - | {info_fun, InfoFun} - | {lookup_fun, LookupFun} - | {parent_fun, ParentFun} - | {post_fun, PostFun} - | {pre_fun, PreFun} - | {key_equality, KeyComparison}</v> - <v>FormatFun = undefined | fun(SelectedObjects) -> FormatedTable</v> - <v>SelectedObjects = all - | {all, NElements, DepthFun} - | {match_spec, MatchExpression} - | {lookup, Position, Keys} - | {lookup, Position, Keys, NElements, DepthFun}</v> - <v>NElements = infinity | int() > 0</v> - <v>DepthFun = fun(term()) -> term()</v> - <v>FormatedTable = {Mod, Fun, Args} - | AbstractExpression - | character_list()</v> - <v>InfoFun = undefined | fun(InfoTag) -> InfoValue</v> - <v>InfoTag = indices | is_unique_objects | keypos | num_of_objects</v> - <v>InfoValue = undefined | term()</v> - <v>LookupFun = undefined | fun(Position, Keys) -> LookupResult</v> - <v>LookupResult = [term()] | term()</v> - <v>ParentFun = undefined | fun() -> ParentFunValue</v> - <v>PostFun = undefined | fun() -> void()</v> - <v>PreFun = undefined | fun([PreArg]) -> void()</v> - <v>PreArg = {parent_value, ParentFunValue} | {stop_fun, StopFun}</v> - <v>ParentFunValue = undefined | term()</v> - <v>StopFun = undefined | fun() -> void()</v> - <v>KeyComparison = '=:=' | '=='</v> - <v>Position = int() > 0</v> - <v>Keys = [term()]</v> - <v>Mod = Fun = atom()</v> - <v>Args = [term()]</v> - </type> <desc> <p><marker id="table"></marker>Returns a query handle for a QLC table. In Erlang/OTP there is support for ETS, Dets and @@ -1315,77 +1226,90 @@ ets:match_spec_run( as well as properties of the table are handled by callback functions provided as options to <c>qlc:table/2</c>.</p> - <p>The callback function <c>TraverseFun</c> is used for - traversing the table. It is to return a list of objects - terminated by either <c>[]</c> or a nullary fun to be used - for traversing the not yet traversed objects of the table. - Any other return value is immediately returned as value of - the query evaluation. Unary <c>TraverseFun</c>s are to - accept a match specification as argument. The match - specification is created by the parse transform by analyzing - the pattern of the generator calling <c>qlc:table/2</c> and - filters using variables introduced in the pattern. If the - parse transform cannot find a match specification equivalent - to the pattern and filters, <c>TraverseFun</c> will be - called with a match specification returning every object. - Modules that can utilize match specifications for optimized + <p>The callback function <c><anno>TraverseFun</anno></c> is + used for traversing the table. It is to return a list of + objects terminated by either <c>[]</c> or a nullary fun to + be used for traversing the not yet traversed objects of the + table. Any other return value is immediately returned as + value of the query evaluation. Unary + <c><anno>TraverseFun</anno></c>s are to accept a match + specification as argument. The match specification is + created by the parse transform by analyzing the pattern of + the generator calling <c>qlc:table/2</c> and filters using + variables introduced in the pattern. If the parse transform + cannot find a match specification equivalent to the pattern + and filters, <c><anno>TraverseFun</anno></c> will be called + with a match specification returning every object. Modules + that can utilize match specifications for optimized traversal of tables should call <c>qlc:table/2</c> with a - unary <c>TraverseFun</c> while other modules can provide a - nullary <c>TraverseFun</c>. <c>ets:table/2</c> is an example - of the former; <c>gb_table:table/1</c> in the <seealso - marker="#implementing_a_qlc_table">Implementing a QLC - table</seealso> section is an example of the latter.</p> - - <p><c>PreFun</c> is a unary callback function that is called - once before the table is read for the first time. If the - call fails, the query evaluation fails. Similarly, the - nullary callback function <c>PostFun</c> is called once - after the table was last read. The return value, which is - caught, is ignored. If <c>PreFun</c> has been called for a - table, <c>PostFun</c> is guaranteed to be called for that - table, even if the evaluation of the query fails for some - reason. The order in which pre (post) functions for + unary + <c><anno>TraverseFun</anno></c> while other modules can + provide a nullary + <c><anno>TraverseFun</anno></c>. <c>ets:table/2</c> is an + example of the former; <c>gb_table:table/1</c> in the + <seealso marker="#implementing_a_qlc_table">Implementing a + QLC table</seealso> section is an example of the latter.</p> + + <p><c><anno>PreFun</anno></c> is a unary callback function + that is called once before the table is read for the first + time. If the call fails, the query evaluation fails. + Similarly, the nullary callback function + <c><anno>PostFun</anno></c> is called once after the table + was last read. The return value, which is caught, is + ignored. If <c><anno>PreFun</anno></c> has been called for a + table, + <c><anno>PostFun</anno></c> is guaranteed to be called for + that table, even if the evaluation of the query fails for + some reason. The order in which pre (post) functions for different tables are evaluated is not specified. Other table - access than reading, such as calling <c>InfoFun</c>, is - assumed to be OK at any time. The argument <c>PreArgs</c> is - a list of tagged values. Currently there are two tags, + access than reading, such as calling + <c><anno>InfoFun</anno></c>, is assumed to be OK at any + time. The argument <c><anno>PreArgs</anno></c> is a list of + tagged values. Currently there are two tags, <c>parent_value</c> and <c>stop_fun</c>, used by Mnesia for managing transactions. The value of <c>parent_value</c> is - the value returned by <c>ParentFun</c>, or <c>undefined</c> - if there is no <c>ParentFun</c>. <c>ParentFun</c> is called - once just before the call of <c>PreFun</c> in the context of - the process calling <c>eval</c>, <c>fold</c>, or + the value returned by <c><anno>ParentFun</anno></c>, or + <c>undefined</c> if there is no <c>ParentFun</c>. + <c><anno>ParentFun</anno></c> is called once just before the + call of + <c><anno>PreFun</anno></c> in the context of the process + calling + <c>eval</c>, <c>fold</c>, or <c>cursor</c>. The value of <c>stop_fun</c> is a nullary fun that deletes the cursor if called from the parent, or <c>undefined</c> if there is no cursor.</p> <p><marker id="lookup_fun"></marker>The binary callback - function <c>LookupFun</c> is used for looking up objects in - the table. The first argument <c>Position</c> is the key - position or an indexed position and the second argument - <c>Keys</c> is a sorted list of unique values. The return - value is to be a list of all objects (tuples) such that the - element at <c>Position</c> is a member of <c>Keys</c>. Any - other return value is immediately returned as value of the - query evaluation. <c>LookupFun</c> is called instead of + function <c><anno>LookupFun</anno></c> is used for looking + up objects in the table. The first argument + <c><anno>Position</anno></c> is the key position or an + indexed position and the second argument + <c><anno>Keys</anno></c> is a sorted list of unique values. + The return value is to be a list of all objects (tuples) + such that the element at <c>Position</c> is a member of + <c><anno>Keys</anno></c>. Any other return value is + immediately returned as value of the query evaluation. + <c><anno>LookupFun</anno></c> is called instead of traversing the table if the parse transform at compile time can find out that the filters match and compare the element - at <c>Position</c> in such a way that only <c>Keys</c> need - to be looked up in order to find all potential answers. The - key position is obtained by calling <c>InfoFun(keypos)</c> - and the indexed positions by calling - <c>InfoFun(indices)</c>. If the key position can be used for - lookup it is always chosen, otherwise the indexed position - requiring the least number of lookups is chosen. If there is - a tie between two indexed positions the one occurring first - in the list returned by <c>InfoFun</c> is chosen. Positions - requiring more than <seealso - marker="#max_lookup">max_lookup</seealso> lookups are - ignored.</p> - - <p>The unary callback function <c>InfoFun</c> is to return - information about the table. <c>undefined</c> should be - returned if the value of some tag is unknown:</p> + at <c><anno>Position</anno></c> in such a way that only + <c><anno>Keys</anno></c> need to be looked up in order to + find all potential answers. The key position is obtained by + calling + <c><anno>InfoFun</anno>(keypos)</c> and the indexed + positions by calling + <c><anno>InfoFun</anno>(indices)</c>. If the key position + can be used for lookup it is always chosen, otherwise the + indexed position requiring the least number of lookups is + chosen. If there is a tie between two indexed positions the + one occurring first in the list returned by + <c><anno>InfoFun</anno></c> is chosen. Positions requiring + more than <seealso marker="#max_lookup">max_lookup</seealso> + lookups are ignored.</p> + + <p>The unary callback function <c><anno>InfoFun</anno></c> is + to return information about the table. <c>undefined</c> + should be returned if the value of some tag is unknown:</p> <list type="bulleted"> <item><c>indices</c>. Returns a list of indexed @@ -1406,20 +1330,22 @@ ets:match_spec_run( </item> </list> - <p>The unary callback function <c>FormatFun</c> is used by - <seealso marker="#info">qlc:info/1,2</seealso> for - displaying the call that created the table's query handle. - The default value, <c>undefined</c>, means that + <p>The unary callback function <c><anno>FormatFun</anno></c> + is used by <seealso marker="#info">qlc:info/1,2</seealso> + for displaying the call that created the table's query + handle. The default value, <c>undefined</c>, means that <c>info/1,2</c> displays a call to <c>'$MOD':'$FUN'/0</c>. - It is up to <c>FormatFun</c> to present the selected objects - of the table in a suitable way. However, if a character list - is chosen for presentation it must be an Erlang expression - that can be scanned and parsed (a trailing dot will be added - by <c>qlc:info</c> though). <c>FormatFun</c> is called with - an argument that describes the selected objects based on - optimizations done as a result of analyzing the filters of - the QLC where the call to <c>qlc:table/2</c> occurs. The - possible values of the argument are:</p> + It is up to <c><anno>FormatFun</anno></c> to present the + selected objects of the table in a suitable way. However, if + a character list is chosen for presentation it must be an + Erlang expression that can be scanned and parsed (a trailing + dot will be added by <c>qlc:info</c> though). + <c><anno>FormatFun</anno></c> is called with an argument + that describes the selected objects based on optimizations + done as a result of analyzing the filters of the QLC where + the call to + <c>qlc:table/2</c> occurs. The possible values of the + argument are:</p> <list type="bulleted"> <item><c>{lookup, Position, Keys, NElements, DepthFun}</c>. @@ -1443,10 +1369,12 @@ ets:match_spec_run( can be used for limiting the size of terms; calling <c>DepthFun(Term)</c> substitutes <c>'...'</c> for parts of <c>Term</c> below the depth specified by the <c>info/1,2</c> - option <c>depth</c>. If calling <c>FormatFun</c> with an - argument including <c>NElements</c> and <c>DepthFun</c> - fails, <c>FormatFun</c> is called once again with an - argument excluding <c>NElements</c> and <c>DepthFun</c> + option <c>depth</c>. If calling + <c><anno>FormatFun</anno></c> with an argument including + <c>NElements</c> and <c>DepthFun</c> fails, + <c><anno>FormatFun</anno></c> is called once again with an + argument excluding + <c>NElements</c> and <c>DepthFun</c> (<c>{lookup, Position, Keys}</c> or <c>all</c>).</p> @@ -1458,7 +1386,7 @@ ets:match_spec_run( <p>See <seealso marker="ets#qlc_table">ets(3)</seealso>, <seealso marker="dets#qlc_table">dets(3)</seealso> and - <seealso marker="mnesia:mnesia#qlc_table">mnesia(3)</seealso> + <seealso marker="mnesia:mnesia#qlc_table">mnesia(3)</seealso> for the various options recognized by <c>table/1,2</c> in respective module.</p> </desc> @@ -1472,12 +1400,12 @@ ets:match_spec_run( <seealso marker="doc/reference_manual:users_guide"> Erlang Reference Manual</seealso>, <seealso marker="erl_eval">erl_eval(3)</seealso>, - <seealso marker="erts:erlang">erlang(3)</seealso>, + <seealso marker="erts:erlang">erlang(3)</seealso>, <seealso marker="ets">ets(3)</seealso>, - <seealso marker="kernel:file">file(3)</seealso>, - <seealso marker="error_logger:file">error_logger(3)</seealso>, + <seealso marker="kernel:file">file(3)</seealso>, + <seealso marker="error_logger:file">error_logger(3)</seealso>, <seealso marker="file_sorter">file_sorter(3)</seealso>, - <seealso marker="mnesia:mnesia">mnesia(3)</seealso>, + <seealso marker="mnesia:mnesia">mnesia(3)</seealso>, <seealso marker="doc/programming_examples:users_guide"> Programming Examples</seealso>, <seealso marker="shell">shell(3)</seealso></p> diff --git a/lib/stdlib/doc/src/queue.xml b/lib/stdlib/doc/src/queue.xml index 5ada1c2c57..383f52d10d 100644 --- a/lib/stdlib/doc/src/queue.xml +++ b/lib/stdlib/doc/src/queue.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2009</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -88,122 +88,94 @@ <title>Original API</title> </section> + <datatypes> + <datatype> + <name><marker id="type-queue">queue()</marker></name> + <desc><p>As returned by <c>new/0</c>.</p></desc> + </datatype> + </datatypes> + <funcs> <func> - <name>new() -> Q</name> + <name name="new" arity="0"/> <fsummary>Create an empty queue</fsummary> - <type> - <v>Q = queue()</v> - </type> <desc> <p>Returns an empty queue.</p> </desc> </func> <func> - <name>is_queue(Term) -> true | false</name> + <name name="is_queue" arity="1"/> <fsummary>Test if a term is a queue</fsummary> - <type> - <v>Term = term()</v> - </type> <desc> - <p>Tests if <c>Q</c> is a queue and returns <c>true</c> if so and + <p>Tests if <c><anno>Term</anno></c> is a queue and returns <c>true</c> if so and <c>false</c> otherwise.</p> </desc> </func> <func> - <name>is_empty(Q) -> true | false</name> + <name name="is_empty" arity="1"/> <fsummary>Test if a queue is empty</fsummary> - <type> - <v>Q = queue()</v> - </type> <desc> - <p>Tests if <c>Q</c> is empty and returns <c>true</c> if so and + <p>Tests if <c><anno>Q</anno></c> is empty and returns <c>true</c> if so and <c>false</c> otherwise.</p> </desc> </func> <func> - <name>len(Q) -> N</name> + <name name="len" arity="1"/> <fsummary>Get the length of a queue</fsummary> - <type> - <v>Q = queue()</v> - <v>N = integer()</v> - </type> <desc> - <p>Calculates and returns the length of queue <c>Q</c>.</p> + <p>Calculates and returns the length of queue <c><anno>Q</anno></c>.</p> </desc> </func> <func> - <name>in(Item, Q1) -> Q2</name> + <name name="in" arity="2"/> <fsummary>Insert an item at the rear of a queue</fsummary> - <type> - <v>Item = term()</v> - <v>Q1 = Q2 = queue()</v> - </type> <desc> - <p>Inserts <c>Item</c> at the rear of queue <c>Q1</c>. - Returns the resulting queue <c>Q2</c>.</p> + <p>Inserts <c><anno>Item</anno></c> at the rear of queue <c><anno>Q1</anno></c>. + Returns the resulting queue <c><anno>Q2</anno></c>.</p> </desc> </func> <func> - <name>in_r(Item, Q1) -> Q2</name> + <name name="in_r" arity="2"/> <fsummary>Insert an item at the front of a queue</fsummary> - <type> - <v>Item = term()</v> - <v>Q1 = Q2 = queue()</v> - </type> <desc> - <p>Inserts <c>Item</c> at the front of queue <c>Q1</c>. - Returns the resulting queue <c>Q2</c>.</p> + <p>Inserts <c><anno>Item</anno></c> at the front of queue <c><anno>Q1</anno></c>. + Returns the resulting queue <c><anno>Q2</anno></c>.</p> </desc> </func> <func> - <name>out(Q1) -> Result</name> + <name name="out" arity="1"/> <fsummary>Remove the front item from a queue</fsummary> - <type> - <v>Result = {{value, Item}, Q2} | {empty, Q1}</v> - <v>Q1 = Q2 = queue()</v> - </type> <desc> - <p>Removes the item at the front of queue <c>Q1</c>. Returns the - tuple <c>{{value, Item}, Q2}</c>, where <c>Item</c> is the - item removed and <c>Q2</c> is the resulting queue. If <c>Q1</c> is - empty, the tuple <c>{empty, Q1}</c> is returned.</p> + <p>Removes the item at the front of queue <c><anno>Q1</anno></c>. Returns the + tuple <c>{{value, <anno>Item</anno>}, <anno>Q2</anno>}</c>, where <c><anno>Item</anno></c> is the + item removed and <c><anno>Q2</anno></c> is the resulting queue. If <c><anno>Q1</anno></c> is + empty, the tuple <c>{empty, <anno>Q1</anno>}</c> is returned.</p> </desc> </func> <func> - <name>out_r(Q1) -> Result</name> + <name name="out_r" arity="1"/> <fsummary>Remove the rear item from a queue</fsummary> - <type> - <v>Result = {{value, Item}, Q2} | {empty, Q1}</v> - <v>Q1 = Q2 = queue()</v> - </type> <desc> - <p>Removes the item at the rear of the queue <c>Q1</c>. Returns the - tuple <c>{{value, Item}, Q2}</c>, where <c>Item</c> is the - item removed and <c>Q2</c> is the new queue. If <c>Q1</c> is - empty, the tuple <c>{empty, Q1}</c> is returned. </p> + <p>Removes the item at the rear of the queue <c><anno>Q1</anno></c>. Returns the + tuple <c>{{value, <anno>Item</anno>}, <anno>Q2</anno>}</c>, where <c><anno>Item</anno></c> is the + item removed and <c><anno>Q2</anno></c> is the new queue. If <c><anno>Q1</anno></c> is + empty, the tuple <c>{empty, <anno>Q1</anno>}</c> is returned. </p> </desc> </func> <func> - <name>from_list(L) -> queue()</name> + <name name="from_list" arity="1"/> <fsummary>Convert a list to a queue</fsummary> - <type> - <v>L = list()</v> - </type> <desc> - <p>Returns a queue containing the items in <c>L</c> in the + <p>Returns a queue containing the items in <c><anno>L</anno></c> in the same order; the head item of the list will become the front item of the queue.</p> </desc> </func> <func> - <name>to_list(Q) -> list()</name> + <name name="to_list" arity="1"/> <fsummary>Convert a queue to a list</fsummary> - <type> - <v>Q = queue()</v> - </type> <desc> <p>Returns a list of the items in the queue in the same order; the front item of the queue will become the head of the list.</p> @@ -211,57 +183,43 @@ </func> <func> - <name>reverse(Q1) -> Q2</name> + <name name="reverse" arity="1"/> <fsummary>Reverse a queue</fsummary> - <type> - <v>Q1 = Q2 = queue()</v> - </type> <desc> - <p>Returns a queue <c>Q2</c> that contains the items of - <c>Q1</c> in the reverse order.</p> + <p>Returns a queue <c><anno>Q2</anno></c> that contains the items of + <c><anno>Q1</anno></c> in the reverse order.</p> </desc> </func> <func> - <name>split(N, Q1) -> {Q2,Q3}</name> + <name name="split" arity="2"/> <fsummary>Split a queue in two</fsummary> - <type> - <v>N = integer()</v> - <v>Q1 = Q2 = Q3 = queue()</v> - </type> <desc> - <p>Splits <c>Q1</c> in two. The <c>N</c> front items - are put in <c>Q2</c> and the rest in <c>Q3</c></p> + <p>Splits <c><anno>Q1</anno></c> in two. The <c><anno>N</anno></c> front items + are put in <c><anno>Q2</anno></c> and the rest in <c><anno>Q3</anno></c></p> </desc> </func> <func> - <name>join(Q1, Q2) -> Q3</name> + <name name="join" arity="2"/> <fsummary>Join two queues</fsummary> - <type> - <v>Q1 = Q2 = Q3 = queue()</v> - </type> <desc> - <p>Returns a queue <c>Q3</c> that is the result of joining - <c>Q1</c> and <c>Q2</c> with <c>Q1</c> in front of - <c>Q2</c>.</p> + <p>Returns a queue <c><anno>Q3</anno></c> that is the result of joining + <c><anno>Q1</anno></c> and <c><anno>Q2</anno></c> with <c><anno>Q1</anno></c> in front of + <c><anno>Q2</anno></c>.</p> </desc> </func> <func> - <name>filter(Fun, Q1) -> Q2</name> + <name name="filter" arity="2"/> <fsummary>Filter a queue</fsummary> - <type> - <v>Fun = fun(Item) -> bool() | list()</v> - <v>Q1 = Q2 = queue()</v> - </type> <desc> - <p>Returns a queue <c>Q2</c> that is the result of calling - <c>Fun(Item)</c> on all items in <c>Q1</c>, + <p>Returns a queue <c><anno>Q2</anno></c> that is the result of calling + <c><anno>Fun</anno>(<anno>Item</anno>)</c> on all items in <c><anno>Q1</anno></c>, in order from front to rear.</p> - <p>If <c>Fun(Item)</c> returns <c>true</c>, <c>Item</c> + <p>If <c><anno>Fun</anno>(<anno>Item</anno>)</c> returns <c>true</c>, <c>Item</c> is copied to the result queue. If it returns <c>false</c>, - <c>Item</c> is not copied. If it returns a list + <c><anno>Item</anno></c> is not copied. If it returns a list the list elements are inserted instead of <c>Item</c> in the result queue.</p> - <p>So, <c>Fun(Item)</c> returning <c>[Item]</c> is thereby + <p>So, <c><anno>Fun</anno>(<anno>Item</anno>)</c> returning <c>[<anno>Item</anno>]</c> is thereby semantically equivalent to returning <c>true</c>, just as returning <c>[]</c> is semantically equivalent to returning <c>false</c>. But returning a list builds @@ -269,15 +227,11 @@ </desc> </func> <func> - <name>member(Item, Q) -> bool()</name> + <name name="member" arity="2"/> <fsummary>Test if an item is in a queue</fsummary> - <type> - <v>Item = term()</v> - <v>Q = queue()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>Item</c> matches some element - in <c>Q</c>, otherwise <c>false</c>.</p> + <p>Returns <c>true</c> if <c><anno>Item</anno></c> matches some element + in <c><anno>Q</anno></c>, otherwise <c>false</c>.</p> </desc> </func> </funcs> @@ -290,77 +244,53 @@ <funcs> <func> - <name>get(Q) -> Item</name> + <name name="get" arity="1"/> <fsummary>Return the front item of a queue</fsummary> - <type> - <v>Item = term()</v> - <v>Q = queue()</v> - </type> <desc> - <p>Returns <c>Item</c> at the front of queue <c>Q</c>.</p> - <p>Fails with reason <c>empty</c> if <c>Q</c> is empty.</p> + <p>Returns <c><anno>Item</anno></c> at the front of queue <c><anno>Q</anno></c>.</p> + <p>Fails with reason <c>empty</c> if <c><anno>Q</anno></c> is empty.</p> </desc> </func> <func> - <name>get_r(Q) -> Item</name> + <name name="get_r" arity="1"/> <fsummary>Return the rear item of a queue</fsummary> - <type> - <v>Item = term()</v> - <v>Q = queue()</v> - </type> <desc> - <p>Returns <c>Item</c> at the rear of queue <c>Q</c>.</p> - <p>Fails with reason <c>empty</c> if <c>Q</c> is empty.</p> + <p>Returns <c><anno>Item</anno></c> at the rear of queue <c><anno>Q</anno></c>.</p> + <p>Fails with reason <c>empty</c> if <c><anno>Q</anno></c> is empty.</p> </desc> </func> <func> - <name>drop(Q1) -> Q2</name> + <name name="drop" arity="1"/> <fsummary>Remove the front item from a queue</fsummary> - <type> - <v>Item = term()</v> - <v>Q1 = Q2 = queue()</v> - </type> <desc> - <p>Returns a queue <c>Q2</c> that is the result of removing - the front item from <c>Q1</c>.</p> - <p>Fails with reason <c>empty</c> if <c>Q1</c> is empty.</p> + <p>Returns a queue <c><anno>Q2</anno></c> that is the result of removing + the front item from <c><anno>Q1</anno></c>.</p> + <p>Fails with reason <c>empty</c> if <c><anno>Q1</anno></c> is empty.</p> </desc> </func> <func> - <name>drop_r(Q1) -> Q2</name> + <name name="drop_r" arity="1"/> <fsummary>Remove the rear item from a queue</fsummary> - <type> - <v>Item = term()</v> - <v>Q1 = Q2 = queue()</v> - </type> <desc> - <p>Returns a queue <c>Q2</c> that is the result of removing - the rear item from <c>Q1</c>.</p> - <p>Fails with reason <c>empty</c> if <c>Q1</c> is empty.</p> + <p>Returns a queue <c><anno>Q2</anno></c> that is the result of removing + the rear item from <c><anno>Q1</anno></c>.</p> + <p>Fails with reason <c>empty</c> if <c><anno>Q1</anno></c> is empty.</p> </desc> </func> <func> - <name>peek(Q) -> {value,Item} | empty</name> + <name name="peek" arity="1"/> <fsummary>Return the front item of a queue</fsummary> - <type> - <v>Item = term()</v> - <v>Q = queue()</v> - </type> <desc> - <p>Returns the tuple <c>{value, Item}</c> where <c>Item</c> is the - front item of <c>Q</c>, or <c>empty</c> if <c>Q1</c> is empty.</p> + <p>Returns the tuple <c>{value, <anno>Item</anno>}</c> where <c><anno>Item</anno></c> is the + front item of <c><anno>Q</anno></c>, or <c>empty</c> if <c><anno>Q</anno></c> is empty.</p> </desc> </func> <func> - <name>peek_r(Q) -> {value,Item} | empty</name> + <name name="peek_r" arity="1"/> <fsummary>Return the rear item of a queue</fsummary> - <type> - <v>Item = term()</v> - <v>Q = queue()</v> - </type> <desc> - <p>Returns the tuple <c>{value, Item}</c> where <c>Item</c> is the - rear item of <c>Q</c>, or <c>empty</c> if <c>Q1</c> is empty.</p> + <p>Returns the tuple <c>{value, <anno>Item</anno>}</c> where <c><anno>Item</anno></c> is the + rear item of <c><anno>Q</anno></c>, or <c>empty</c> if <c><anno>Q</anno></c> is empty.</p> </desc> </func> </funcs> @@ -372,80 +302,56 @@ <funcs> <func> - <name>cons(Item, Q1) -> Q2</name> + <name name="cons" arity="2"/> <fsummary>Insert an item at the head of a queue</fsummary> - <type> - <v>Item = term()</v> - <v>Q1 = Q2 = queue()</v> - </type> <desc> - <p>Inserts <c>Item</c> at the head of queue <c>Q1</c>. Returns - the new queue <c>Q2</c>.</p> + <p>Inserts <c><anno>Item</anno></c> at the head of queue <c><anno>Q1</anno></c>. Returns + the new queue <c><anno>Q2</anno></c>.</p> </desc> </func> <func> - <name>head(Q) -> Item</name> + <name name="head" arity="1"/> <fsummary>Return the item at the head of a queue</fsummary> - <type> - <v>Item = term()</v> - <v>Q = queue()</v> - </type> <desc> - <p>Returns <c>Item</c> from the head of queue <c>Q</c>.</p> - <p>Fails with reason <c>empty</c> if <c>Q</c> is empty.</p> + <p>Returns <c><anno>Item</anno></c> from the head of queue <c><anno>Q</anno></c>.</p> + <p>Fails with reason <c>empty</c> if <c><anno>Q</anno></c> is empty.</p> </desc> </func> <func> - <name>tail(Q1) -> Q2</name> + <name name="tail" arity="1"/> <fsummary>Remove the head item from a queue</fsummary> - <type> - <v>Item = term()</v> - <v>Q1 = Q2 = queue()</v> - </type> <desc> - <p>Returns a queue <c>Q2</c> that is the result of removing - the head item from <c>Q1</c>.</p> - <p>Fails with reason <c>empty</c> if <c>Q1</c> is empty.</p> + <p>Returns a queue <c><anno>Q2</anno></c> that is the result of removing + the head item from <c><anno>Q1</anno></c>.</p> + <p>Fails with reason <c>empty</c> if <c><anno>Q1</anno></c> is empty.</p> </desc> </func> <func> - <name>snoc(Q1, Item) -> Q2</name> + <name name="snoc" arity="2"/> <fsummary>Insert an item at the tail of a queue</fsummary> - <type> - <v>Item = term()</v> - <v>Q1 = Q2 = queue()</v> - </type> <desc> - <p>Inserts <c>Item</c> as the tail item of queue <c>Q1</c>. Returns - the new queue <c>Q2</c>.</p> + <p>Inserts <c><anno>Item</anno></c> as the tail item of queue <c><anno>Q1</anno></c>. Returns + the new queue <c><anno>Q2</anno></c>.</p> </desc> </func> <func> - <name>daeh(Q) -> Item</name> - <name>last(Q) -> Item</name> + <name name="daeh" arity="1"/> + <name name="last" arity="1"/> <fsummary>Return the tail item of a queue</fsummary> - <type> - <v>Item = term()</v> - <v>Q = queue()</v> - </type> <desc> - <p>Returns the tail item of queue <c>Q</c>.</p> - <p>Fails with reason <c>empty</c> if <c>Q</c> is empty.</p> + <p>Returns the tail item of queue <c><anno>Q</anno></c>.</p> + <p>Fails with reason <c>empty</c> if <c><anno>Q</anno></c> is empty.</p> </desc> </func> <func> - <name>liat(Q1) -> Q2</name> - <name>init(Q1) -> Q2</name> - <name>lait(Q1) -> Q2</name> + <name name="liat" arity="1"/> + <name name="init" arity="1"/> + <name name="lait" arity="1"/> <fsummary>Remove the tail item from a queue</fsummary> - <type> - <v>Item = term()</v> - <v>Q1 = Q2 = queue()</v> - </type> <desc> - <p>Returns a queue <c>Q2</c> that is the result of removing - the tail item from <c>Q1</c>.</p> - <p>Fails with reason <c>empty</c> if <c>Q1</c> is empty.</p> + <p>Returns a queue <c><anno>Q2</anno></c> that is the result of removing + the tail item from <c><anno>Q1</anno></c>.</p> + <p>Fails with reason <c>empty</c> if <c><anno>Q1</anno></c> is empty.</p> <p>The name <c>lait/1</c> is a misspelling - do not use it anymore.</p> </desc> </func> diff --git a/lib/stdlib/doc/src/random.xml b/lib/stdlib/doc/src/random.xml index dcc6d756e1..93affc3191 100644 --- a/lib/stdlib/doc/src/random.xml +++ b/lib/stdlib/doc/src/random.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2009</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -49,9 +49,15 @@ strong. If a strong cryptographic random number generator is needed for example <c>crypto:rand_bytes/1</c> could be used instead.</p> </description> + <datatypes> + <datatype> + <name name="ran"/> + <desc><p>The state.</p></desc> + </datatype> + </datatypes> <funcs> <func> - <name>seed() -> ran()</name> + <name name="seed" arity="0"/> <fsummary>Seeds random number generation with default values</fsummary> <desc> <p>Seeds random number generation with default (fixed) values @@ -59,11 +65,8 @@ </desc> </func> <func> - <name>seed(A1, A2, A3) -> undefined | ran()</name> + <name name="seed" arity="3"/> <fsummary>Seeds random number generator</fsummary> - <type> - <v>A1 = A2 = A3 = integer()</v> - </type> <desc> <p>Seeds random number generation with integer values in the process dictionary, and returns the old state.</p> @@ -76,26 +79,23 @@ </desc> </func> <func> - <name>seed({A1, A2, A3}) -> undefined | ran()</name> + <name name="seed" arity="1"/> <fsummary>Seeds random number generator</fsummary> - <type> - <v>A1 = A2 = A3 = integer()</v> - </type> <desc> <p> - <c>seed({A1, A2, A3})</c> is equivalent to <c>seed(A1, A2, A3)</c>. + <c>seed({<anno>A1</anno>, <anno>A2</anno>, <anno>A3</anno>})</c> is equivalent to <c>seed(<anno>A1</anno>, <anno>A2</anno>, <anno>A3</anno>)</c>. </p> </desc> </func> <func> - <name>seed0() -> ran()</name> + <name name="seed0" arity="0"/> <fsummary>Return default state for random number generation</fsummary> <desc> <p>Returns the default state.</p> </desc> </func> <func> - <name>uniform()-> float()</name> + <name name="uniform" arity="0"/> <fsummary>Return a random float</fsummary> <desc> <p>Returns a random float uniformly distributed between <c>0.0</c> @@ -103,39 +103,29 @@ </desc> </func> <func> - <name>uniform(N) -> integer()</name> + <name name="uniform" arity="1"/> <fsummary>Return a random integer</fsummary> - <type> - <v>N = integer()</v> - </type> <desc> - <p>Given an integer <c>N >= 1</c>, <c>uniform/1</c> returns a + <p>Given an integer <c><anno>N</anno> >= 1</c>, <c>uniform/1</c> returns a random integer uniformly distributed between <c>1</c> and - <c>N</c>, updating the state in the process dictionary.</p> + <c><anno>N</anno></c>, updating the state in the process dictionary.</p> </desc> </func> <func> - <name>uniform_s(State0) -> {float(), State1}</name> + <name name="uniform_s" arity="1"/> <fsummary>Return a random float</fsummary> - <type> - <v>State0 = State1 = ran()</v> - </type> <desc> <p>Given a state, <c>uniform_s/1</c>returns a random float uniformly distributed between <c>0.0</c> and <c>1.0</c>, and a new state.</p> </desc> </func> <func> - <name>uniform_s(N, State0) -> {integer(), State1}</name> + <name name="uniform_s" arity="2"/> <fsummary>Return a random integer</fsummary> - <type> - <v>N = integer()</v> - <v>State0 = State1 = ran()</v> - </type> <desc> - <p>Given an integer <c>N >= 1</c> and a state, <c>uniform_s/2</c> + <p>Given an integer <c><anno>N</anno> >= 1</c> and a state, <c>uniform_s/2</c> returns a random integer uniformly distributed between <c>1</c> and - <c>N</c>, and a new state.</p> + <c><anno>N</anno></c>, and a new state.</p> </desc> </func> </funcs> diff --git a/lib/stdlib/doc/src/re.xml b/lib/stdlib/doc/src/re.xml index 4d2a0e0995..18867cfb68 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>2008</year> + <year>2011</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> @@ -37,51 +37,46 @@ <modulesummary>Perl like regular expressions for Erlang</modulesummary> <description> - <p>This module contains functions for regular expression - matching for strings and binaries.</p> + <p>This module contains regular expression matching functions for + strings and binaries.</p> <p>The regular expression syntax and semantics resemble that of - Perl. This library in many ways replaces the old regexp library - written purely in Erlang, as it has a richer syntax as well as - many more options. The library is also faster than the - older regexp implementation.</p> - - <p>Although the library's matching algorithms are currently based - on the PCRE library, it is not to be viewed as an Erlang to PCRE - mapping. Only parts of the PCRE library is interfaced and the re - library in some ways extend PCRE. The PCRE documentation contains - many parts of no interest to the Erlang programmer, why only the - relevant part of the documentation is included here. There should - bee no need to go directly to the PCRE library documentation.</p> + Perl. This library replaces the deprecated pure-Erlang regexp + library; it has a richer syntax, more options and is faster.</p> + + <p>The library's matching algorithms are currently based on the + PCRE library, but not all of the PCRE library is interfaced and + some parts of the library go beyond what PCRE offers. The sections of + the PCRE documentation which are relevant to this module are included + here.</p> <note> - <p>The Erlang literal syntax for strings give special - meaning to the "\" (backslash) character. To literally write - a regular expression or a replacement string containing a - backslash in your code or in the shell, two backslashes have to be written: - "\\".</p> + <p>The Erlang literal syntax for strings uses the "\" + (backslash) character as an escape code. You need to escape + backslashes in literal strings, both in your code and in the shell, + with an additional backslash, i.e.: "\\".</p> </note> </description> - <section> - <title>DATA TYPES</title> - <code type="none"> - iodata() = iolist() | binary() - iolist() = [char() | binary() | iolist()] - - a binary is allowed as the tail of the list</code> - <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> - - <code type="none"> - mp() = Opaque datatype containing a compiled regular expression.</code> - </section> + <datatypes> + <datatype> + <name name="mp"/> + <desc> + <p>Opaque datatype containing a compiled regular expression. + The mp() is guaranteed to be a tuple() having the atom + 're_pattern' as its first element, to allow for matching in + guards. The arity of the tuple() or the content of the other fields + may change in future releases.</p> + </desc> + </datatype> + <datatype> + <name name="nl_spec"/> + </datatype> + <datatype> + <name name="compile_option"/> + </datatype> + </datatypes> <funcs> <func> <name>compile(Regexp) -> {ok, MP} | {error, ErrSpec}</name> @@ -97,14 +92,14 @@ <name>compile(Regexp,Options) -> {ok, MP} | {error, ErrSpec}</name> <fsummary>Compile a regular expression into a match program</fsummary> <type> - <v>Regexp = iodata() | charlist()</v> + <v>Regexp = iodata() | <seealso marker="unicode#type-charlist">io:charlist()</seealso></v> <v>Options = [ Option ]</v> - <v>Option = unicode | anchored | caseless | dollar_endonly | dotall | extended | firstline | multiline | no_auto_capture | dupnames | ungreedy | {newline, NLSpec}| bsr_anycrlf | bsr_unicode</v> - <v>NLSpec = cr | crlf | lf | anycrlf | any </v> - <v>MP = mp()</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 = int()</v> + <v>Position = non_neg_integer()</v> </type> <desc> <p>This function compiles a regular expression with the syntax @@ -117,7 +112,7 @@ 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>The options have the following meanings:</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> @@ -128,7 +123,7 @@ <tag><c>dollar_endonly</c></tag> <item>A dollar metacharacter in the pattern matches only at the end of the subject string. Without this option, a dollar also matches immediately before a newline at the end of the string (but not before any other newlines). The <c>dollar_endonly</c> option is ignored if <c>multiline</c> is given. There is no equivalent option in Perl, and no way to set it within a pattern.</item> <tag><c>dotall</c></tag> - <item>A dot maturate in the pattern matches all characters, including those that indicate newline. Without it, a dot does not match when the current position is at a newline. This option is equivalent to Perl's /s option, and it can be changed within a pattern by a (?s) option setting. A negative class such as [^a] always matches newline characters, independent of the setting of this option.</item> + <item>A dot in the pattern matches all characters, including those that indicate newline. Without it, a dot does not match when the current position is at a newline. This option is equivalent to Perl's /s option, and it can be changed within a pattern by a (?s) option setting. A negative class such as [^a] always matches newline characters, independent of this option's setting.</item> <tag><c>extended</c></tag> <item>Whitespace data characters in the pattern are ignored except when escaped or inside a character class. Whitespace does not include the VT character (ASCII 11). In addition, characters between an unescaped # outside a character class and the next newline, inclusive, are also ignored. This is equivalent to Perl's /x option, and it can be changed within a pattern by a (?x) option setting. @@ -138,7 +133,7 @@ This option makes it possible to include comments inside complicated patterns. N <tag><c>multiline</c></tag> <item><p>By default, PCRE treats the subject string as consisting of a single line of characters (even if it actually contains newlines). The "start of line" metacharacter (^) matches only at the start of the string, while the "end of line" metacharacter ($) matches only at the end of the string, or before a terminating newline (unless <c>dollar_endonly</c> is given). This is the same as Perl.</p> -<p>When <c>multiline</c> it is given, the "start of line" and "end of line" constructs match immediately following or immediately before internal newlines in the subject string, respectively, as well as at the very start and end. This is equivalent to Perl's /m option, and it can be changed within a pattern by a (?m) option setting. If there are no newlines in a subject string, or no occurrences of ^ or $ in a pattern, setting <c>multiline</c> has no effect.</p> </item> +<p>When <c>multiline</c> is given, the "start of line" and "end of line" constructs match immediately following or immediately before internal newlines in the subject string, respectively, as well as at the very start and end. This is equivalent to Perl's /m option, and it can be changed within a pattern by a (?m) option setting. If there are no newlines in a subject string, or no occurrences of ^ or $ in a pattern, setting <c>multiline</c> has no effect.</p> </item> <tag><c>no_auto_capture</c></tag> <item>Disables the use of numbered capturing parentheses in the pattern. Any opening parenthesis that is not followed by ? behaves as if it were followed by ?: but named parentheses can still be used for capturing (and they acquire numbers in the usual way). There is no equivalent of this option in Perl. </item> @@ -174,10 +169,10 @@ This option makes it possible to include comments inside complicated patterns. N <name>run(Subject,RE) -> {match, Captured} | nomatch</name> <fsummary>Match a subject against regular expression and capture subpatterns</fsummary> <type> - <v>Subject = iodata() | charlist()</v> - <v>RE = mp() | iodata() | charlist()</v> + <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 = {int(),int()}</v> + <v>CaptureData = {integer(),integer()}</v> </type> <desc> <p>The same as <c>run(Subject,RE,[])</c>.</p> @@ -187,18 +182,19 @@ This option makes it possible to include comments inside complicated patterns. N <name>run(Subject,RE,Options) -> {match, Captured} | match | nomatch</name> <fsummary>Match a subject against regular expression and capture subpatterns</fsummary> <type> - <v>Subject = iodata() | charlist()</v> - <v>RE = mp() | iodata() | charlist()</v> + <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, int()} | {newline, NLSpec} | bsr_anycrlf | bsr_unicode | {capture, ValueSpec} | {capture, ValueSpec, Type} | CompileOpt</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 = int() | string() | atom()</v> - <v>CompileOpt = see compile/2 above</v> - <v>NLSpec = cr | crlf | lf | anycrlf | any</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 = {int(),int()} | ListConversionData | binary()</v> + <v>CaptureData = {integer(),integer()} | ListConversionData | binary()</v> <v>ListConversionData = string() | {error, string(), binary()} | {incomplete, string(), binary()}</v> </type> <desc> @@ -210,14 +206,15 @@ This option makes it possible to include comments inside complicated patterns. N 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 thrown if - a compilation error occurs. To locate the error in the regular - expression, use the function <c>re:compile/2</c> to get more information.</p> + <p>When compilation is involved, the exception <c>badarg</c> is + thrown if a compilation error occurs. Call <c>re:compile/2</c> + to get information about the location of the error in the + regular expression.</p> <p>If the regular expression is previously compiled, the option list can only contain the options <c>anchored</c>, <c>global</c>, <c>notbol</c>, <c>noteol</c>, - <c>notempty</c>, <c>{offset, int()}</c>, <c>{newline, + <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 <c>re:compile/2</c> function are allowed as well. Options @@ -242,7 +239,7 @@ This option makes it possible to include comments inside complicated patterns. N how captured substrings are to be returned (as index tuples, lists or binaries). The <c>capture</c> option makes the function quite flexible and powerful. The different options are described - in detail below</p> + in detail below.</p> <p>If the capture options describe that no substring capturing at all is to be done (<c>{capture, none}</c>), the function will @@ -252,7 +249,7 @@ This option makes it possible to include comments inside complicated patterns. N be done either by specifying <c>none</c> or an empty list as <c>ValueSpec</c>.</p> - <p>A description of all the options relevant for execution follows:</p> + <p>The options relevant for execution are:</p> <taglist> <tag><c>anchored</c></tag> @@ -266,27 +263,25 @@ This option makes it possible to include comments inside complicated patterns. N <tag><c>global</c></tag> <item> - <p>Implements global (repetitive) search as the <c>g</c> flag in - i.e. Perl. Each match found is returned as a separate + <p>Implements global (repetitive) search (the <c>g</c> flag in + 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 - hence be a <c>list()</c> of <c>list()</c>'s when this + hence be a <c>list()</c> of <c>list()</c>s when this option is given.</p> - <p>When the regular expression matches an empty string, the - behaviour might seem non-intuitive, why the behaviour requites - some clarifying. With the global option, <c>re:run/3</c> - handles empty matches in the same way as Perl, meaning that a - match at any point giving an empty string (with length 0) will - be retried with the options - <c>[anchored, notempty]</c> as well. If that - search gives a result of length > 0, the result is included. - An example:</p> + <p>The interaction of the global option with a regular + expression which matches an empty string surprises some users. + When the global option is given, <c>re:run/3</c> handles empty + matches in the same way as Perl: a zero-length match at any + point will be retried with the options <c>[anchored, + notempty]</c> as well. If that search gives a result of length + > 0, the result is included. For example:</p> <code> re:run("cat","(|at)",[global]).</code> - <p>The matching will be performed as following:</p> + <p>The following matching will be performed:</p> <taglist> <tag>At offset <c>0</c></tag> <item>The regexp <c>(|at)</c> will first match at the initial @@ -298,11 +293,11 @@ This option makes it possible to include comments inside complicated patterns. N <item> The search is retried with the options <c>[anchored, notempty]</c> at the same position, which does not give any interesting result of longer - length, why the search position is now advanced to the next + length, so the search position is now advanced to the next character (<c>a</c>).</item> <tag>At offset <c>1</c></tag> - <item>Now the search results in - <c>[{1,0},{1,0}]</c> meaning this search will also be repeated + <item>This time, the search results in + <c>[{1,0},{1,0}]</c>, so this search will also be repeated with the extra options.</item> <tag>At offset <c>1</c> with <c>[anchored, notempty]</c></tag> <item>Now the <c>ab</c> alternative @@ -329,16 +324,17 @@ This option makes it possible to include comments inside complicated patterns. N entire match fails. For example, if the pattern</p> <code> a?b?</code> <p>is applied to a string not beginning with "a" or "b", it - matches the empty string at the start of the subject. With - <c>notempty</c> given, this match is not valid, so re:run/3 searches - further into the string for occurrences of "a" or "b".</p> + would normally match the empty string at the start of the + subject. With the <c>notempty</c> option, this match is not + valid, so re:run/3 searches further into the string for + occurrences of "a" or "b".</p> <p>Perl has no direct equivalent of <c>notempty</c>, but it does make a special case of a pattern match of the empty string within its split() function, and when using the /g modifier. It is possible to emulate Perl's behavior after matching a null string by first trying the match again at the same offset with - <c>notempty</c> and <c>anchored</c>, and then if that fails by + <c>notempty</c> and <c>anchored</c>, and then, if that fails, by advancing the starting offset (see below) and trying an ordinary match again.</p> </item> @@ -348,7 +344,7 @@ This option makes it possible to include comments inside complicated patterns. N string is not the beginning of a line, so the circumflex metacharacter should not match before it. Setting this without <c>multiline</c> (at compile time) causes circumflex never to - match. This option affects only the behavior of the circumflex + match. This option only affects the behavior of the circumflex metacharacter. It does not affect \A.</item> <tag><c>noteol</c></tag> @@ -361,7 +357,7 @@ This option makes it possible to include comments inside complicated patterns. N behavior of the dollar metacharacter. It does not affect \Z or \z.</item> - <tag><c>{offset, int()}</c></tag> + <tag><c>{offset, integer() >= 0}</c></tag> <item>Start matching at the offset (position) given in the subject string. The offset is zero-based, so that the default is @@ -384,7 +380,7 @@ This option makes it possible to include comments inside complicated patterns. N </taglist> </item> <tag><c>bsr_anycrlf</c></tag> - <item>Specifies specifically that \R is to match only the cr, lf or crlf sequences, not the Unicode specific newline characters.(overrides compilation option)</item> + <item>Specifies specifically that \R is to match only the cr, lf or crlf sequences, not the Unicode specific newline characters. (overrides compilation option)</item> <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> @@ -440,7 +436,7 @@ This option makes it possible to include comments inside complicated patterns. N <tag><c>none</c></tag> <item>Do not return matching subpatterns at all, yielding the single atom <c>match</c> as the return value of the function when matching successfully instead of the <c>{match, list()}</c> return. Specifying an empty list gives the same behavior.</item> </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. This deserves an example, consider the following regular expression:</p> + <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> <code> re:run("ABCabcdABC",".*(abcd).*",[{capture,[1]}]).</code> @@ -451,7 +447,7 @@ This option makes it possible to include comments inside complicated patterns. N <code> ".*(?<FOO>abcd).*"</code> <p>With this expression, we could still give the index of the subpattern with the following call:</p> <code> re:run("ABCabcdABC",".*(?<FOO>abcd).*",[{capture,[1]}]).</code> - <p>giving the same result as before. But as the subpattern is named, we can also give its name in the value list:</p> + <p>giving the same result as before. But, since the subpattern is named, we can also specify its name in the value list:</p> <code> re:run("ABCabcdABC",".*(?<FOO>abcd).*",[{capture,['FOO']}]).</code> <p>which would yield the same result as the earlier examples, namely:</p> <code> {match,[{3,4}]}</code> @@ -469,15 +465,15 @@ This option makes it possible to include comments inside complicated patterns. N <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> <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 imagined) <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 contra-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> + <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> <tag><c>list</c></tag> - <item>Return matching substrings as lists of characters (Erlang <c>string()</c>'s). It the <c>unicode</c> option is used in combination with the \C sequence in the regular expression, a captured subpattern can contain bytes that has is not valid UTF-8 (\C matches bytes regardless of character encoding). In that case the <c>list</c> capturing may result in the same types of tuples that <c>unicode:characters_to_list/2</c> can return, namely three-tuples with the tag <c>incomplete</c> or <c>error</c>, the successfully converted characters and the invalid UTF-8 tail of the conversion as a binary. The best strategy is to avoid using the \C sequence when capturing lists.</item> + <item>Return matching substrings as lists of characters (Erlang <c>string()</c>s). It the <c>unicode</c> option is used in combination with the \C sequence in the regular expression, a captured subpattern can contain bytes that are not valid UTF-8 (\C matches bytes regardless of character encoding). In that case the <c>list</c> capturing may result in the same types of tuples that <c>unicode:characters_to_list/2</c> can return, namely three-tuples with the tag <c>incomplete</c> or <c>error</c>, the successfully converted characters and the invalid UTF-8 tail of the conversion as a binary. The best strategy is to avoid using the \C sequence when capturing lists.</item> <tag><c>binary</c></tag> - <item>Return matching substrings as binaries. If the <c>unicode</c> option is used, these binaries is in UTF-8. If the \C sequence is used together with <c>unicode</c> the binaries may be invalid UTF-8.</item> + <item>Return matching substrings as binaries. If the <c>unicode</c> option is used, these binaries are in UTF-8. If the \C sequence is used together with <c>unicode</c> the binaries may be invalid UTF-8.</item> </taglist> </item> </taglist> - <p>In general, subpatterns that got assigned no value in the match are returned as the tuple <c>{-1,0}</c> when <c>type</c> is <c>index</c>. Unassigned subpatterns are returned as the empty binary or list respectively for other return types. Consider the regular expression:</p> + <p>In general, subpatterns that were not assigned a value in the match are returned as the tuple <c>{-1,0}</c> when <c>type</c> is <c>index</c>. Unassigned subpatterns are returned as the empty binary or list, respectively, for other return types. Consider the regular expression:</p> <code> ".*((?<FOO>abdd)|a(..d)).*"</code> <p>There are three explicitly capturing subpatterns, where the opening parenthesis position determines the order in the result, hence <c>((?<FOO>abdd)|a(..d))</c> is subpattern index 1, <c>(?<FOO>abdd)</c> is subpattern index 2 and <c>(..d)</c> is subpattern index 3. When matched against the following string:</p> <code> "ABCabcdABC"</code> @@ -504,43 +500,28 @@ This option makes it possible to include comments inside complicated patterns. N </desc> </func> <func> - <name>replace(Subject,RE,Replacement) -> iodata() | charlist()</name> + <name name="replace" arity="3"/> <fsummary>Match a subject against regular expression and replace matching elements with Replacement</fsummary> - <type> - <v>Subject = iodata() | charlist()</v> - <v>RE = mp() | iodata()</v> - <v>Replacement = iodata() | charlist()</v> - </type> <desc> - <p>The same as <c>replace(Subject,RE,Replacement,[])</c>.</p> + <p>The same as <c>replace(<anno>Subject</anno>,<anno>RE</anno>,<anno>Replacement</anno>,[])</c>.</p> </desc> </func> <func> - <name>replace(Subject,RE,Replacement,Options) -> iodata() | charlist() | binary() | list()</name> + <name name="replace" arity="4"/> <fsummary>Match a subject against regular expression and replace matching elements with Replacement</fsummary> - <type> - <v>Subject = iodata() | charlist()</v> - <v>RE = mp() | iodata() | charlist()</v> - <v>Replacement = iodata() | charlist()</v> - <v>Options = [ Option ]</v> - <v>Option = anchored | global | notbol | noteol | notempty | {offset, int()} | {newline, NLSpec} | bsr_anycrlf | bsr_unicode | {return, ReturnType} | CompileOpt</v> - <v>ReturnType = iodata | list | binary</v> - <v>CompileOpt = see compile/2 above</v> - <v>NLSpec = cr | crlf | lf | anycrlf | any </v> - </type> <desc> - <p>Replaces the matched part of the <c>Subject</c> string with the content of <c>Replacement</c>.</p> - <p>Options are given as to the <c>re:run/3</c> function except that the <c>capture</c> option of <c>re:run/3</c> is not allowed. - Instead a <c>{return, ReturnType}</c> is present. The default return type is <c>iodata</c>, constructed in a + <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 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 - with the <c>unicode</c> option requires the <c>Subject</c> to be + with the <c>unicode</c> option requires the <c><anno>Subject</anno></c> to be a Unicode <c>charlist()</c>. If compilation is done implicitly and the <c>unicode</c> compilation option is given to this - function, both the regular expression and the <c>Subject</c> - should be given as valid Unicode <c>charlist()</c>'s.</p> + function, both the regular expression and the <c><anno>Subject</anno></c> + should be given as valid Unicode <c>charlist()</c>s.</p> <p>The replacement string can contain the special character <c>&</c>, which inserts the whole matching expression in the @@ -550,7 +531,7 @@ This option makes it possible to include comments inside complicated patterns. N generated by the regular expression, nothing is inserted.</p> <p>To insert an <c>&</c> or <c>\</c> in the result, precede it with a <c>\</c>. Note that Erlang already gives a special - meaning to <c>\</c> in literal strings, why a single <c>\</c> + meaning to <c>\</c> in literal strings, so a single <c>\</c> has to be written as <c>"\\"</c> and therefore a double <c>\</c> as <c>"\\\\"</c>. Example:</p> <code> re:replace("abcd","c","[&]",[{return,list}]).</code> @@ -566,34 +547,17 @@ This option makes it possible to include comments inside complicated patterns. N </desc> </func> <func> - <name>split(Subject,RE) -> SplitList</name> + <name name="split" arity="2"/> <fsummary>Split a string by tokens specified as a regular expression</fsummary> - <type> - <v>Subject = iodata() | charlist()</v> - <v>RE = mp() | iodata()</v> - <v>SplitList = [ iodata() | charlist() ]</v> - </type> <desc> - <p>The same as <c>split(Subject,RE,[])</c>.</p> + <p>The same as <c>split(<anno>Subject</anno>,<anno>RE</anno>,[])</c>.</p> </desc> </func> <func> - <name>split(Subject,RE,Options) -> SplitList</name> + <name name="split" arity="3"/> <fsummary>Split a string by tokens specified as a regular expression</fsummary> - <type> - <v>Subject = iodata() | charlist()</v> - <v>RE = mp() | iodata() | charlist()</v> - <v>Options = [ Option ]</v> - <v>Option = anchored | global | notbol | noteol | notempty | {offset, int()} | {newline, NLSpec} | bsr_anycrlf | bsr_unicode | {return, ReturnType} | {parts, NumParts} | group | trim | CompileOpt</v> - <v>NumParts = int() | infinity</v> - <v>ReturnType = iodata | list | binary</v> - <v>CompileOpt = see compile/2 above</v> - <v>NLSpec = cr | crlf | lf | anycrlf | any </v> - <v>SplitList = [ RetData ] | [ GroupedRetData ]</v> - <v>GroupedRetData = [ RetData ]</v> - <v>RetData = iodata() | charlist() | binary() | list()</v> - </type> + <type_desc variable="CompileOpt">See <seealso marker="#compile_options">compile/2</seealso> above.</type_desc> <desc> <p>This function splits the input into parts by finding tokens according to the regular expression supplied.</p> @@ -603,11 +567,11 @@ This option makes it possible to include comments inside complicated patterns. N of the string is removed from the output.</p> <p>As in the <c>re:run/3</c> function, an <c>mp()</c> compiled - with the <c>unicode</c> option requires the <c>Subject</c> to be + with the <c>unicode</c> option requires the <c><anno>Subject</anno></c> to be a Unicode <c>charlist()</c>. If compilation is done implicitly and the <c>unicode</c> compilation option is given to this - function, both the regular expression and the <c>Subject</c> - should be given as valid Unicode <c>charlist()</c>'s.</p> + function, both the regular expression and the <c><anno>Subject</anno></c> + should be given as valid Unicode <c>charlist()</c>s.</p> <p>The result is given as a list of "strings", the preferred datatype given in the <c>return</c> option (default iodata).</p> @@ -652,25 +616,25 @@ This option makes it possible to include comments inside complicated patterns. N <p>Here the regular expression matched first the "l", causing "Er" to be the first part in the result. When the regular expression matched, the (only) subexpression was - bound to the "l", why the "l" is inserted + bound to the "l", so the "l" is inserted in the group together with "Er". The next match is of the "n", making "a" the next part to be - returned. As the subexpression is bound to the substring + returned. Since the subexpression is bound to the substring "n" in this case, the "n" is inserted into this group. The last group consists of the rest of the string, as no more matches are found.</p> <p>By default, all parts of the string, including the empty - strings are returned from the function. As an example:</p> + strings, are returned from the function. For example:</p> <code> re:split("Erlang","[lg]",[{return,list}]).</code> - <p>The result will be:</p> + <p>will return:</p> <code> ["Er","an",[]]</code> - <p>as the matching of the "g" in the end of the string + <p>since the matching of the "g" in the end of the string leaves an empty rest which is also returned. This behaviour differs from the default behaviour of the split function in Perl, where empty strings at the end are by default removed. To @@ -697,10 +661,10 @@ This option makes it possible to include comments inside complicated patterns. N <p>Note that the last part is "ang", not "an", as we only specified splitting into two parts, - and the splitting stops when enough parts are given, why the - result differs from that of <c>trim</c>.</p> + and the splitting stops when enough parts are given, which is + why the result differs from that of <c>trim</c>.</p> - <p>More than three parts are not possible with this indata, why</p> + <p>More than three parts are not possible with this indata, so</p> <code> re:split("Erlang","[lg]",[{return,list},{parts,4}]).</code> @@ -723,7 +687,7 @@ This option makes it possible to include comments inside complicated patterns. N <p>Summary of options not previously described for the <c>re:run/3</c> function:</p> <taglist> - <tag>{return,ReturnType}</tag> + <tag>{return,<anno>ReturnType</anno>}</tag> <item><p>Specifies how the parts of the original string are presented in the result list. The possible types are:</p> <taglist> <tag>iodata</tag> @@ -741,7 +705,7 @@ This option makes it possible to include comments inside complicated patterns. N the parts of the string matching the subexpressions of the regexp.</p> <p>The return value from the function will in this case be a - <c>list()</c> of <c>list()</c>'s. Each sublist begins with the + <c>list()</c> of <c>list()</c>s. Each sublist begins with the string picked out of the subject string, followed by the parts matching each of the subexpressions in order of occurrence in the regular expression.</p> @@ -778,10 +742,8 @@ This option makes it possible to include comments inside complicated patterns. N <title>PERL LIKE REGULAR EXPRESSIONS SYNTAX</title> <p>The following sections contain reference material for the regular expressions used by this module. The regular expression - reference is taken from the PCRE documentation, but converted as - needed.</p> - <p>The documentation is altered where appropriate and where the re - module behaves differently than the PCRE library.</p> + reference is based on the PCRE documentation, with changes in + cases where the re module behaves differently to the PCRE library.</p> </section> <section><title>PCRE regular expression details</title> diff --git a/lib/stdlib/doc/src/ref_man.xml b/lib/stdlib/doc/src/ref_man.xml index f6ae368e92..85aae6151d 100644 --- a/lib/stdlib/doc/src/ref_man.xml +++ b/lib/stdlib/doc/src/ref_man.xml @@ -4,7 +4,7 @@ <application xmlns:xi="http://www.w3.org/2001/XInclude"> <header> <copyright> - <year>1996</year><year>2009</year> + <year>1996</year><year>2010</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -13,12 +13,12 @@ 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. - + </legalnotice> <title>STDLIB Reference Manual</title> @@ -37,6 +37,7 @@ <xi:include href="array.xml"/> <xi:include href="base64.xml"/> <xi:include href="beam_lib.xml"/> + <xi:include href="binary.xml"/> <xi:include href="c.xml"/> <xi:include href="calendar.xml"/> <xi:include href="dets.xml"/> diff --git a/lib/stdlib/doc/src/regexp.xml b/lib/stdlib/doc/src/regexp.xml index 8c4191c88f..35d8e1c3f8 100644 --- a/lib/stdlib/doc/src/regexp.xml +++ b/lib/stdlib/doc/src/regexp.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2009</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -40,176 +40,150 @@ <p>This module contains functions for regular expression matching and substitution.</p> </description> + <datatypes> + <datatype> + <name name="errordesc"></name> + </datatype> + <datatype> + <name name="regexp"></name> + <desc><p>Internal representation of a regular expression.</p></desc> + </datatype> + </datatypes> <funcs> <func> - <name>match(String, RegExp) -> MatchRes</name> + <name name="match" arity="2"/> <fsummary>Match a regular expression</fsummary> - <type> - <v>String = RegExp = string()</v> - <v>MatchRes = {match,Start,Length} | nomatch | {error,errordesc()}</v> - <v>Start = Length = integer()</v> - </type> <desc> - <p>Finds the first, longest match of the regular expression <c>RegExp</c> in <c>String</c>. This function searches for the longest possible match and returns the first one found if there are several expressions of the same length. It returns as follows:</p> + <p>Finds the first, longest match of the regular expression <c><anno>RegExp</anno></c> in <c><anno>String</anno></c>. This function searches for the longest possible match and returns the first one found if there are several expressions of the same length. It returns as follows:</p> <taglist> - <tag><c>{match,Start,Length}</c></tag> + <tag><c>{match,<anno>Start</anno>,<anno>Length</anno>}</c></tag> <item> - <p>if the match succeeded. <c>Start</c> is the starting - position of the match, and <c>Length</c> is the length of + <p>if the match succeeded. <c><anno>Start</anno></c> is the starting + position of the match, and <c><anno>Length</anno></c> is the length of the matching string.</p> </item> <tag><c>nomatch</c></tag> <item> <p>if there were no matching characters.</p> </item> - <tag><c>{error,Error}</c></tag> + <tag><c>{error,<anno>Error</anno>}</c></tag> <item> - <p>if there was an error in <c>RegExp</c>.</p> + <p>if there was an error in <c><anno>RegExp</anno></c>.</p> </item> </taglist> </desc> </func> <func> - <name>first_match(String, RegExp) -> MatchRes</name> + <name name="first_match" arity="2"/> <fsummary>Match a regular expression</fsummary> - <type> - <v>String = RegExp = string()</v> - <v>MatchRes = {match,Start,Length} | nomatch | {error,errordesc()}</v> - <v>Start = Length = integer()</v> - </type> <desc> - <p>Finds the first match of the regular expression <c>RegExp</c> in <c>String</c>. This call is + <p>Finds the first match of the regular expression <c><anno>RegExp</anno></c> in <c><anno>String</anno></c>. This call is usually faster than <c>match</c> and it is also a useful way to ascertain that a match exists. It returns as follows:</p> <taglist> - <tag><c>{match,Start,Length}</c></tag> + <tag><c>{match,<anno>Start</anno>,<anno>Length</anno>}</c></tag> <item> - <p>if the match succeeded. <c>Start</c> is the starting - position of the match and <c>Length</c> is the length of + <p>if the match succeeded. <c><anno>Start</anno></c> is the starting + position of the match and <c><anno>Length</anno></c> is the length of the matching string.</p> </item> <tag><c>nomatch</c></tag> <item> <p>if there were no matching characters.</p> </item> - <tag><c>{error,Error}</c></tag> + <tag><c>{error,<anno>Error</anno>}</c></tag> <item> - <p>if there was an error in <c>RegExp</c>.</p> + <p>if there was an error in <c><anno>RegExp</anno></c>.</p> </item> </taglist> </desc> </func> <func> - <name>matches(String, RegExp) -> MatchRes</name> + <name name="matches" arity="2"/> <fsummary>Match a regular expression</fsummary> - <type> - <v>String = RegExp = string()</v> - <v>MatchRes = {match, Matches} | {error, errordesc()}</v> - <v>Matches = list()</v> - </type> <desc> <p>Finds all non-overlapping matches of the - expression <c>RegExp</c> in <c>String</c>. + expression <c><anno>RegExp</anno></c> in <c><anno>String</anno></c>. It returns as follows:</p> <taglist> - <tag><c>{match, Matches}</c></tag> + <tag><c>{match, <anno>Matches</anno>}</c></tag> <item> <p>if the regular expression was correct. - The list will be empty if there was no match. Each element in the list looks like <c>{Start, Length}</c>, where <c>Start</c> is the starting position of the match, and <c>Length</c> is the length of the matching string.</p> + The list will be empty if there was no match. Each element in the list looks like <c>{<anno>Start</anno>, <anno>Length</anno>}</c>, where <c><anno>Start</anno></c> is the starting position of the match, and <c><anno>Length</anno></c> is the length of the matching string.</p> </item> - <tag><c>{error,Error}</c></tag> + <tag><c>{error,<anno>Error</anno>}</c></tag> <item> - <p>if there was an error in <c>RegExp</c>.</p> + <p>if there was an error in <c><anno>RegExp</anno></c>.</p> </item> </taglist> </desc> </func> <func> - <name>sub(String, RegExp, New) -> SubRes</name> + <name name="sub" arity="3"/> <fsummary>Substitute the first occurrence of a regular expression</fsummary> - <type> - <v>String = RegExp = New = string()</v> - <v>SubRes = {ok,NewString,RepCount} | {error,errordesc()}</v> - <v>RepCount = integer()</v> - </type> <desc> - <p>Substitutes the first occurrence of a substring matching <c>RegExp</c> in <c>String</c> with the string <c>New</c>. A <c><![CDATA[&]]></c> in the string <c>New</c> is replaced by the matched substring of <c>String</c>. <c><![CDATA[\&]]></c> puts a literal <c><![CDATA[&]]></c> into the replacement string. It returns as follows:</p> + <p>Substitutes the first occurrence of a substring matching <c><anno>RegExp</anno></c> in <c><anno>String</anno></c> with the string <c><anno>New</anno></c>. A <c><![CDATA[&]]></c> in the string <c><anno>New</anno></c> is replaced by the matched substring of <c><anno>String</anno></c>. <c><![CDATA[\&]]></c> puts a literal <c><![CDATA[&]]></c> into the replacement string. It returns as follows:</p> <taglist> - <tag><c>{ok,NewString,RepCount}</c></tag> + <tag><c>{ok,<anno>NewString</anno>,<anno>RepCount</anno>}</c></tag> <item> - <p>if <c>RegExp</c> is correct. <c>RepCount</c> is the number of replacements which have been made + <p>if <c><anno>RegExp</anno></c> is correct. <c><anno>RepCount</anno></c> is the number of replacements which have been made (this will be either 0 or 1).</p> </item> - <tag><c>{error, Error}</c></tag> + <tag><c>{error, <anno>Error</anno>}</c></tag> <item> - <p>if there is an error in <c>RegExp</c>.</p> + <p>if there is an error in <c><anno>RegExp</anno></c>.</p> </item> </taglist> </desc> </func> <func> - <name>gsub(String, RegExp, New) -> SubRes</name> + <name name="gsub" arity="3"/> <fsummary>Substitute all occurrences of a regular expression</fsummary> - <type> - <v>String = RegExp = New = string()</v> - <v>SubRes = {ok,NewString,RepCount} | {error,errordesc()}</v> - <v>RepCount = integer()</v> - </type> <desc> <p>The same as <c>sub</c>, except that all non-overlapping occurrences of a substring matching - <c>RegExp</c> in <c>String</c> are replaced by the string <c>New</c>. It returns:</p> + <c><anno>RegExp</anno></c> in <c><anno>String</anno></c> are replaced by the string <c><anno>New</anno></c>. It returns:</p> <taglist> - <tag><c>{ok,NewString,RepCount}</c></tag> + <tag><c>{ok,<anno>NewString</anno>,<anno>RepCount</anno>}</c></tag> <item> - <p>if <c>RegExp</c> is correct. <c>RepCount</c> is the number of replacements which have been made.</p> + <p>if <c><anno>RegExp</anno></c> is correct. <c><anno>RepCount</anno></c> is the number of replacements which have been made.</p> </item> - <tag><c>{error, Error}</c></tag> + <tag><c>{error, <anno>Error</anno>}</c></tag> <item> - <p>if there is an error in <c>RegExp</c>.</p> + <p>if there is an error in <c><anno>RegExp</anno></c>.</p> </item> </taglist> </desc> </func> <func> - <name>split(String, RegExp) -> SplitRes</name> + <name name="split" arity="2"/> <fsummary>Split a string into fields</fsummary> - <type> - <v>String = RegExp = string()</v> - <v>SubRes = {ok,FieldList} | {error,errordesc()}</v> - <v>Fieldlist = [string()]</v> - </type> <desc> - <p><c>String</c> is split into fields (sub-strings) by the - regular expression <c>RegExp</c>.</p> + <p><c><anno>String</anno></c> is split into fields (sub-strings) by the + regular expression <c><anno>RegExp</anno></c>.</p> <p>If the separator expression is <c>" "</c> (a single space), then the fields are separated by blanks and/or tabs and leading and trailing blanks and tabs are discarded. For all other values of the separator, leading and trailing blanks and tabs are not discarded. It returns:</p> <taglist> - <tag><c>{ok, FieldList}</c></tag> + <tag><c>{ok, <anno>FieldList</anno>}</c></tag> <item> <p>to indicate that the string has been split up into the fields of - <c>FieldList</c>.</p> + <c><anno>FieldList</anno></c>.</p> </item> - <tag><c>{error, Error}</c></tag> + <tag><c>{error, <anno>Error</anno>}</c></tag> <item> - <p>if there is an error in <c>RegExp</c>.</p> + <p>if there is an error in <c><anno>RegExp</anno></c>.</p> </item> </taglist> </desc> </func> <func> - <name>sh_to_awk(ShRegExp) -> AwkRegExp</name> + <name name="sh_to_awk" arity="1"/> <fsummary>Convert an <c>sh</c>regular expression into an <c>AWK</c>one</fsummary> - <type> - <v>ShRegExp AwkRegExp = string()</v> - <v>SubRes = {ok,NewString,RepCount} | {error,errordesc()}</v> - <v>RepCount = integer()</v> - </type> <desc> <p>Converts the <c>sh</c> type regular expression - <c>ShRegExp</c> into a full <c>AWK</c> regular + <c><anno>ShRegExp</anno></c> into a full <c>AWK</c> regular expression. Returns the converted regular expression string. <c>sh</c> expressions are used in the shell for matching file names and have the following special @@ -236,40 +210,32 @@ </desc> </func> <func> - <name>parse(RegExp) -> ParseRes</name> + <name name="parse" arity="1"/> <fsummary>Parse a regular expression</fsummary> - <type> - <v>RegExp = string()</v> - <v>ParseRes = {ok,RE} | {error,errordesc()}</v> - </type> <desc> - <p>Parses the regular expression <c>RegExp</c> and builds the + <p>Parses the regular expression <c><anno>RegExp</anno></c> and builds the internal representation used in the other regular expression functions. Such representations can be used in all of the other functions instead of a regular expression string. This is more efficient when the same regular expression is used in many strings. It returns:</p> <taglist> - <tag><c>{ok, RE}</c>if <c>RegExp</c>is correct and <c>RE</c>is the internal representation.</tag> + <tag><c>{ok, <anno>RE</anno>}</c></tag> <item> - <p></p> + <p>if <c>RegExp</c> is correct and <c><anno>RE</anno></c> is the internal representation.</p> </item> - <tag><c>{error, Error}</c>if there is an error in <c>RegExpString</c>.</tag> + <tag><c>{error, <anno>Error</anno>}</c></tag> <item> - <p></p> + <p>if there is an error in <c><anno>RegExp</anno></c>.</p> </item> </taglist> </desc> </func> <func> - <name>format_error(ErrorDescriptor) -> Chars</name> + <name name="format_error" arity="1"/> <fsummary>Format an error descriptor</fsummary> - <type> - <v>ErrorDescriptor = errordesc()</v> - <v>Chars = [char() | Chars]</v> - </type> <desc> - <p>Returns a string which describes the error <c>ErrorDescriptor</c> + <p>Returns a string which describes the error <c><anno>ErrorDescriptor</anno></c> returned when there is an error in a regular expression.</p> </desc> </func> diff --git a/lib/stdlib/doc/src/sets.xml b/lib/stdlib/doc/src/sets.xml index 3610bb0184..071ee437cb 100644 --- a/lib/stdlib/doc/src/sets.xml +++ b/lib/stdlib/doc/src/sets.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2000</year><year>2009</year> + <year>2000</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -43,202 +43,141 @@ different if and only if they do not compare equal (<c>==</c>).</p> </description> - <section> - <title>DATA TYPES</title> - <code type="none"> -set() - as returned by new/0</code> - </section> + <datatypes> + <datatype> + <name><marker id="type-dict">set()</marker></name> + <desc><p>As returned by <c>new/0</c>.</p></desc> + </datatype> + </datatypes> <funcs> <func> - <name>new() -> Set</name> + <name name="new" arity="0"/> <fsummary>Return an empty set</fsummary> - <type> - <v>Set = set()</v> - </type> <desc> <p>Returns a new empty set.</p> </desc> </func> <func> - <name>is_set(Set) -> bool()</name> + <name name="is_set" arity="1"/> <fsummary>Test for an <c>Set</c></fsummary> - <type> - <v>Set = term()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>Set</c> is a set of + <p>Returns <c>true</c> if <c><anno>Set</anno></c> is a set of elements, otherwise <c>false</c>.</p> </desc> </func> <func> - <name>size(Set) -> int()</name> + <name name="size" arity="1"/> <fsummary>Return the number of elements in a set</fsummary> - <type> - <v>Set = term()</v> - </type> <desc> - <p>Returns the number of elements in <c>Set</c>.</p> + <p>Returns the number of elements in <c><anno>Set</anno></c>.</p> </desc> </func> <func> - <name>to_list(Set) -> List</name> + <name name="to_list" arity="1"/> <fsummary>Convert an <c>Set</c>into a list</fsummary> - <type> - <v>Set = set()</v> - <v>List = [term()]</v> - </type> <desc> - <p>Returns the elements of <c>Set</c> as a list.</p> + <p>Returns the elements of <c><anno>Set</anno></c> as a list.</p> </desc> </func> <func> - <name>from_list(List) -> Set</name> + <name name="from_list" arity="1"/> <fsummary>Convert a list into an <c>Set</c></fsummary> - <type> - <v>List = [term()]</v> - <v>Set = set()</v> - </type> <desc> - <p>Returns an set of the elements in <c>List</c>.</p> + <p>Returns an set of the elements in <c><anno>List</anno></c>.</p> </desc> </func> <func> - <name>is_element(Element, Set) -> bool()</name> + <name name="is_element" arity="2"/> <fsummary>Test for membership of an <c>Set</c></fsummary> - <type> - <v>Element = term()</v> - <v>Set = set()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>Element</c> is an element of - <c>Set</c>, otherwise <c>false</c>.</p> + <p>Returns <c>true</c> if <c><anno>Element</anno></c> is an element of + <c><anno>Set</anno></c>, otherwise <c>false</c>.</p> </desc> </func> <func> - <name>add_element(Element, Set1) -> Set2</name> + <name name="add_element" arity="2"/> <fsummary>Add an element to an <c>Set</c></fsummary> - <type> - <v>Element = term()</v> - <v>Set1 = Set2 = set()</v> - </type> <desc> - <p>Returns a new set formed from <c>Set1</c> with - <c>Element</c> inserted.</p> + <p>Returns a new set formed from <c><anno>Set1</anno></c> with + <c><anno>Element</anno></c> inserted.</p> </desc> </func> <func> - <name>del_element(Element, Set1) -> Set2</name> + <name name="del_element" arity="2"/> <fsummary>Remove an element from an <c>Set</c></fsummary> - <type> - <v>Element = term()</v> - <v>Set1 = Set2 = set()</v> - </type> <desc> - <p>Returns <c>Set1</c>, but with <c>Element</c> removed.</p> + <p>Returns <c><anno>Set1</anno></c>, but with <c><anno>Element</anno></c> removed.</p> </desc> </func> <func> - <name>union(Set1, Set2) -> Set3</name> + <name name="union" arity="2"/> <fsummary>Return the union of two <c>Sets</c></fsummary> - <type> - <v>Set1 = Set2 = Set3 = set()</v> - </type> <desc> - <p>Returns the merged (union) set of <c>Set1</c> and - <c>Set2</c>.</p> + <p>Returns the merged (union) set of <c><anno>Set1</anno></c> and + <c><anno>Set2</anno></c>.</p> </desc> </func> <func> - <name>union(SetList) -> Set</name> + <name name="union" arity="1"/> <fsummary>Return the union of a list of <c>Sets</c></fsummary> - <type> - <v>SetList = [set()]</v> - <v>Set = set()</v> - </type> <desc> <p>Returns the merged (union) set of the list of sets.</p> </desc> </func> <func> - <name>intersection(Set1, Set2) -> Set3</name> + <name name="intersection" arity="2"/> <fsummary>Return the intersection of two <c>Sets</c></fsummary> - <type> - <v>Set1 = Set2 = Set3 = set()</v> - </type> <desc> - <p>Returns the intersection of <c>Set1</c> and - <c>Set2</c>.</p> + <p>Returns the intersection of <c><anno>Set1</anno></c> and + <c><anno>Set2</anno></c>.</p> </desc> </func> <func> - <name>intersection(SetList) -> Set</name> + <name name="intersection" arity="1"/> <fsummary>Return the intersection of a list of <c>Sets</c></fsummary> - <type> - <v>SetList = [set()]</v> - <v>Set = set()</v> - </type> <desc> <p>Returns the intersection of the non-empty list of sets.</p> </desc> </func> <func> - <name>is_disjoint(Set1, Set2) -> bool()</name> + <name name="is_disjoint" arity="2"/> <fsummary>Check whether two <c>Sets</c> are disjoint</fsummary> - <type> - <v>Set1 = Set2 = set()</v> - </type> <desc> - <p>Returns <c>true</c> if <c>Set1</c> and - <c>Set2</c> are disjoint (have no elements in common), + <p>Returns <c>true</c> if <c><anno>Set1</anno></c> and + <c><anno>Set2</anno></c> are disjoint (have no elements in common), and <c>false</c> otherwise.</p> </desc> </func> <func> - <name>subtract(Set1, Set2) -> Set3</name> + <name name="subtract" arity="2"/> <fsummary>Return the difference of two <c>Sets</c></fsummary> - <type> - <v>Set1 = Set2 = Set3 = set()</v> - </type> <desc> - <p>Returns only the elements of <c>Set1</c> which are not - also elements of <c>Set2</c>.</p> + <p>Returns only the elements of <c><anno>Set1</anno></c> which are not + also elements of <c><anno>Set2</anno></c>.</p> </desc> </func> <func> - <name>is_subset(Set1, Set2) -> bool()</name> + <name name="is_subset" arity="2"/> <fsummary>Test for subset</fsummary> - <type> - <v>Set1 = Set2 = set()</v> - </type> <desc> - <p>Returns <c>true</c> when every element of <c>Set</c>1 is - also a member of <c>Set2</c>, otherwise <c>false</c>.</p> + <p>Returns <c>true</c> when every element of <c><anno>Set1</anno></c>1 is + also a member of <c><anno>Set2</anno></c>, otherwise <c>false</c>.</p> </desc> </func> <func> - <name>fold(Function, Acc0, Set) -> Acc1</name> + <name name="fold" arity="3"/> <fsummary>Fold over set elements</fsummary> - <type> - <v>Function = fun (E, AccIn) -> AccOut</v> - <v>Acc0 = Acc1 = AccIn = AccOut = term()</v> - <v>Set = set()</v> - </type> <desc> - <p>Fold <c>Function</c> over every element in <c>Set</c> + <p>Fold <c><anno>Function</anno></c> over every element in <c><anno>Set</anno></c> returning the final value of the accumulator.</p> </desc> </func> <func> - <name>filter(Pred, Set1) -> Set2</name> + <name name="filter" arity="2"/> <fsummary>Filter set elements</fsummary> - <type> - <v>Pred = fun (E) -> bool()</v> - <v>Set1 = Set2 = set()</v> - </type> <desc> - <p>Filter elements in <c>Set1</c> with boolean function - <c>Fun</c>.</p> + <p>Filter elements in <c><anno>Set1</anno></c> with boolean function + <c><anno>Pred</anno></c>.</p> </desc> </func> </funcs> diff --git a/lib/stdlib/doc/src/shell.xml b/lib/stdlib/doc/src/shell.xml index 73cc1b33bd..bc2120c37d 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>2010</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -763,26 +763,20 @@ loop(N) -> <funcs> <func> - <name>history(N) -> integer()</name> + <name name="history" arity="1"/> <fsummary>Sets the number of previous commands to keep</fsummary> - <type> - <v>N = integer()</v> - </type> <desc> <p>Sets the number of previous commands to keep in the - history list to <c>N</c>. The previous number is returned. + history list to <c><anno>N</anno></c>. The previous number is returned. The default number is 20.</p> </desc> </func> <func> - <name>results(N) -> integer()</name> + <name name="results" arity="1"/> <fsummary>Sets the number of previous results to keep</fsummary> - <type> - <v>N = integer()</v> - </type> <desc> <p>Sets the number of results from previous commands to keep in - the history list to <c>N</c>. The previous number is returned. + the history list to <c><anno>N</anno></c>. The previous number is returned. The default number is 20.</p> </desc> </func> @@ -790,7 +784,7 @@ loop(N) -> <name>catch_exception(Bool) -> Bool</name> <fsummary>Sets the exception handling of the shell</fsummary> <type> - <v>Bool = bool()</v> + <v>Bool = boolean()</v> </type> <desc> <p>Sets the exception handling of the evaluator process. The @@ -804,38 +798,29 @@ loop(N) -> </desc> </func> <func> - <name>prompt_func(PromptFunc) -> prompt_func()</name> + <name name="prompt_func" arity="1"/> <fsummary>Sets the shell prompt</fsummary> - <type> - <v>PromptFunc = prompt_func()</v> - <v>prompt_func() = default | {Mod, Func}</v> - <v>Mod = Func = atom()</v> - </type> <desc> <p>Sets the shell prompt function to <c>PromptFunc</c>. The previous prompt function is returned.</p> </desc> </func> <func> - <name>start_restricted(Module) -> ok | {error, Reason}</name> + <name name="start_restricted" arity="1"/> <fsummary>Exits a normal shell and starts a restricted shell.</fsummary> - <type> - <v>Module = atom()</v> - <v>Reason = atom()</v> - </type> <desc> <p>Exits a normal shell and starts a restricted - shell. <c>Module</c> specifies the callback module for the + shell. <c><anno>Module</anno></c> specifies the callback module for the functions <c>local_allowed/3</c> and <c>non_local_allowed/3</c>. The function is meant to be called from the shell.</p> <p>If the callback module cannot be loaded, an error tuple is - returned. The <c>Reason</c> in the error tuple is the one + returned. The <c><anno>Reason</anno></c> in the error tuple is the one returned by the code loader when trying to load the code of the callback module.</p> </desc> </func> <func> - <name>stop_restricted() -> ok</name> + <name name="stop_restricted" arity="0"/> <fsummary>Exits a restricted shell and starts a normal shell.</fsummary> <desc> <p>Exits a restricted shell and starts a normal shell. The function diff --git a/lib/stdlib/doc/src/shell_default.xml b/lib/stdlib/doc/src/shell_default.xml index 4f8cc6c5bb..f7e7d5388a 100644 --- a/lib/stdlib/doc/src/shell_default.xml +++ b/lib/stdlib/doc/src/shell_default.xml @@ -5,7 +5,7 @@ <header> <copyright> <year>1996</year> - <year>2007</year> + <year>2011</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> diff --git a/lib/stdlib/doc/src/slave.xml b/lib/stdlib/doc/src/slave.xml index 168d83f301..15b6711731 100644 --- a/lib/stdlib/doc/src/slave.xml +++ b/lib/stdlib/doc/src/slave.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2009</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -56,22 +56,16 @@ </description> <funcs> <func> - <name>start(Host) -></name> - <name>start(Host, Name) -></name> - <name>start(Host, Name, Args) -> {ok, Node} | {error, Reason}</name> + <name name="start" arity="1"/> + <name name="start" arity="2"/> + <name name="start" arity="3"/> <fsummary>Start a slave node on a host</fsummary> - <type> - <v>Host = Name = atom()</v> - <v>Args = string()</v> - <v>Node = node()</v> - <v>Reason = timeout | no_rsh | {already_running, Node}</v> - </type> <desc> - <p>Starts a slave node on the host <c>Host</c>. Host names need + <p>Starts a slave node on the host <c><anno>Host</anno></c>. Host names need not necessarily be specified as fully qualified names; short names can also be used. This is the same condition that applies to names of distributed Erlang nodes.</p> - <p>The name of the started node will be <c>Name@Host</c>. If no + <p>The name of the started node will be <c><anno>Name</anno>@<anno>Host</anno></c>. If no name is provided, the name will be the same as the node which executes the call (with the exception of the host name part of the node name).</p> @@ -79,7 +73,7 @@ terminal I/O which is produced at the slave is automatically relayed to the master. Also, the file process will be relayed to the master.</p> - <p>The <c>Args</c> argument is used to set <c>erl</c> command + <p>The <c><anno>Args</anno></c> argument is used to set <c>erl</c> command line arguments. If provided, it is passed to the new node and can be used for a variety of purposes. See <seealso marker="erts:erl#erl">erl(1)</seealso></p> @@ -103,9 +97,9 @@ E = " -env DISPLAY " ++ net_adm:localhost() ++ ":0 ", Arg = "-mnesia_dir " ++ M ++ " -pa " ++ Dir ++ E, slave:start(H, Name, Arg).</code> - <p>If successful, the function returns <c>{ok, Node}</c>, - where <c>Node</c> is the name of the new node. Otherwise it - returns <c>{error, Reason}</c>, where <c>Reason</c> can be + <p>If successful, the function returns <c>{ok, <anno>Node</anno>}</c>, + where <c><anno>Node</anno></c> is the name of the new node. Otherwise it + returns <c>{error, <anno>Reason</anno>}</c>, where <c><anno>Reason</anno></c> can be one of:</p> <taglist> <tag><c>timeout</c></tag> @@ -123,24 +117,18 @@ slave:start(H, Name, Arg).</code> <item> <p>There is no <c>rsh</c> program on the computer.</p> </item> - <tag><c>{already_running, Node}</c></tag> + <tag><c>{already_running, <anno>Node</anno>}</c></tag> <item> - <p>A node with the name <c>Name@Host</c> already exists.</p> + <p>A node with the name <c><anno>Name</anno>@<anno>Host</anno></c> already exists.</p> </item> </taglist> </desc> </func> <func> - <name>start_link(Host) -></name> - <name>start_link(Host, Name) -></name> - <name>start_link(Host, Name, Args) -> {ok, Node} | {error, Reason}</name> + <name name="start_link" arity="1"/> + <name name="start_link" arity="2"/> + <name name="start_link" arity="3"/> <fsummary>Start and link to a slave node on a host</fsummary> - <type> - <v>Host = Name = atom()</v> - <v>Args = string()</v> - <v>Node = node()</v> - <v>Reason = timeout | no_rsh | {already_running, Node}</v> - </type> <desc> <p>Starts a slave node in the same way as <c>start/1,2,3</c>, except that the slave node is linked to the currently @@ -151,11 +139,8 @@ slave:start(H, Name, Arg).</code> </desc> </func> <func> - <name>stop(Node) -> ok</name> + <name name="stop" arity="1"/> <fsummary>Stop (kill) a node</fsummary> - <type> - <v>Node = node()</v> - </type> <desc> <p>Stops (kills) a node.</p> </desc> @@ -177,12 +162,8 @@ slave:start(H, Name, Arg).</code> </desc> </func> <func> - <name>pseudo(Master, ServerList) -> ok</name> + <name name="pseudo" arity="2"/> <fsummary>Start a number of pseudo servers</fsummary> - <type> - <v>Master = node()</v> - <v>ServerList = [atom()]</v> - </type> <desc> <p>Starts a number of pseudo servers. A pseudo server is a server with a registered name which does absolutely nothing @@ -198,16 +179,13 @@ rpc:call(N, slave, pseudo, [node(), [pxw_server]]).</code> </desc> </func> <func> - <name>relay(Pid)</name> + <name name="relay" arity="1"/> <fsummary>Run a pseudo server</fsummary> - <type> - <v>Pid = pid()</v> - </type> <desc> <p>Runs a pseudo server. This function never returns any value and the process which executes the function will receive messages. All messages received will simply be passed on to - <c>Pid</c>.</p> + <c><anno>Pid</anno></c>.</p> </desc> </func> </funcs> diff --git a/lib/stdlib/doc/src/sofs.xml b/lib/stdlib/doc/src/sofs.xml index 8c8ae51262..2e7768a1df 100644 --- a/lib/stdlib/doc/src/sofs.xml +++ b/lib/stdlib/doc/src/sofs.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2001</year><year>2010</year> + <year>2001</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -181,10 +181,11 @@ the <marker id="canonical_map"></marker><em>canonical map</em> is the function that maps every element of X onto its equivalence class. </p> - <p>Relations as defined above (as sets of ordered pairs) will from - now on be referred to as <em>binary relations</em>. We call a - set of ordered sets (x[1], ..., x[n]) - an <em>(n-ary) relation</em>, and say that the relation is a subset of + <p><marker id="binary_relation"></marker>Relations as defined above + (as sets of ordered pairs) will from now on be referred to as + <em>binary relations</em>. We call a set of ordered sets + (x[1], ..., x[n]) an <marker id="n_ary_relation"></marker> + <em>(n-ary) relation</em>, and say that the relation is a subset of the <marker id="Cartesian_product_tuple"></marker>Cartesian product X[1] × ... × X[n] where x[i] is an element of X[i], 1 <= i <= n. @@ -210,7 +211,7 @@ X[i] to Y[i] and S a subset of X[1] × ... × X[n]. The <marker id="multiple_relative_product"></marker><em>multiple - relative product</em> of TR and and S is defined to be the + relative product</em> of TR and S is defined to be the set {z : z = ((x[1], ..., x[n]), (y[1],...,y[n])) for some (x[1], ..., x[n]) in S and for some (x[i], y[i]) in R[i], @@ -293,7 +294,8 @@ <c>partition_family/2</c>, <c>projection/2</c>, <c>restriction/3</c>, <c>substitution/2</c>) accept an Erlang function as a means to modify each element of a given unordered - set. Such a function, called SetFun in the following, can be + set. <marker id="set_fun"></marker>Such a function, called + SetFun in the following, can be specified as a functional object (fun), a tuple <c>{external, Fun}</c>, or an integer. If SetFun is specified as a fun, the fun is applied to each element of the @@ -337,34 +339,73 @@ fun(S) -> sofs:partition(1, S) end message when given badly formed arguments or sets the types of which are not compatible.</p> <p>When comparing external sets the operator <c>==/2</c> is used.</p> - <p><em>Types</em></p> - <pre> -anyset() = - an unordered, ordered or atomic set - -binary_relation() = - a binary relation - -bool() = true | false -external_set() = - an external set - -family() = - a family (of subsets) - -function() = - a function - -ordset() = - an ordered set - -relation() = - an n-ary relation - -set() = - an unordered set - -set_of_sets() = - an unordered set of set() - -set_fun() = integer() >= 1 - | {external, fun(external_set()) -> external_set()} - | fun(anyset()) -> anyset() -spec_fun() = {external, fun(external_set()) -> bool()} - | fun(anyset()) -> bool() -type() = - a type - </pre> </description> + <datatypes> + <datatype> + <name name="anyset"></name> + <desc><p>Any kind of set (also included are the atomic sets).</p></desc> + </datatype> + <datatype> + <name name="binary_relation"></name> + <desc><p>A <seealso marker="#binary_relation">binary + relation</seealso>.</p></desc> + </datatype> + <datatype> + <name name="external_set"></name> + <desc><p>An <seealso marker="#external_set">external + set</seealso>.</p></desc> + </datatype> + <datatype> + <name name="family"></name> + <desc><p>A <seealso marker="#family">family</seealso> (of subsets).</p> + </desc> + </datatype> + <datatype> + <name name="a_function"></name> + <desc><p>A <seealso marker="#function">function</seealso>.</p></desc> + </datatype> + <datatype> + <name name="ordset"></name> + <desc><p>An <seealso marker="#sets_definition">ordered + set</seealso>.</p></desc> + </datatype> + <datatype> + <name name="relation"></name> + <desc><p>An <seealso marker="#n_ary_relation">n-ary relation</seealso>. + </p></desc> + </datatype> + <datatype> + <name name="a_set"></name> + <desc><p>An <seealso marker="#sets_definition">unordered + set</seealso>.</p></desc> + </datatype> + <datatype> + <name name="set_of_sets"></name> + <desc><p>An <seealso marker="#sets_definition">unordered + set</seealso> of unordered sets.</p></desc> + </datatype> + <datatype> + <name name="set_fun"></name> + <desc><p>A <seealso marker="#set_fun">SetFun</seealso>.</p></desc> + </datatype> + <datatype> + <name name="spec_fun"></name> + </datatype> + <datatype> + <name name="type"></name> + <desc><p>A <seealso marker="#type">type</seealso>.</p></desc> + </datatype> + <datatype> + <!-- Parameterized opaque types are NYI: --> + <name><marker id="type-tuple_of">tuple_of(T)</marker></name> + <desc><p>A tuple where the elements are of type <c>T</c>.</p></desc> + </datatype> + </datatypes> <funcs> <func> - <name>a_function(Tuples [, Type]) -> Function</name> + <name name="a_function" arity="1"/> + <name name="a_function" arity="2"/> <fsummary>Create a function.</fsummary> - <type> - <v>Function = function()</v> - <v>Tuples = [tuple()]</v> - <v>Type = type()</v> - </type> <desc> <p>Creates a <seealso marker="#function">function</seealso>. <c>a_function(F, T)</c> is equivalent to @@ -375,16 +416,12 @@ type() = - a type - </pre> </desc> </func> <func> - <name>canonical_relation(SetOfSets) -> BinRel</name> + <name name="canonical_relation" arity="1"/> <fsummary>Return the canonical map.</fsummary> - <type> - <v>BinRel = binary_relation()</v> - <v>SetOfSets = set_of_sets()</v> - </type> <desc> <p>Returns the binary relation containing the elements - (E, Set) such that Set belongs to SetOfSets and E - belongs to Set. If SetOfSets is + (E, Set) such that Set belongs to <anno>SetOfSets</anno> and E + belongs to Set. If SetOfSets is a <seealso marker="#partition">partition</seealso> of a set X and R is the equivalence relation in X induced by SetOfSets, then the returned relation is @@ -398,14 +435,12 @@ type() = - a type - </pre> </desc> </func> <func> - <name>composite(Function1, Function2) -> Function3</name> + <name name="composite" arity="2"/> <fsummary>Return the composite of two functions.</fsummary> - <type> - <v>Function1 = Function2 = Function3 = function()</v> - </type> <desc> <p>Returns the <seealso marker="#composite">composite</seealso> of - the functions Function1 and Function2.</p> + the functions <anno>Function1</anno> and + <anno>Function2</anno>.</p> <pre> 1> <input>F1 = sofs:a_function([{a,1},{b,2},{c,2}]),</input> <input>F2 = sofs:a_function([{1,x},{2,y},{3,z}]),</input> @@ -415,14 +450,9 @@ type() = - a type - </pre> </desc> </func> <func> - <name>constant_function(Set, AnySet) -> Function</name> + <name name="constant_function" arity="2"/> <fsummary>Create the function that maps each element of a set onto another set.</fsummary> - <type> - <v>AnySet = anyset()</v> - <v>Function = function()</v> - <v>Set = set()</v> - </type> <desc> <p>Creates the <seealso marker="#function">function</seealso> that maps each element of the set Set onto AnySet.</p> @@ -435,14 +465,11 @@ type() = - a type - </pre> </desc> </func> <func> - <name>converse(BinRel1) -> BinRel2</name> + <name name="converse" arity="1"/> <fsummary>Return the converse of a binary relation.</fsummary> - <type> - <v>BinRel1 = BinRel2 = binary_relation()</v> - </type> <desc> <p>Returns the <seealso marker="#converse">converse</seealso> - of the binary relation BinRel1.</p> + of the binary relation <anno>BinRel1</anno>.</p> <pre> 1> <input>R1 = sofs:relation([{1,a},{2,b},{3,a}]),</input> <input>R2 = sofs:converse(R1),</input> @@ -451,31 +478,25 @@ type() = - a type - </pre> </desc> </func> <func> - <name>difference(Set1, Set2) -> Set3</name> + <name name="difference" arity="2"/> <fsummary>Return the difference of two sets.</fsummary> - <type> - <v>Set1 = Set2 = Set3 = set()</v> - </type> <desc> <p>Returns the <seealso marker="#difference">difference</seealso> of - the sets Set1 and Set2.</p> + the sets <anno>Set1</anno> and <anno>Set2</anno>.</p> </desc> </func> <func> - <name>digraph_to_family(Graph [, Type]) -> Family</name> + <name name="digraph_to_family" arity="1"/> + <name name="digraph_to_family" arity="2"/> <fsummary>Create a family from a directed graph.</fsummary> - <type> - <v>Graph = digraph() - see digraph(3) -</v> - <v>Family = family()</v> - <v>Type = type()</v> - </type> <desc> <p>Creates a <seealso marker="#family">family</seealso> from - the directed graph Graph. Each vertex a of Graph is + the directed graph <anno>Graph</anno>. Each vertex a of + <anno>Graph</anno> is represented by a pair (a, {b[1], ..., b[n]}) where the b[i]'s are the out-neighbours of a. If no type is explicitly given, [{atom, [atom]}] is used as type of - the family. It is assumed that Type is + the family. It is assumed that <anno>Type</anno> is a <seealso marker="#valid_type">valid type</seealso> of the external set of the family.</p> <p>If G is a directed graph, it holds that the vertices and @@ -484,15 +505,11 @@ type() = - a type - </pre> </desc> </func> <func> - <name>domain(BinRel) -> Set</name> + <name name="domain" arity="1"/> <fsummary>Return the domain of a binary relation.</fsummary> - <type> - <v>BinRel = binary_relation()</v> - <v>Set = set()</v> - </type> <desc> <p>Returns the <seealso marker="#domain">domain</seealso> of - the binary relation BinRel.</p> + the binary relation <anno>BinRel</anno>.</p> <pre> 1> <input>R = sofs:relation([{1,a},{1,b},{2,b},{2,c}]),</input> <input>S = sofs:domain(R),</input> @@ -501,16 +518,13 @@ type() = - a type - </pre> </desc> </func> <func> - <name>drestriction(BinRel1, Set) -> BinRel2</name> + <name name="drestriction" arity="2"/> <fsummary>Return a restriction of a binary relation.</fsummary> - <type> - <v>BinRel1 = BinRel2 = binary_relation()</v> - <v>Set = set()</v> - </type> <desc> - <p>Returns the difference between the binary relation BinRel1 + <p>Returns the difference between the binary relation + <anno>BinRel1</anno> and the <seealso marker="#restriction">restriction</seealso> - of BinRel1 to Set.</p> + of <anno>BinRel1</anno> to <anno>Set</anno>.</p> <pre> 1> <input>R1 = sofs:relation([{1,a},{2,b},{3,c}]),</input> <input>S = sofs:set([2,4,6]),</input> @@ -522,16 +536,13 @@ type() = - a type - </pre> </desc> </func> <func> - <name>drestriction(SetFun, Set1, Set2) -> Set3</name> + <name name="drestriction" arity="3"/> <fsummary>Return a restriction of a relation.</fsummary> - <type> - <v>SetFun = set_fun()</v> - <v>Set1 = Set2 = Set3 = set()</v> - </type> - <desc> - <p>Returns a subset of Set1 containing those elements that do - not yield an element in Set2 as the result of applying - SetFun.</p> + <desc> + <p>Returns a subset of <anno>Set1</anno> containing those elements + that do + not yield an element in <anno>Set2</anno> as the result of applying + <anno>SetFun</anno>.</p> <pre> 1> <input>SetFun = {external, fun({_A,B,C}) -> {B,C} end},</input> <input>R1 = sofs:relation([{a,aa,1},{b,bb,2},{c,cc,3}]),</input> @@ -544,11 +555,8 @@ type() = - a type - </pre> </desc> </func> <func> - <name>empty_set() -> Set</name> + <name name="empty_set" arity="0"/> <fsummary>Return the untyped empty set.</fsummary> - <type> - <v>Set = set()</v> - </type> <desc> <p>Returns the <seealso marker="#sets_definition">untyped empty set</seealso>. <c>empty_set()</c> is equivalent to @@ -556,19 +564,14 @@ type() = - a type - </pre> </desc> </func> <func> - <name>extension(BinRel1, Set, AnySet) -> BinRel2</name> + <name name="extension" arity="3"/> <fsummary>Extend the domain of a binary relation.</fsummary> - <type> - <v>AnySet = anyset()</v> - <v>BinRel1 = BinRel2 = binary_relation()</v> - <v>Set = set()</v> - </type> <desc> <p>Returns the <seealso marker="#extension">extension</seealso> of - BinRel1 such that - for each element E in Set that does not belong to the - <seealso marker="#domain">domain</seealso> of BinRel1, - BinRel2 contains the pair (E, AnySet).</p> + <anno>BinRel1</anno> such that + for each element E in <anno>Set</anno> that does not belong to the + <seealso marker="#domain">domain</seealso> of <anno>BinRel1</anno>, + <anno>BinRel2</anno> contains the pair (E, AnySet).</p> <pre> 1> <input>S = sofs:set([b,c]),</input> <input>A = sofs:empty_set(),</input> @@ -579,13 +582,9 @@ type() = - a type - </pre> </desc> </func> <func> - <name>family(Tuples [, Type]) -> Family</name> + <name name="family" arity="1"/> + <name name="family" arity="2"/> <fsummary>Create a family of subsets.</fsummary> - <type> - <v>Family = family()</v> - <v>Tuples = [tuple()]</v> - <v>Type = type()</v> - </type> <desc> <p>Creates a <seealso marker="#family">family of subsets</seealso>. <c>family(F, T)</c> is equivalent to @@ -596,18 +595,17 @@ type() = - a type - </pre> </desc> </func> <func> - <name>family_difference(Family1, Family2) -> Family3</name> + <name name="family_difference" arity="2"/> <fsummary>Return the difference of two families.</fsummary> - <type> - <v>Family1 = Family2 = Family3 = family()</v> - </type> <desc> - <p>If Family1 and Family2 + <p>If <anno>Family1</anno> and <anno>Family2</anno> are <seealso marker="#family">families</seealso>, then - Family3 is the family + <anno>Family3</anno> is the family such that the index set is equal to the index set of - Family1, and Family3[i] is the difference between Family1[i] - and Family2[i] if Family2 maps i, Family1[i] otherwise.</p> + <anno>Family1</anno>, and <anno>Family3</anno>[i] is the + difference between <anno>Family1</anno>[i] + and <anno>Family2</anno>[i] if <anno>Family2</anno> maps i, + <anno>Family1</anno>[i] otherwise.</p> <pre> 1> <input>F1 = sofs:family([{a,[1,2]},{b,[3,4]}]),</input> <input>F2 = sofs:family([{b,[4,5]},{c,[6,7]}]),</input> @@ -617,17 +615,18 @@ type() = - a type - </pre> </desc> </func> <func> - <name>family_domain(Family1) -> Family2</name> + <name name="family_domain" arity="1"/> <fsummary>Return a family of domains.</fsummary> - <type> - <v>Family1 = Family2 = family()</v> - </type> - <desc> - <p>If Family1 is a <seealso marker="#family">family</seealso> - and Family1[i] is a binary relation for every i in the index - set of Family1, then Family2 is the family with the same - index set as Family1 such that Family2[i] is - the <seealso marker="#domain">domain</seealso> of Family1[i].</p> + <desc> + <p>If <anno>Family1</anno> is + a <seealso marker="#family">family</seealso> + and <anno>Family1</anno>[i] is a binary relation for every i + in the index set of <anno>Family1</anno>, + then <anno>Family2</anno> is the family with the same index + set as <anno>Family1</anno> such + that <anno>Family2</anno>[i] is + the <seealso marker="#domain">domain</seealso> of + <anno>Family1</anno>[i].</p> <pre> 1> <input>FR = sofs:from_term([{a,[{1,a},{2,b},{3,c}]},{b,[]},{c,[{4,d},{5,e}]}]),</input> <input>F = sofs:family_domain(FR),</input> @@ -636,17 +635,18 @@ type() = - a type - </pre> </desc> </func> <func> - <name>family_field(Family1) -> Family2</name> + <name name="family_field" arity="1"/> <fsummary>Return a family of fields.</fsummary> - <type> - <v>Family1 = Family2 = family()</v> - </type> - <desc> - <p>If Family1 is a <seealso marker="#family">family</seealso> - and Family1[i] is a binary relation for every i in the index - set of Family1, then Family2 is the family with the same - index set as Family1 such that Family2[i] is - the <seealso marker="#field">field</seealso> of Family1[i].</p> + <desc> + <p>If <anno>Family1</anno> is + a <seealso marker="#family">family</seealso> + and <anno>Family1</anno>[i] is a binary relation for every i + in the index set of <anno>Family1</anno>, + then <anno>Family2</anno> is the family with the same index + set as <anno>Family1</anno> such + that <anno>Family2</anno>[i] is + the <seealso marker="#field">field</seealso> of + <anno>Family1</anno>[i].</p> <pre> 1> <input>FR = sofs:from_term([{a,[{1,a},{2,b},{3,c}]},{b,[]},{c,[{4,d},{5,e}]}]),</input> <input>F = sofs:family_field(FR),</input> @@ -657,21 +657,21 @@ type() = - a type - </pre> </desc> </func> <func> - <name>family_intersection(Family1) -> Family2</name> + <name name="family_intersection" arity="1"/> <fsummary>Return the intersection of a family of sets of sets.</fsummary> - <type> - <v>Family1 = Family2 = family()</v> - </type> - <desc> - <p>If Family1 is a <seealso marker="#family">family</seealso> - and Family1[i] is a set of sets for every i in the index set - of Family1, then Family2 is the family with the same index - set as Family1 such that Family2[i] is - the <seealso marker="#intersection_n">intersection</seealso> of - Family1[i].</p> - <p>If Family1[i] is an empty set for some i, then the process - exits with a <c>badarg</c> message.</p> + <desc> + <p>If <anno>Family1</anno> is + a <seealso marker="#family">family</seealso> + and <anno>Family1</anno>[i] is a set of sets for every i in + the index set of <anno>Family1</anno>, + then <anno>Family2</anno> is the family with the same index + set as <anno>Family1</anno> such + that <anno>Family2</anno>[i] is + the <seealso marker="#intersection_n">intersection</seealso> + of <anno>Family1</anno>[i].</p> + <p>If <anno>Family1</anno>[i] is an empty set for some i, then + the process exits with a <c>badarg</c> message.</p> <pre> 1> <input>F1 = sofs:from_term([{a,[[1,2,3],[2,3,4]]},{b,[[x,y,z],[x,y]]}]),</input> <input>F2 = sofs:family_intersection(F1),</input> @@ -680,17 +680,16 @@ type() = - a type - </pre> </desc> </func> <func> - <name>family_intersection(Family1, Family2) -> Family3</name> + <name name="family_intersection" arity="2"/> <fsummary>Return the intersection of two families.</fsummary> - <type> - <v>Family1 = Family2 = Family3 = family()</v> - </type> - <desc> - <p>If Family1 and Family2 - are <seealso marker="#family">families</seealso>, then Family3 - is the family such that the index set is the intersection of - Family1's and Family2's index sets, and Family3[i] is the - intersection of Family1[i] and Family2[i].</p> + <desc> + <p>If <anno>Family1</anno> and <anno>Family2</anno> + are <seealso marker="#family">families</seealso>, + then <anno>Family3</anno> is the family such that the index + set is the intersection of <anno>Family1</anno>'s and + <anno>Family2</anno>'s index sets, + and <anno>Family3</anno>[i] is the intersection of + <anno>Family1</anno>[i] and <anno>Family2</anno>[i].</p> <pre> 1> <input>F1 = sofs:family([{a,[1,2]},{b,[3,4]},{c,[5,6]}]),</input> <input>F2 = sofs:family([{b,[4,5]},{c,[7,8]},{d,[9,10]}]),</input> @@ -700,18 +699,16 @@ type() = - a type - </pre> </desc> </func> <func> - <name>family_projection(SetFun, Family1) -> Family2</name> + <name name="family_projection" arity="2"/> <fsummary>Return a family of modified subsets.</fsummary> - <type> - <v>SetFun = set_fun()</v> - <v>Family1 = Family2 = family()</v> - <v>Set = set()</v> - </type> - <desc> - <p>If Family1 is a <seealso marker="#family">family</seealso> - then Family2 is the family with the same index set as - Family1 such that Family2[i] is the result of calling SetFun - with Family1[i] as argument.</p> + <desc> + <p>If <anno>Family1</anno> is + a <seealso marker="#family">family</seealso> + then <anno>Family2</anno> is the family with the same index + set as <anno>Family1</anno> such + that <anno>Family2</anno>[i] is the result of + calling <anno>SetFun</anno> with <anno>Family1</anno>[i] as + argument.</p> <pre> 1> <input>F1 = sofs:from_term([{a,[[1,2],[2,3]]},{b,[[]]}]),</input> <input>F2 = sofs:family_projection({sofs, union}, F1),</input> @@ -720,17 +717,18 @@ type() = - a type - </pre> </desc> </func> <func> - <name>family_range(Family1) -> Family2</name> + <name name="family_range" arity="1"/> <fsummary>Return a family of ranges.</fsummary> - <type> - <v>Family1 = Family2 = family()</v> - </type> - <desc> - <p>If Family1 is a <seealso marker="#family">family</seealso> - and Family1[i] is a binary relation for every i in the index - set of Family1, then Family2 is the family with the same - index set as Family1 such that Family2[i] is - the <seealso marker="#range">range</seealso> of Family1[i].</p> + <desc> + <p>If <anno>Family1</anno> is + a <seealso marker="#family">family</seealso> + and <anno>Family1</anno>[i] is a binary relation for every i + in the index set of <anno>Family1</anno>, + then <anno>Family2</anno> is the family with the same index + set as <anno>Family1</anno> such + that <anno>Family2</anno>[i] is + the <seealso marker="#range">range</seealso> of + <anno>Family1</anno>[i].</p> <pre> 1> <input>FR = sofs:from_term([{a,[{1,a},{2,b},{3,c}]},{b,[]},{c,[{4,d},{5,e}]}]),</input> <input>F = sofs:family_range(FR),</input> @@ -739,22 +737,21 @@ type() = - a type - </pre> </desc> </func> <func> - <name>family_specification(Fun, Family1) -> Family2</name> + <name name="family_specification" arity="2"/> <fsummary>Select a subset of a family using a predicate.</fsummary> - <type> - <v>Fun = spec_fun()</v> - <v>Family1 = Family2 = family()</v> - </type> - <desc> - <p>If Family1 is a <seealso marker="#family">family</seealso>, - then Family2 is - the <seealso marker="#restriction">restriction</seealso> of - Family1 to those elements i of the - index set for which Fun applied to Family1[i] returns - <c>true</c>. If Fun is a tuple <c>{external, Fun2}</c>, - Fun2 is applied to - the <seealso marker="#external_set">external set</seealso> of - Family1[i], otherwise Fun is applied to Family1[i].</p> + <desc> + <p>If <anno>Family1</anno> is + a <seealso marker="#family">family</seealso>, + then <anno>Family2</anno> is + the <seealso marker="#restriction">restriction</seealso> of + <anno>Family1</anno> to those elements i of the index set + for which <anno>Fun</anno> applied + to <anno>Family1</anno>[i] returns + <c>true</c>. If <anno>Fun</anno> is a + tuple <c>{external, Fun2}</c>, Fun2 is applied to + the <seealso marker="#external_set">external set</seealso> + of <anno>Family1</anno>[i], otherwise <anno>Fun</anno> is + applied to <anno>Family1</anno>[i].</p> <pre> 1> <input>F1 = sofs:family([{a,[1,2,3]},{b,[1,2]},{c,[1]}]),</input> <input>SpecFun = fun(S) -> sofs:no_elements(S) =:= 2 end,</input> @@ -764,24 +761,22 @@ type() = - a type - </pre> </desc> </func> <func> - <name>family_to_digraph(Family [, GraphType]) -> Graph</name> + <name name="family_to_digraph" arity="1"/> + <name name="family_to_digraph" arity="2"/> <fsummary>Create a directed graph from a family.</fsummary> - <type> - <v>Graph = digraph()</v> - <v>Family = family()</v> - <v>GraphType = - see digraph(3) -</v> - </type> <desc> <p>Creates a directed graph from - the <seealso marker="#family">family</seealso> Family. For each - pair (a, {b[1], ..., b[n]}) of Family, the vertex + the <seealso marker="#family">family</seealso> <anno>Family</anno>. + For each pair (a, {b[1], ..., b[n]}) + of <anno>Family</anno>, the vertex a as well the edges (a, b[i]) for 1 <= i <= n are added to a newly created directed graph.</p> - <p>If no graph type is given, <c>digraph:new/1</c> is used for - creating the directed graph, otherwise the GraphType + <p>If no graph type is given <seealso marker="digraph#new/0"> + digraph:new/0</seealso> is used for + creating the directed graph, otherwise the <anno>GraphType</anno> argument is passed on as second argument to - <c>digraph:new/2</c>.</p> + <seealso marker="digraph#new/1">digraph:new/1</seealso>.</p> <p>It F is a family, it holds that F is a subset of <c>digraph_to_family(family_to_digraph(F), type(F))</c>. Equality holds if <c>union_of_family(F)</c> is a subset of @@ -791,17 +786,15 @@ type() = - a type - </pre> </desc> </func> <func> - <name>family_to_relation(Family) -> BinRel</name> + <name name="family_to_relation" arity="1"/> <fsummary>Create a binary relation from a family.</fsummary> - <type> - <v>Family = family()</v> - <v>BinRel = binary_relation()</v> - </type> - <desc> - <p>If Family is a <seealso marker="#family">family</seealso>, - then BinRel is the binary relation containing all pairs - (i, x) such that i belongs to the index set of Family - and x belongs to Family[i].</p> + <desc> + <p>If <anno>Family</anno> is + a <seealso marker="#family">family</seealso>, + then <anno>BinRel</anno> is the binary relation containing + all pairs (i, x) such that i belongs to the index set + of <anno>Family</anno> and x belongs + to <anno>Family</anno>[i].</p> <pre> 1> <input>F = sofs:family([{a,[]}, {b,[1]}, {c,[2,3]}]),</input> <input>R = sofs:family_to_relation(F),</input> @@ -810,17 +803,18 @@ type() = - a type - </pre> </desc> </func> <func> - <name>family_union(Family1) -> Family2</name> + <name name="family_union" arity="1"/> <fsummary>Return the union of a family of sets of sets.</fsummary> - <type> - <v>Family1 = Family2 = family()</v> - </type> - <desc> - <p>If Family1 is a <seealso marker="#family">family</seealso> - and Family1[i] is a set of sets for each i in the index set - of Family1, then Family2 is the family with the same index - set as Family1 such that Family2[i] is - the <seealso marker="#union_n">union</seealso> of Family1[i].</p> + <desc> + <p>If <anno>Family1</anno> is + a <seealso marker="#family">family</seealso> + and <anno>Family1</anno>[i] is a set of sets for each i in + the index set of <anno>Family1</anno>, + then <anno>Family2</anno> is the family with the same index + set as <anno>Family1</anno> such + that <anno>Family2</anno>[i] is + the <seealso marker="#union_n">union</seealso> of + <anno>Family1</anno>[i].</p> <pre> 1> <input>F1 = sofs:from_term([{a,[[1,2],[2,3]]},{b,[[]]}]),</input> <input>F2 = sofs:family_union(F1),</input> @@ -831,18 +825,18 @@ type() = - a type - </pre> </desc> </func> <func> - <name>family_union(Family1, Family2) -> Family3</name> + <name name="family_union" arity="2"/> <fsummary>Return the union of two families.</fsummary> - <type> - <v>Family1 = Family2 = Family3 = family()</v> - </type> - <desc> - <p>If Family1 and Family2 - are <seealso marker="#family">families</seealso>, then Family3 - is the family such that the index set is the union of Family1's - and Family2's index sets, and Family3[i] is the union of - Family1[i] and Family2[i] if both maps i, Family1[i] or - Family2[i] otherwise.</p> + <desc> + <p>If <anno>Family1</anno> and <anno>Family2</anno> + are <seealso marker="#family">families</seealso>, + then <anno>Family3</anno> is the family such that the index + set is the union of <anno>Family1</anno>'s + and <anno>Family2</anno>'s index sets, + and <anno>Family3</anno>[i] is the union + of <anno>Family1</anno>[i] and <anno>Family2</anno>[i] if + both maps i, <anno>Family1</anno>[i] + or <anno>Family2</anno>[i] otherwise.</p> <pre> 1> <input>F1 = sofs:family([{a,[1,2]},{b,[3,4]},{c,[5,6]}]),</input> <input>F2 = sofs:family([{b,[4,5]},{c,[7,8]},{d,[9,10]}]),</input> @@ -852,15 +846,11 @@ type() = - a type - </pre> </desc> </func> <func> - <name>field(BinRel) -> Set</name> + <name name="field" arity="1"/> <fsummary>Return the field of a binary relation.</fsummary> - <type> - <v>BinRel = binary_relation()</v> - <v>Set = set()</v> - </type> <desc> <p>Returns the <seealso marker="#field">field</seealso> of the - binary relation BinRel.</p> + binary relation <anno>BinRel</anno>.</p> <pre> 1> <input>R = sofs:relation([{1,a},{1,b},{2,b},{2,c}]),</input> <input>S = sofs:field(R),</input> @@ -871,31 +861,24 @@ type() = - a type - </pre> </desc> </func> <func> - <name>from_external(ExternalSet, Type) -> AnySet</name> + <name name="from_external" arity="2"/> <fsummary>Create a set.</fsummary> - <type> - <v>ExternalSet = external_set()</v> - <v>AnySet = anyset()</v> - <v>Type = type()</v> - </type> <desc> <p>Creates a set from the <seealso marker="#external_set">external - set</seealso> ExternalSet - and the <seealso marker="#type">type</seealso> Type. It is - assumed that Type is a <seealso marker="#valid_type">valid - type</seealso> of ExternalSet.</p> + set</seealso> <anno>ExternalSet</anno> + and the <seealso marker="#type">type</seealso> <anno>Type</anno>. + It is assumed that <anno>Type</anno> is + a <seealso marker="#valid_type">valid + type</seealso> of <anno>ExternalSet</anno>.</p> </desc> </func> <func> - <name>from_sets(ListOfSets) -> Set</name> + <name name="from_sets" arity="1" clause_i="1"/> <fsummary>Create a set out of a list of sets.</fsummary> - <type> - <v>Set = set()</v> - <v>ListOfSets = [anyset()]</v> - </type> <desc> <p>Returns the <seealso marker="#sets_definition">unordered - set</seealso> containing the sets of the list ListOfSets.</p> + set</seealso> containing the sets of the list + <anno>ListOfSets</anno>.</p> <pre> 1> <input>S1 = sofs:relation([{a,1},{b,2}]),</input> <input>S2 = sofs:relation([{x,3},{y,4}]),</input> @@ -905,38 +888,33 @@ type() = - a type - </pre> </desc> </func> <func> - <name>from_sets(TupleOfSets) -> Ordset</name> + <name name="from_sets" arity="1" clause_i="2"/> <fsummary>Create an ordered set out of a tuple of sets.</fsummary> - <type> - <v>Ordset = ordset()</v> - <v>TupleOfSets = tuple-of(anyset())</v> - </type> <desc> <p>Returns the <seealso marker="#sets_definition">ordered set</seealso> containing the sets of the non-empty tuple - TupleOfSets.</p> + <anno>TupleOfSets</anno>.</p> </desc> </func> <func> - <name>from_term(Term [, Type]) -> AnySet</name> + <name name="from_term" arity="1"/> + <name name="from_term" arity="2"/> <fsummary>Create a set.</fsummary> - <type> - <v>AnySet = anyset()</v> - <v>Term = term()</v> - <v>Type = type()</v> - </type> <desc> <p><marker id="from_term"></marker>Creates an element of <seealso marker="#sets_definition">Sets</seealso> by - traversing the term Term, sorting lists, removing duplicates and + traversing the term <anno>Term</anno>, sorting lists, + removing duplicates and deriving or verifying a <seealso marker="#valid_type">valid type</seealso> for the so obtained external set. An - explicitly given <seealso marker="#type">type</seealso> Type + explicitly given <seealso marker="#type">type</seealso> + <anno>Type</anno> can be used to limit the depth of the traversal; an atomic type stops the traversal, as demonstrated by this example where "foo" and {"foo"} are left unmodified:</p> <pre> -1> <input>S = sofs:from_term([{{"foo"},[1,1]},{"foo",[2,2]}], [{atom,[atom]}]),</input> +1> <input>S = sofs:from_term([{{"foo"},[1,1]},{"foo",[2,2]}], +[{atom,[atom]}]),</input> <input>sofs:to_external(S).</input> [{{"foo"},[1]},{"foo",[2]}]</pre> <p><c>from_term</c> can be used for creating atomic or ordered @@ -963,15 +941,12 @@ type() = - a type - </pre> </desc> </func> <func> - <name>image(BinRel, Set1) -> Set2</name> + <name name="image" arity="2"/> <fsummary>Return the image of a set under a binary relation.</fsummary> - <type> - <v>BinRel = binary_relation()</v> - <v>Set1 = Set2 = set()</v> - </type> <desc> <p>Returns the <seealso marker="#image">image</seealso> of the - set Set1 under the binary relation BinRel.</p> + set <anno>Set1</anno> under the binary + relation <anno>BinRel</anno>.</p> <pre> 1> <input>R = sofs:relation([{1,a},{2,b},{2,c},{3,d}]),</input> <input>S1 = sofs:set([1,2]),</input> @@ -981,42 +956,32 @@ type() = - a type - </pre> </desc> </func> <func> - <name>intersection(SetOfSets) -> Set</name> + <name name="intersection" arity="1"/> <fsummary>Return the intersection of a set of sets.</fsummary> - <type> - <v>Set = set()</v> - <v>SetOfSets = set_of_sets()</v> - </type> <desc> <p>Returns the <seealso marker="#intersection_n">intersection</seealso> of - the set of sets SetOfSets.</p> + the set of sets <anno>SetOfSets</anno>.</p> <p>Intersecting an empty set of sets exits the process with a <c>badarg</c> message.</p> </desc> </func> <func> - <name>intersection(Set1, Set2) -> Set3</name> + <name name="intersection" arity="2"/> <fsummary>Return the intersection of two sets.</fsummary> - <type> - <v>Set1 = Set2 = Set3 = set()</v> - </type> <desc> <p>Returns the <seealso marker="#intersection">intersection</seealso> of - Set1 and Set2.</p> + <anno>Set1</anno> and <anno>Set2</anno>.</p> </desc> </func> <func> - <name>intersection_of_family(Family) -> Set</name> + <name name="intersection_of_family" arity="1"/> <fsummary>Return the intersection of a family.</fsummary> - <type> - <v>Family = family()</v> - <v>Set = set()</v> - </type> <desc> <p>Returns the intersection of - the <seealso marker="#family">family</seealso> Family.</p> + the <seealso marker="#family">family</seealso> <anno>Family</anno>. + </p> <p>Intersecting an empty family exits the process with a <c>badarg</c> message.</p> <pre> @@ -1027,14 +992,11 @@ type() = - a type - </pre> </desc> </func> <func> - <name>inverse(Function1) -> Function2</name> + <name name="inverse" arity="1"/> <fsummary>Return the inverse of a function.</fsummary> - <type> - <v>Function1 = Function2 = function()</v> - </type> <desc> <p>Returns the <seealso marker="#inverse">inverse</seealso> - of the function Function1.</p> + of the function <anno>Function1</anno>.</p> <pre> 1> <input>R1 = sofs:relation([{1,a},{2,b},{3,c}]),</input> <input>R2 = sofs:inverse(R1),</input> @@ -1043,16 +1005,13 @@ type() = - a type - </pre> </desc> </func> <func> - <name>inverse_image(BinRel, Set1) -> Set2</name> + <name name="inverse_image" arity="2"/> <fsummary>Return the inverse image of a set under a binary relation.</fsummary> - <type> - <v>BinRel = binary_relation()</v> - <v>Set1 = Set2 = set()</v> - </type> <desc> - <p>Returns the <seealso marker="#inverse_image">inverse - image</seealso> of Set1 under the binary relation BinRel.</p> + <p>Returns the <seealso marker="#inverse_image">inverse + image</seealso> of <anno>Set1</anno> under the binary + relation <anno>BinRel</anno>.</p> <pre> 1> <input>R = sofs:relation([{1,a},{2,b},{2,c},{3,d}]),</input> <input>S1 = sofs:set([c,d,e]),</input> @@ -1062,52 +1021,38 @@ type() = - a type - </pre> </desc> </func> <func> - <name>is_a_function(BinRel) -> Bool</name> + <name name="is_a_function" arity="1"/> <fsummary>Test for a function.</fsummary> - <type> - <v>Bool = bool()</v> - <v>BinRel = binary_relation()</v> - </type> <desc> - <p>Returns <c>true</c> if the binary relation BinRel is a - <seealso marker="#function">function</seealso> or the + <p>Returns <c>true</c> if the binary relation <anno>BinRel</anno> + is a <seealso marker="#function">function</seealso> or the untyped empty set, <c>false</c> otherwise.</p> </desc> </func> <func> - <name>is_disjoint(Set1, Set2) -> Bool</name> + <name name="is_disjoint" arity="2"/> <fsummary>Test for disjoint sets.</fsummary> - <type> - <v>Bool = bool()</v> - <v>Set1 = Set2 = set()</v> - </type> <desc> - <p>Returns <c>true</c> if Set1 and Set2 + <p>Returns <c>true</c> if <anno>Set1</anno> + and <anno>Set2</anno> are <seealso marker="#disjoint">disjoint</seealso>, <c>false</c> otherwise.</p> </desc> </func> <func> - <name>is_empty_set(AnySet) -> Bool</name> + <name name="is_empty_set" arity="1"/> <fsummary>Test for an empty set.</fsummary> - <type> - <v>AnySet = anyset()</v> - <v>Bool = bool()</v> - </type> <desc> - <p>Returns <c>true</c> if Set is an empty unordered set, - <c>false</c> otherwise.</p> + <p>Returns <c>true</c> if <anno>AnySet</anno> is an empty + unordered set, <c>false</c> otherwise.</p> </desc> </func> <func> - <name>is_equal(AnySet1, AnySet2) -> Bool</name> + <name name="is_equal" arity="2"/> <fsummary>Test two sets for equality.</fsummary> - <type> - <v>AnySet1 = AnySet2 = anyset()</v> - <v>Bool = bool()</v> - </type> <desc> - <p>Returns <c>true</c> if the AnySet1 and AnySet2 + <p>Returns <c>true</c> if the <anno>AnySet1</anno> + and <anno>AnySet2</anno> are <seealso marker="#equal">equal</seealso>, <c>false</c> otherwise. This example shows that <c>==/2</c> is used when comparing sets for equality:</p> @@ -1119,67 +1064,49 @@ true</pre> </desc> </func> <func> - <name>is_set(AnySet) -> Bool</name> + <name name="is_set" arity="1"/> <fsummary>Test for an unordered set.</fsummary> - <type> - <v>AnySet = anyset()</v> - <v>Bool = bool()</v> - </type> <desc> - <p>Returns <c>true</c> if AnySet is + <p>Returns <c>true</c> if <anno>AnySet</anno> is an <seealso marker="#sets_definition">unordered set</seealso>, and - <c>false</c> if AnySet is an ordered set or an atomic set.</p> + <c>false</c> if <anno>AnySet</anno> is an ordered set or an + atomic set.</p> </desc> </func> <func> - <name>is_sofs_set(Term) -> Bool</name> + <name name="is_sofs_set" arity="1"/> <fsummary>Test for an unordered set.</fsummary> - <type> - <v>Bool = bool()</v> - <v>Term = term()</v> - </type> <desc> - <p>Returns <c>true</c> if Term is + <p>Returns <c>true</c> if <anno>Term</anno> is an <seealso marker="#sets_definition">unordered set</seealso>, an ordered set or an atomic set, <c>false</c> otherwise.</p> </desc> </func> <func> - <name>is_subset(Set1, Set2) -> Bool</name> + <name name="is_subset" arity="2"/> <fsummary>Test two sets for subset.</fsummary> - <type> - <v>Bool = bool()</v> - <v>Set1 = Set2 = set()</v> - </type> <desc> - <p>Returns <c>true</c> if Set1 is - a <seealso marker="#subset">subset</seealso> of Set2, <c>false</c> - otherwise.</p> + <p>Returns <c>true</c> if <anno>Set1</anno> is + a <seealso marker="#subset">subset</seealso> + of <anno>Set2</anno>, <c>false</c> otherwise.</p> </desc> </func> <func> - <name>is_type(Term) -> Bool</name> + <name name="is_type" arity="1"/> <fsummary>Test for a type.</fsummary> - <type> - <v>Bool = bool()</v> - <v>Term = term()</v> - </type> <desc> - <p>Returns <c>true</c> if the term Term is + <p>Returns <c>true</c> if the term <anno>Term</anno> is a <seealso marker="#type">type</seealso>.</p> </desc> </func> <func> - <name>join(Relation1, I, Relation2, J) -> Relation3</name> + <name name="join" arity="4"/> <fsummary>Return the join of two relations.</fsummary> - <type> - <v>Relation1 = Relation2 = Relation3 = relation()</v> - <v>I = J = integer() > 0</v> - </type> <desc> <p>Returns the <seealso marker="#natural_join">natural - join</seealso> of the relations Relation1 and Relation2 on - coordinates I and J.</p> + join</seealso> of the relations <anno>Relation1</anno> + and <anno>Relation2</anno> on coordinates <anno>I</anno> and + <anno>J</anno>.</p> <pre> 1> <input>R1 = sofs:relation([{a,x,1},{b,y,2}]),</input> <input>R2 = sofs:relation([{1,f,g},{1,h,i},{2,3,4}]),</input> @@ -1189,20 +1116,17 @@ true</pre> </desc> </func> <func> - <name>multiple_relative_product(TupleOfBinRels, BinRel1) -> BinRel2</name> + <name name="multiple_relative_product" arity="2"/> <fsummary>Return the multiple relative product of a tuple of binary relations and a relation.</fsummary> - <type> - <v>TupleOfBinRels = tuple-of(BinRel)</v> - <v>BinRel = BinRel1 = BinRel2 = binary_relation()</v> - </type> - <desc> - <p>If TupleOfBinRels is a non-empty tuple - {R[1], ..., R[n]} of binary relations and BinRel1 - is a binary relation, then BinRel2 is + <desc> + <p>If <anno>TupleOfBinRels</anno> is a non-empty tuple + {R[1], ..., R[n]} of binary relations + and <anno>BinRel1</anno> is a binary relation, + then <anno>BinRel2</anno> is the <seealso marker="#multiple_relative_product">multiple relative product</seealso> of the ordered set - (R[i], ..., R[n]) and BinRel1.</p> + (R[i], ..., R[n]) and <anno>BinRel1</anno>.</p> <pre> 1> <input>Ri = sofs:relation([{a,1},{b,2},{c,3}]),</input> <input>R = sofs:relation([{a,b},{b,c},{c,a}]),</input> @@ -1212,29 +1136,21 @@ true</pre> </desc> </func> <func> - <name>no_elements(ASet) -> NoElements</name> + <name name="no_elements" arity="1"/> <fsummary>Return the number of elements of a set.</fsummary> - <type> - <v>ASet = set() | ordset()</v> - <v>NoElements = integer() >= 0 </v> - </type> <desc> <p>Returns the number of elements of the ordered or unordered - set ASet.</p> + set <anno>ASet</anno>.</p> </desc> </func> <func> - <name>partition(SetOfSets) -> Partition</name> + <name name="partition" arity="1"/> <fsummary>Return the coarsest partition given a set of sets.</fsummary> - <type> - <v>SetOfSets = set_of_sets()</v> - <v>Partition = set()</v> - </type> <desc> <p>Returns the <seealso marker="#partition">partition</seealso> of - the union of the set of sets SetOfSets such that two + the union of the set of sets <anno>SetOfSets</anno> such that two elements are considered equal if they belong to the same - elements of SetOfSets.</p> + elements of <anno>SetOfSets</anno>.</p> <pre> 1> <input>Sets1 = sofs:from_term([[a,b,c],[d,e,f],[g,h,i]]),</input> <input>Sets2 = sofs:from_term([[b,c,d],[e,f,g],[h,i,j]]),</input> @@ -1244,17 +1160,12 @@ true</pre> </desc> </func> <func> - <name>partition(SetFun, Set) -> Partition</name> + <name name="partition" arity="2"/> <fsummary>Return a partition of a set.</fsummary> - <type> - <v>SetFun = set_fun()</v> - <v>Partition = set()</v> - <v>Set = set()</v> - </type> <desc> <p>Returns the <seealso marker="#partition">partition</seealso> of - Set such that two elements are considered equal if the - results of applying SetFun are equal.</p> + <anno>Set</anno> such that two elements are considered equal + if the results of applying <anno>SetFun</anno> are equal.</p> <pre> 1> <input>Ss = sofs:from_term([[a],[b],[c,d],[e,f]]),</input> <input>SetFun = fun(S) -> sofs:from_term(sofs:no_elements(S)) end,</input> @@ -1264,19 +1175,16 @@ true</pre> </desc> </func> <func> - <name>partition(SetFun, Set1, Set2) -> {Set3, Set4}</name> + <name name="partition" arity="3"/> <fsummary>Return a partition of a set.</fsummary> - <type> - <v>SetFun = set_fun()</v> - <v>Set1 = Set2 = Set3 = Set4 = set()</v> - </type> <desc> <p>Returns a pair of sets that, regarded as constituting a set, forms a <seealso marker="#partition">partition</seealso> of - Set1. If the - result of applying SetFun to an element of Set1 yields an - element in Set2, the element belongs to Set3, otherwise the - element belongs to Set4.</p> + <anno>Set1</anno>. If the + result of applying <anno>SetFun</anno> to an element + of <anno>Set1</anno> yields an element in <anno>Set2</anno>, + the element belongs to <anno>Set3</anno>, otherwise the + element belongs to <anno>Set4</anno>.</p> <pre> 1> <input>R1 = sofs:relation([{1,a},{2,b},{3,c}]),</input> <input>S = sofs:set([2,4,6]),</input> @@ -1289,21 +1197,17 @@ true</pre> </desc> </func> <func> - <name>partition_family(SetFun, Set) -> Family</name> + <name name="partition_family" arity="2"/> <fsummary>Return a family indexing a partition.</fsummary> - <type> - <v>Family = family()</v> - <v>SetFun = set_fun()</v> - <v>Set = set()</v> - </type> <desc> <p>Returns the <seealso marker="#family">family</seealso> - Family where the indexed set is - a <seealso marker="#partition">partition</seealso> of Set - such that two elements are considered equal if the results - of applying SetFun are the same value i. This i is the index - that Family maps onto - the <seealso marker="#equivalence_class">equivalence + <anno>Family</anno> where the indexed set is + a <seealso marker="#partition">partition</seealso> + of <anno>Set</anno> such that two elements are considered + equal if the results of applying <anno>SetFun</anno> are the + same value i. This i is the index that <anno>Family</anno> + maps onto + the <seealso marker="#equivalence_class">equivalence class</seealso>.</p> <pre> 1> <input>S = sofs:relation([{a,a,a,a},{a,a,b,b},{a,b,b,b}]),</input> @@ -1314,18 +1218,15 @@ true</pre> </desc> </func> <func> - <name>product(TupleOfSets) -> Relation</name> + <name name="product" arity="1"/> <fsummary>Return the Cartesian product of a tuple of sets.</fsummary> - <type> - <v>Relation = relation()</v> - <v>TupleOfSets = tuple-of(set())</v> - </type> <desc> <p>Returns the <seealso marker="#Cartesian_product_tuple">Cartesian product</seealso> of the non-empty tuple of sets - TupleOfSets. If (x[1], ..., x[n]) is an element of - the n-ary relation Relation, then x[i] is drawn from element - i of TupleOfSets.</p> + <anno>TupleOfSets</anno>. If (x[1], ..., x[n]) is + an element of the n-ary relation <anno>Relation</anno>, then + x[i] is drawn from element i + of <anno>TupleOfSets</anno>.</p> <pre> 1> <input>S1 = sofs:set([a,b]),</input> <input>S2 = sofs:set([1,2]),</input> @@ -1336,15 +1237,12 @@ true</pre> </desc> </func> <func> - <name>product(Set1, Set2) -> BinRel</name> + <name name="product" arity="2"/> <fsummary>Return the Cartesian product of two sets.</fsummary> - <type> - <v>BinRel = binary_relation()</v> - <v>Set1 = Set2 = set()</v> - </type> <desc> <p>Returns the <seealso marker="#Cartesian_product">Cartesian - product</seealso> of Set1 and Set2.</p> + product</seealso> of <anno>Set1</anno> + and <anno>Set2</anno>.</p> <pre> 1> <input>S1 = sofs:set([1,2]),</input> <input>S2 = sofs:set([a,b]),</input> @@ -1356,19 +1254,16 @@ true</pre> </desc> </func> <func> - <name>projection(SetFun, Set1) -> Set2</name> + <name name="projection" arity="2"/> <fsummary>Return a set of substituted elements.</fsummary> - <type> - <v>SetFun = set_fun()</v> - <v>Set1 = Set2 = set()</v> - </type> <desc> <p>Returns the set created by substituting each element of - Set1 by the result of applying SetFun to the element.</p> - <p>If SetFun is a number i >= 1 and Set1 is a - relation, then the returned set is - the <seealso marker="#projection">projection</seealso> of Set1 - onto coordinate i.</p> + <anno>Set1</anno> by the result of + applying <anno>SetFun</anno> to the element.</p> + <p>If <anno>SetFun</anno> is a number i >= 1 and + <anno>Set1</anno> is a relation, then the returned set is + the <seealso marker="#projection">projection</seealso> of + <anno>Set1</anno> onto coordinate i.</p> <pre> 1> <input>S1 = sofs:from_term([{1,a},{2,b},{3,a}]),</input> <input>S2 = sofs:projection(2, S1),</input> @@ -1377,15 +1272,11 @@ true</pre> </desc> </func> <func> - <name>range(BinRel) -> Set</name> + <name name="range" arity="1"/> <fsummary>Return the range of a binary relation.</fsummary> - <type> - <v>BinRel = binary_relation()</v> - <v>Set = set()</v> - </type> <desc> <p>Returns the <seealso marker="#range">range</seealso> of the - binary relation BinRel.</p> + binary relation <anno>BinRel</anno>.</p> <pre> 1> <input>R = sofs:relation([{1,a},{1,b},{2,b},{2,c}]),</input> <input>S = sofs:range(R),</input> @@ -1394,41 +1285,33 @@ true</pre> </desc> </func> <func> - <name>relation(Tuples [, Type]) -> Relation</name> + <name name="relation" arity="1"/> + <name name="relation" arity="2"/> <fsummary>Create a relation.</fsummary> - <type> - <v>N = integer()</v> - <v>Type = N | type()</v> - <v>Relation = relation()</v> - <v>Tuples = [tuple()]</v> - </type> <desc> <p>Creates a <seealso marker="#relation">relation</seealso>. <c>relation(R, T)</c> is equivalent to <c>from_term(R, T)</c>, if T is a <seealso marker="#type">type</seealso> and the result is a - relation. If Type is an integer N, then + relation. If <anno>Type</anno> is an integer N, then <c>[{atom, ..., atom}])</c>, where the size of the tuple is N, is used as type of the relation. If no type is - explicitly given, the size of the first tuple of Tuples is + explicitly given, the size of the first tuple of + <anno>Tuples</anno> is used if there is such a tuple. <c>relation([])</c> is equivalent to <c>relation([], 2)</c>.</p> </desc> </func> <func> - <name>relation_to_family(BinRel) -> Family</name> + <name name="relation_to_family" arity="1"/> <fsummary>Create a family from a binary relation.</fsummary> - <type> - <v>Family = family()</v> - <v>BinRel = binary_relation()</v> - </type> <desc> <p>Returns the <seealso marker="#family">family</seealso> - Family such that the index set is equal to + <anno>Family</anno> such that the index set is equal to the <seealso marker="#domain">domain</seealso> of the binary - relation BinRel, and Family[i] is + relation <anno>BinRel</anno>, and <anno>Family</anno>[i] is the <seealso marker="#image">image</seealso> of the set of i - under BinRel.</p> + under <anno>BinRel</anno>.</p> <pre> 1> <input>R = sofs:relation([{b,1},{c,2},{c,3}]),</input> <input>F = sofs:relation_to_family(R),</input> @@ -1437,63 +1320,57 @@ true</pre> </desc> </func> <func> - <name>relative_product(TupleOfBinRels [, BinRel1]) -> BinRel2</name> - <fsummary>Return the relative product of a tuple of binary relations + <name name="relative_product" arity="1"/> + <name name="relative_product" arity="2" clause_i="1"/> + <fsummary>Return the relative product of a list of binary relations and a binary relation.</fsummary> - <type> - <v>TupleOfBinRels = tuple-of(BinRel)</v> - <v>BinRel = BinRel1 = BinRel2 = binary_relation()</v> - </type> - <desc> - <p>If TupleOfBinRels is a non-empty tuple - {R[1], ..., R[n]} of binary relations and BinRel1 - is a binary relation, then BinRel2 is - the <seealso marker="#tuple_relative_product">relative - product</seealso> of the ordered set (R[i], ..., R[n]) - and BinRel1.</p> - <p>If BinRel1 is omitted, the relation of equality between the - elements of - the <seealso marker="#Cartesian_product_tuple">Cartesian - product</seealso> of the ranges of R[i], + <desc> + <p>If <anno>ListOfBinRels</anno> is a non-empty list + [R[1], ..., R[n]] of binary relations and + <anno>BinRel1</anno> + is a binary relation, then <anno>BinRel2</anno> is the <seealso + marker="#tuple_relative_product">relative product</seealso> + of the ordered set (R[i], ..., R[n]) and + <anno>BinRel1</anno>.</p> + <p>If <anno>BinRel1</anno> is omitted, the relation of equality + between the elements of + the <seealso marker="#Cartesian_product_tuple">Cartesian + product</seealso> of the ranges of R[i], range R[1] × ... × range R[n], is used instead (intuitively, nothing is "lost").</p> <pre> 1> <input>TR = sofs:relation([{1,a},{1,aa},{2,b}]),</input> <input>R1 = sofs:relation([{1,u},{2,v},{3,c}]),</input> -<input>R2 = sofs:relative_product({TR, R1}),</input> +<input>R2 = sofs:relative_product([TR, R1]),</input> <input>sofs:to_external(R2).</input> [{1,{a,u}},{1,{aa,u}},{2,{b,v}}]</pre> - <p>Note that <c>relative_product({R1}, R2)</c> is + <p>Note that <c>relative_product([R1], R2)</c> is different from <c>relative_product(R1, R2)</c>; the - tuple of one element is not identified with the element + list of one element is not identified with the element itself.</p> </desc> </func> <func> - <name>relative_product(BinRel1, BinRel2) -> BinRel3</name> + <name name="relative_product" arity="2" clause_i="2"/> <fsummary>Return the relative product of two binary relations.</fsummary> - <type> - <v>BinRel1 = BinRel2 = BinRel3 = binary_relation()</v> - </type> <desc> <p><marker id="relprod_impl"></marker>Returns the <seealso marker="#relative_product">relative - product</seealso> of the binary relations BinRel1 and BinRel2.</p> + product</seealso> of the binary relations <anno>BinRel1</anno> + and <anno>BinRel2</anno>.</p> </desc> </func> <func> - <name>relative_product1(BinRel1, BinRel2) -> BinRel3</name> + <name name="relative_product1" arity="2"/> <fsummary>Return the relative_product of two binary relations.</fsummary> - <type> - <v>BinRel1 = BinRel2 = BinRel3 = binary_relation()</v> - </type> <desc> <p>Returns the <seealso marker="#relative_product">relative product</seealso> of the <seealso marker="#converse">converse</seealso> of the - binary relation BinRel1 and the binary relation BinRel2.</p> + binary relation <anno>BinRel1</anno> and the binary + relation <anno>BinRel2</anno>.</p> <pre> 1> <input>R1 = sofs:relation([{1,a},{1,aa},{2,b}]),</input> <input>R2 = sofs:relation([{1,u},{2,v},{3,c}]),</input> @@ -1505,15 +1382,12 @@ true</pre> </desc> </func> <func> - <name>restriction(BinRel1, Set) -> BinRel2</name> + <name name="restriction" arity="2"/> <fsummary>Return a restriction of a binary relation.</fsummary> - <type> - <v>BinRel1 = BinRel2 = binary_relation()</v> - <v>Set = set()</v> - </type> <desc> <p>Returns the <seealso marker="#restriction">restriction</seealso> of - the binary relation BinRel1 to Set.</p> + the binary relation <anno>BinRel1</anno> + to <anno>Set</anno>.</p> <pre> 1> <input>R1 = sofs:relation([{1,a},{2,b},{3,c}]),</input> <input>S = sofs:set([1,2,4]),</input> @@ -1523,15 +1397,12 @@ true</pre> </desc> </func> <func> - <name>restriction(SetFun, Set1, Set2) -> Set3</name> + <name name="restriction" arity="3"/> <fsummary>Return a restriction of a set.</fsummary> - <type> - <v>SetFun = set_fun()</v> - <v>Set1 = Set2 = Set3 = set()</v> - </type> <desc> - <p>Returns a subset of Set1 containing those elements that - yield an element in Set2 as the result of applying SetFun.</p> + <p>Returns a subset of <anno>Set1</anno> containing those + elements that yield an element in <anno>Set2</anno> as the + result of applying <anno>SetFun</anno>.</p> <pre> 1> <input>S1 = sofs:relation([{1,a},{2,b},{3,c}]),</input> <input>S2 = sofs:set([b,c,d]),</input> @@ -1541,13 +1412,9 @@ true</pre> </desc> </func> <func> - <name>set(Terms [, Type]) -> Set</name> + <name name="set" arity="1"/> + <name name="set" arity="2"/> <fsummary>Create a set of atoms or any type of sets.</fsummary> - <type> - <v>Set = set()</v> - <v>Terms = [term()]</v> - <v>Type = type()</v> - </type> <desc> <p>Creates an <seealso marker="#sets_definition">unordered set</seealso>. <c>set(L, T)</c> is equivalent to @@ -1557,18 +1424,16 @@ true</pre> </desc> </func> <func> - <name>specification(Fun, Set1) -> Set2</name> + <name name="specification" arity="2"/> <fsummary>Select a subset using a predicate.</fsummary> - <type> - <v>Fun = spec_fun()</v> - <v>Set1 = Set2 = set()</v> - </type> <desc> - <p>Returns the set containing every element of Set1 for which - Fun returns <c>true</c>. If Fun is a tuple + <p>Returns the set containing every element + of <anno>Set1</anno> for which <anno>Fun</anno> + returns <c>true</c>. If <anno>Fun</anno> is a tuple <c>{external, Fun2}</c>, Fun2 is applied to the <seealso marker="#external_set">external set</seealso> of - each element, otherwise Fun is applied to each element.</p> + each element, otherwise <anno>Fun</anno> is applied to each + element.</p> <pre> 1> <input>R1 = sofs:relation([{a,1},{b,2}]),</input> <input>R2 = sofs:relation([{x,1},{x,2},{y,3}]),</input> @@ -1579,15 +1444,13 @@ true</pre> </desc> </func> <func> - <name>strict_relation(BinRel1) -> BinRel2</name> + <name name="strict_relation" arity="1"/> <fsummary>Return the strict relation corresponding to a given relation.</fsummary> - <type> - <v>BinRel1 = BinRel2 = binary_relation()</v> - </type> <desc> <p>Returns the <seealso marker="#strict_relation">strict - relation</seealso> corresponding to the binary relation BinRel1.</p> + relation</seealso> corresponding to the binary + relation <anno>BinRel1</anno>.</p> <pre> 1> <input>R1 = sofs:relation([{1,1},{1,2},{2,1},{2,2}]),</input> <input>R2 = sofs:strict_relation(R1),</input> @@ -1596,16 +1459,13 @@ true</pre> </desc> </func> <func> - <name>substitution(SetFun, Set1) -> Set2</name> + <name name="substitution" arity="2"/> <fsummary>Return a function with a given set as domain.</fsummary> - <type> - <v>SetFun = set_fun()</v> - <v>Set1 = Set2 = set()</v> - </type> - <desc> - <p>Returns a function, the domain of which is Set1. The value - of an element of the domain is the result of applying SetFun - to the element.</p> + <desc> + <p>Returns a function, the domain of which + is <anno>Set1</anno>. The value of an element of the domain + is the result of applying <anno>SetFun</anno> to the + element.</p> <pre> 1> <input>L = [{a,1},{b,2}].</input> [{a,1},{b,2}] @@ -1647,14 +1507,12 @@ images2(SetOfSets, BinRel) -> </desc> </func> <func> - <name>symdiff(Set1, Set2) -> Set3</name> + <name name="symdiff" arity="2"/> <fsummary>Return the symmetric difference of two sets.</fsummary> - <type> - <v>Set1 = Set2 = Set3 = set()</v> - </type> <desc> <p>Returns the <seealso marker="#symmetric_difference">symmetric - difference</seealso> (or the Boolean sum) of Set1 and Set2.</p> + difference</seealso> (or the Boolean sum) + of <anno>Set1</anno> and <anno>Set2</anno>.</p> <pre> 1> <input>S1 = sofs:set([1,2,3]),</input> <input>S2 = sofs:set([2,3,4]),</input> @@ -1664,88 +1522,67 @@ images2(SetOfSets, BinRel) -> </desc> </func> <func> - <name>symmetric_partition(Set1, Set2) -> {Set3, Set4, Set5}</name> + <name name="symmetric_partition" arity="2"/> <fsummary>Return a partition of two sets.</fsummary> - <type> - <v>Set1 = Set2 = Set3 = Set4 = Set5 = set()</v> - </type> <desc> - <p>Returns a triple of sets: Set3 contains the elements - of Set1 that do not belong to Set2; Set4 contains the - elements of Set1 that belong to Set2; Set5 contains the - elements of Set2 that do not belong to Set1.</p> + <p>Returns a triple of sets: <anno>Set3</anno> contains the + elements of <anno>Set1</anno> that do not belong + to <anno>Set2</anno>; <anno>Set4</anno> contains the + elements of <anno>Set1</anno> that belong + to <anno>Set2</anno>; <anno>Set5</anno> contains the + elements of <anno>Set2</anno> that do not belong + to <anno>Set1</anno>.</p> </desc> </func> <func> - <name>to_external(AnySet) -> ExternalSet</name> + <name name="to_external" arity="1"/> <fsummary>Return the elements of a set.</fsummary> - <type> - <v>ExternalSet = external_set()</v> - <v>AnySet = anyset()</v> - </type> <desc> <p>Returns the <seealso marker="#external_set">external set</seealso> of an atomic, ordered or unordered set.</p> </desc> </func> <func> - <name>to_sets(ASet) -> Sets</name> + <name name="to_sets" arity="1"/> <fsummary>Return a list or a tuple of the elements of set.</fsummary> - <type> - <v>ASet = set() | ordset()</v> - <v>Sets = tuple_of(AnySet) | [AnySet]</v> - </type> <desc> - <p>Returns the elements of the ordered set ASet as a tuple of - sets, and the elements of the unordered set ASet as a sorted - list of sets without duplicates.</p> + <p>Returns the elements of the ordered set <anno>ASet</anno> + as a tuple of sets, and the elements of the unordered set + <anno>ASet</anno> as a sorted list of sets without + duplicates.</p> </desc> </func> <func> - <name>type(AnySet) -> Type</name> + <name name="type" arity="1"/> <fsummary>Return the type of a set.</fsummary> - <type> - <v>AnySet = anyset()</v> - <v>Type = type()</v> - </type> <desc> <p>Returns the <seealso marker="#type">type</seealso> of an atomic, ordered or unordered set.</p> </desc> </func> <func> - <name>union(SetOfSets) -> Set</name> + <name name="union" arity="1"/> <fsummary>Return the union of a set of sets.</fsummary> - <type> - <v>Set = set()</v> - <v>SetOfSets = set_of_sets()</v> - </type> <desc> <p>Returns the <seealso marker="#union_n">union</seealso> of the - set of sets SetOfSets.</p> + set of sets <anno>SetOfSets</anno>.</p> </desc> </func> <func> - <name>union(Set1, Set2) -> Set3</name> + <name name="union" arity="2"/> <fsummary>Return the union of two sets.</fsummary> - <type> - <v>Set1 = Set2 = Set3 = set()</v> - </type> <desc> <p>Returns the <seealso marker="#union">union</seealso> of - Set1 and Set2.</p> + <anno>Set1</anno> and <anno>Set2</anno>.</p> </desc> </func> <func> - <name>union_of_family(Family) -> Set</name> + <name name="union_of_family" arity="1"/> <fsummary>Return the union of a family.</fsummary> - <type> - <v>Family = family()</v> - <v>Set = set()</v> - </type> <desc> <p>Returns the union of - the <seealso marker="#family">family</seealso> Family.</p> + the <seealso marker="#family">family</seealso> <anno>Family</anno>. + </p> <pre> 1> <input>F = sofs:family([{a,[0,2,4]},{b,[0,1,2]},{c,[2,3]}]),</input> <input>S = sofs:union_of_family(F),</input> @@ -1754,17 +1591,15 @@ images2(SetOfSets, BinRel) -> </desc> </func> <func> - <name>weak_relation(BinRel1) -> BinRel2</name> + <name name="weak_relation" arity="1"/> <fsummary>Return the weak relation corresponding to a given relation.</fsummary> - <type> - <v>BinRel1 = BinRel2 = binary_relation()</v> - </type> <desc> <p>Returns a subset S of the <seealso marker="#weak_relation">weak relation</seealso> W - corresponding to the binary relation BinRel1. Let F be the - <seealso marker="#field">field</seealso> of BinRel1. The + corresponding to the binary relation <anno>BinRel1</anno>. + Let F be the <seealso marker="#field">field</seealso> of + <anno>BinRel1</anno>. The subset S is defined so that x S y if x W y for some x in F and for some y in F.</p> <pre> diff --git a/lib/stdlib/doc/src/specs.xml b/lib/stdlib/doc/src/specs.xml new file mode 100644 index 0000000000..98338b5ec2 --- /dev/null +++ b/lib/stdlib/doc/src/specs.xml @@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="latin1" ?> +<specs xmlns:xi="http://www.w3.org/2001/XInclude"> + <xi:include href="../specs/specs_array.xml"/> + <xi:include href="../specs/specs_base64.xml"/> + <xi:include href="../specs/specs_beam_lib.xml"/> + <xi:include href="../specs/specs_binary.xml"/> + <xi:include href="../specs/specs_c.xml"/> + <xi:include href="../specs/specs_calendar.xml"/> + <xi:include href="../specs/specs_dets.xml"/> + <xi:include href="../specs/specs_dict.xml"/> + <xi:include href="../specs/specs_digraph.xml"/> + <xi:include href="../specs/specs_digraph_utils.xml"/> + <xi:include href="../specs/specs_epp.xml"/> + <xi:include href="../specs/specs_erl_eval.xml"/> + <xi:include href="../specs/specs_erl_expand_records.xml"/> + <xi:include href="../specs/specs_erl_id_trans.xml"/> + <xi:include href="../specs/specs_erl_internal.xml"/> + <xi:include href="../specs/specs_erl_lint.xml"/> + <xi:include href="../specs/specs_erl_parse.xml"/> + <xi:include href="../specs/specs_erl_pp.xml"/> + <xi:include href="../specs/specs_erl_scan.xml"/> + <xi:include href="../specs/specs_erl_tar.xml"/> + <xi:include href="../specs/specs_ets.xml"/> + <xi:include href="../specs/specs_file_sorter.xml"/> + <xi:include href="../specs/specs_filelib.xml"/> + <xi:include href="../specs/specs_filename.xml"/> + <xi:include href="../specs/specs_gb_sets.xml"/> + <xi:include href="../specs/specs_gb_trees.xml"/> + <xi:include href="../specs/specs_gen_event.xml"/> + <xi:include href="../specs/specs_gen_fsm.xml"/> + <xi:include href="../specs/specs_gen_server.xml"/> + <xi:include href="../specs/specs_io.xml"/> + <xi:include href="../specs/specs_io_lib.xml"/> + <xi:include href="../specs/specs_lib.xml"/> + <xi:include href="../specs/specs_lists.xml"/> + <xi:include href="../specs/specs_log_mf_h.xml"/> + <xi:include href="../specs/specs_math.xml"/> + <xi:include href="../specs/specs_ms_transform.xml"/> + <xi:include href="../specs/specs_orddict.xml"/> + <xi:include href="../specs/specs_ordsets.xml"/> + <xi:include href="../specs/specs_pg.xml"/> + <xi:include href="../specs/specs_pool.xml"/> + <xi:include href="../specs/specs_proc_lib.xml"/> + <xi:include href="../specs/specs_proplists.xml"/> + <xi:include href="../specs/specs_qlc.xml"/> + <xi:include href="../specs/specs_queue.xml"/> + <xi:include href="../specs/specs_random.xml"/> + <xi:include href="../specs/specs_re.xml"/> + <xi:include href="../specs/specs_regexp.xml"/> + <xi:include href="../specs/specs_sets.xml"/> + <xi:include href="../specs/specs_shell.xml"/> + <xi:include href="../specs/specs_shell_default.xml"/> + <xi:include href="../specs/specs_slave.xml"/> + <xi:include href="../specs/specs_sofs.xml"/> + <xi:include href="../specs/specs_string.xml"/> + <xi:include href="../specs/specs_supervisor.xml"/> + <xi:include href="../specs/specs_supervisor_bridge.xml"/> + <xi:include href="../specs/specs_sys.xml"/> + <xi:include href="../specs/specs_timer.xml"/> + <xi:include href="../specs/specs_unicode.xml"/> + <xi:include href="../specs/specs_win32reg.xml"/> + <xi:include href="../specs/specs_zip.xml"/> +</specs> diff --git a/lib/stdlib/doc/src/string.xml b/lib/stdlib/doc/src/string.xml index de1b99a2d5..48867ffe72 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>2009</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -38,65 +38,46 @@ </description> <funcs> <func> - <name>len(String) -> Length</name> + <name name="len" arity="1"/> <fsummary>Return the length of a string</fsummary> - <type> - <v>String = string()</v> - <v>Length = integer()</v> - </type> <desc> <p>Returns the number of characters in the string.</p> </desc> </func> <func> - <name>equal(String1, String2) -> bool()</name> + <name name="equal" arity="2"/> <fsummary>Test string equality</fsummary> - <type> - <v>String1 = String2 = string()</v> - </type> <desc> <p>Tests whether two strings are equal. Returns <c>true</c> if they are, otherwise <c>false</c>.</p> </desc> </func> <func> - <name>concat(String1, String2) -> String3</name> + <name name="concat" arity="2"/> <fsummary>Concatenate two strings</fsummary> - <type> - <v>String1 = String2 = String3 = string()</v> - </type> <desc> <p>Concatenates two strings to form a new string. Returns the new string.</p> </desc> </func> <func> - <name>chr(String, Character) -> Index</name> - <name>rchr(String, Character) -> Index</name> + <name name="chr" arity="2"/> + <name name="rchr" arity="2"/> <fsummary>Return the index of the first/last occurrence of<c>Character</c>in <c>String</c></fsummary> - <type> - <v>String = string()</v> - <v>Character = char()</v> - <v>Index = integer()</v> - </type> <desc> <p>Returns the index of the first/last occurrence of - <c>Character</c> in <c>String</c>. <c>0</c> is returned if <c>Character</c> does not + <c><anno>Character</anno></c> in <c><anno>String</anno></c>. <c>0</c> is returned if <c><anno>Character</anno></c> does not occur.</p> </desc> </func> <func> - <name>str(String, SubString) -> Index</name> - <name>rstr(String, SubString) -> Index</name> + <name name="str" arity="2"/> + <name name="rstr" arity="2"/> <fsummary>Find the index of a substring</fsummary> - <type> - <v>String = SubString = string()</v> - <v>Index = integer()</v> - </type> <desc> <p>Returns the position where the first/last occurrence of - <c>SubString</c> begins in <c>String</c>. <c>0</c> is returned if <c>SubString</c> - does not exist in <c>String</c>. + <c><anno>SubString</anno></c> begins in <c><anno>String</anno></c>. <c>0</c> is returned if <c><anno>SubString</anno></c> + does not exist in <c><anno>String</anno></c>. For example:</p> <code type="none"> > string:str(" Hello Hello World World ", "Hello World"). @@ -104,17 +85,13 @@ </desc> </func> <func> - <name>span(String, Chars) -> Length </name> - <name>cspan(String, Chars) -> Length</name> + <name name="span" arity="2"/> + <name name="cspan" arity="2"/> <fsummary>Span characters at start of string</fsummary> - <type> - <v>String = Chars = string()</v> - <v>Length = integer()</v> - </type> <desc> <p>Returns the length of the maximum initial segment of - String, which consists entirely of characters from (not - from) Chars.</p> + <c><anno>String</anno></c>, which consists entirely of characters from (not + from) <c><anno>Chars</anno></c>.</p> <p>For example:</p> <code type="none"> > string:span("\t abcdef", " \t"). @@ -124,17 +101,13 @@ </desc> </func> <func> - <name>substr(String, Start) -> SubString</name> - <name>substr(String, Start, Length) -> Substring</name> + <name name="substr" arity="2"/> + <name name="substr" arity="3"/> <fsummary>Return a substring of <c>String</c></fsummary> - <type> - <v>String = SubString = string()</v> - <v>Start = Length = integer()</v> - </type> <desc> - <p>Returns a substring of <c>String</c>, starting at the - position <c>Start</c>, and ending at the end of the string or - at length <c>Length</c>.</p> + <p>Returns a substring of <c><anno>String</anno></c>, starting at the + position <c><anno>Start</anno></c>, and ending at the end of the string or + at length <c><anno>Length</anno></c>.</p> <p>For example:</p> <code type="none"> > substr("Hello World", 4, 5). @@ -142,15 +115,11 @@ </desc> </func> <func> - <name>tokens(String, SeparatorList) -> Tokens</name> + <name name="tokens" arity="2"/> <fsummary>Split string into tokens</fsummary> - <type> - <v>String = SeparatorList = string()</v> - <v>Tokens = [string()]</v> - </type> <desc> - <p>Returns a list of tokens in <c>String</c>, separated by the - characters in <c>SeparatorList</c>.</p> + <p>Returns a list of tokens in <c><anno>String</anno></c>, separated by the + characters in <c><anno>SeparatorList</anno></c>.</p> <p>For example:</p> <code type="none"> > tokens("abc defxxghix jkl", "x "). @@ -158,15 +127,11 @@ </desc> </func> <func> - <name>join(StringList, Separator) -> String</name> + <name name="join" arity="2"/> <fsummary>Join a list of strings with separator</fsummary> - <type> - <v>StringList = [string()]</v> - <v>Separator = string()</v> - </type> <desc> - <p>Returns a string with the elements of <c>StringList</c> - separated by the string in <c>Seperator</c>.</p> + <p>Returns a string with the elements of <c><anno>StringList</anno></c> + separated by the string in <c><anno>Separator</anno></c>.</p> <p>For example:</p> <code type="none"> > join(["one", "two", "three"], ", "). @@ -174,44 +139,30 @@ </desc> </func> <func> - <name>chars(Character, Number) -> String</name> - <name>chars(Character, Number, Tail) -> String</name> + <name name="chars" arity="2"/> + <name name="chars" arity="3"/> <fsummary>Returns a string consisting of numbers of characters</fsummary> - <type> - <v>Character = char()</v> - <v>Number = integer()</v> - <v>String = string()</v> - </type> <desc> - <p>Returns a string consisting of <c>Number</c> of characters - <c>Character</c>. Optionally, the string can end with the - string <c>Tail</c>.</p> + <p>Returns a string consisting of <c><anno>Number</anno></c> of characters + <c><anno>Character</anno></c>. Optionally, the string can end with the + string <c><anno>Tail</anno></c>.</p> </desc> </func> <func> - <name>copies(String, Number) -> Copies</name> + <name name="copies" arity="2"/> <fsummary>Copy a string</fsummary> - <type> - <v>String = Copies = string()</v> - <v>Number = integer()</v> - </type> <desc> - <p>Returns a string containing <c>String</c> repeated - <c>Number</c> times.</p> + <p>Returns a string containing <c><anno>String</anno></c> repeated + <c><anno>Number</anno></c> times.</p> </desc> </func> <func> - <name>words(String) -> Count</name> - <name>words(String, Character) -> Count</name> + <name name="words" arity="1"/> + <name name="words" arity="2"/> <fsummary>Count blank separated words</fsummary> - <type> - <v>String = string()</v> - <v>Character = char()</v> - <v>Count = integer()</v> - </type> <desc> - <p>Returns the number of words in <c>String</c>, separated by - blanks or <c>Character</c>.</p> + <p>Returns the number of words in <c><anno>String</anno></c>, separated by + blanks or <c><anno>Character</anno></c>.</p> <p>For example:</p> <code type="none"> > words(" Hello old boy!", $o). @@ -219,17 +170,12 @@ </desc> </func> <func> - <name>sub_word(String, Number) -> Word</name> - <name>sub_word(String, Number, Character) -> Word</name> + <name name="sub_word" arity="2"/> + <name name="sub_word" arity="3"/> <fsummary>Extract subword</fsummary> - <type> - <v>String = Word = string()</v> - <v>Character = char()</v> - <v>Number = integer()</v> - </type> <desc> - <p>Returns the word in position <c>Number</c> of <c>String</c>. - Words are separated by blanks or <c>Character</c>s.</p> + <p>Returns the word in position <c><anno>Number</anno></c> of <c><anno>String</anno></c>. + Words are separated by blanks or <c><anno>Character</anno></c>s.</p> <p>For example:</p> <code type="none"> > string:sub_word(" Hello old boy !",3,$o). @@ -237,19 +183,14 @@ </desc> </func> <func> - <name>strip(String) -> Stripped</name> - <name>strip(String, Direction) -> Stripped</name> - <name>strip(String, Direction, Character) -> Stripped</name> + <name name="strip" arity="1"/> + <name name="strip" arity="2"/> + <name name="strip" arity="3"/> <fsummary>Strip leading or trailing characters</fsummary> - <type> - <v>String = Stripped = string()</v> - <v>Direction = left | right | both</v> - <v>Character = char()</v> - </type> <desc> <p>Returns a string, where leading and/or trailing blanks or a - number of <c>Character</c> have been removed. - <c>Direction</c> can be <c>left</c>, <c>right</c>, or + number of <c><anno>Character</anno></c> have been removed. + <c><anno>Direction</anno></c> can be <c>left</c>, <c>right</c>, or <c>both</c> and indicates from which direction blanks are to be removed. The function <c>strip/1</c> is equivalent to <c>strip(String, both)</c>.</p> @@ -260,19 +201,14 @@ </desc> </func> <func> - <name>left(String, Number) -> Left</name> - <name>left(String, Number, Character) -> Left</name> + <name name="left" arity="2"/> + <name name="left" arity="3"/> <fsummary>Adjust left end of string</fsummary> - <type> - <v>String = Left = string()</v> - <v>Character = char</v> - <v>Number = integer()</v> - </type> <desc> - <p>Returns the <c>String</c> with the length adjusted in - accordance with <c>Number</c>. The left margin is - fixed. If the <c>length(String)</c> < <c>Number</c>, - <c>String</c> is padded with blanks or <c>Character</c>s.</p> + <p>Returns the <c><anno>String</anno></c> with the length adjusted in + accordance with <c><anno>Number</anno></c>. The left margin is + fixed. If the <c>length(<anno>String</anno>)</c> < <c><anno>Number</anno></c>, + <c><anno>String</anno></c> is padded with blanks or <c><anno>Character</anno></c>s.</p> <p>For example:</p> <code type="none"> > string:left("Hello",10,$.). @@ -280,19 +216,14 @@ </desc> </func> <func> - <name>right(String, Number) -> Right</name> - <name>right(String, Number, Character) -> Right</name> + <name name="right" arity="2"/> + <name name="right" arity="3"/> <fsummary>Adjust right end of string</fsummary> - <type> - <v>String = Right = string()</v> - <v>Character = char</v> - <v>Number = integer()</v> - </type> <desc> - <p>Returns the <c>String</c> with the length adjusted in - accordance with <c>Number</c>. The right margin is - fixed. If the length of <c>(String)</c> < <c>Number</c>, - <c>String</c> is padded with blanks or <c>Character</c>s.</p> + <p>Returns the <c><anno>String</anno></c> with the length adjusted in + accordance with <c><anno>Number</anno></c>. The right margin is + fixed. If the length of <c>(<anno>String</anno>)</c> < <c><anno>Number</anno></c>, + <c><anno>String</anno></c> is padded with blanks or <c><anno>Character</anno></c>s.</p> <p>For example:</p> <code type="none"> > string:right("Hello", 10, $.). @@ -300,32 +231,23 @@ </desc> </func> <func> - <name>centre(String, Number) -> Centered</name> - <name>centre(String, Number, Character) -> Centered</name> + <name name="centre" arity="2"/> + <name name="centre" arity="3"/> <fsummary>Center a string</fsummary> - <type> - <v>String = Centered = string()</v> - <v>Character = char</v> - <v>Number = integer()</v> - </type> <desc> - <p>Returns a string, where <c>String</c> is centred in the + <p>Returns a string, where <c><anno>String</anno></c> is centred in the string and surrounded by blanks or characters. The resulting - string will have the length <c>Number</c>.</p> + string will have the length <c><anno>Number</anno></c>.</p> </desc> </func> <func> - <name>sub_string(String, Start) -> SubString</name> - <name>sub_string(String, Start, Stop) -> SubString</name> + <name name="sub_string" arity="2"/> + <name name="sub_string" arity="3"/> <fsummary>Extract a substring</fsummary> - <type> - <v>String = SubString = string()</v> - <v>Start = Stop = integer()</v> - </type> <desc> - <p>Returns a substring of <c>String</c>, starting at the - position <c>Start</c> to the end of the string, or to and - including the <c>Stop</c> position.</p> + <p>Returns a substring of <c><anno>String</anno></c>, starting at the + position <c><anno>Start</anno></c> to the end of the string, or to and + including the <c><anno>Stop</anno></c> position.</p> <p>For example:</p> <code type="none"> sub_string("Hello World", 4, 8). @@ -383,15 +305,15 @@ sub_string("Hello World", 4, 8). </desc> </func> <func> - <name>to_lower(String) -> Result</name> - <name>to_lower(Char) -> CharResult</name> - <name>to_upper(String) -> Result</name> - <name>to_upper(Char) -> CharResult</name> + <name name="to_lower" arity="1" clause_i="1"/> + <name name="to_lower" arity="1" clause_i="2"/> + <name name="to_upper" arity="1" clause_i="1"/> + <name name="to_upper" arity="1" clause_i="2"/> <fsummary>Convert case of string (ISO/IEC 8859-1)</fsummary> - <type> - <v>String = Result = string()</v> - <v>Char = CharResult = integer()</v> - </type> + <type variable="String" name_i="1"/> + <type variable="Result" name_i="1"/> + <type variable="Char"/> + <type variable="CharResult"/> <desc> <p>The given string or character is case-converted. Note that the supported character set is ISO/IEC 8859-1 (a.k.a. Latin 1), diff --git a/lib/stdlib/doc/src/supervisor.xml b/lib/stdlib/doc/src/supervisor.xml index c696434d49..009aa60faa 100644 --- a/lib/stdlib/doc/src/supervisor.xml +++ b/lib/stdlib/doc/src/supervisor.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2010</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -83,11 +83,17 @@ supervisor, where all child processes are dynamically added instances of the same process type, i.e. running the same code.</p> - <p>The functions <c>terminate_child/2</c>, <c>delete_child/2</c> + <p>The functions <c>delete_child/2</c> and <c>restart_child/2</c> are invalid for <c>simple_one_for_one</c> supervisors and will return <c>{error,simple_one_for_one}</c> if the specified supervisor uses this restart strategy.</p> + <p>The function <c>terminate_child/2</c> can be used for + children under <c>simple_one_for_one</c> supervisors by + giving the child's <c>pid()</c> as the second argument. If + instead the child specification identifier is used, + <c>terminate_child/2</c> will return + <c>{error,simple_one_for_one}</c>.</p> </item> </list> <p>To prevent a supervisor from getting into an infinite loop of @@ -156,7 +162,7 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules} the child process to terminate by calling <c>exit(Child,shutdown)</c> and then wait for an exit signal with reason <c>shutdown</c> back from the child process. If - no exit signal is received within the specified time, + no exit signal is received within the specified number of milliseconds, the child process is unconditionally terminated using <c>exit(Child,kill)</c>.</p> <p>If the child process is another supervisor, <c>Shutdown</c> @@ -193,51 +199,81 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules} </item> </list> </section> + <datatypes> + <datatype> + <name name="child"/> + </datatype> + <datatype> + <name name="child_id"/> + <desc><p>Not a <c>pid()</c>.</p></desc> + </datatype> + <datatype> + <name name="child_spec"/> + </datatype> + <datatype> + <name name="mfargs"/> + <desc><p><c>A</c> (the argument list) has the value + <c>undefined</c> if <c>Restart</c> is <c>temporary</c>.</p> + </desc> + </datatype> + <datatype> + <name name="modules"/> + </datatype> + <datatype> + <name name="restart"/> + </datatype> + <datatype> + <name name="shutdown"/> + </datatype> + <datatype> + <name name="strategy"/> + </datatype> + <datatype> + <name name="sup_ref"/> + </datatype> + <datatype> + <name name="worker"/> + </datatype> + </datatypes> <funcs> <func> - <name>start_link(Module, Args) -> Result</name> - <name>start_link(SupName, Module, Args) -> Result</name> + <name name="start_link" arity="2"/> + <name name="start_link" arity="3"/> <fsummary>Create a supervisor process.</fsummary> - <type> - <v>SupName = {local,Name} | {global,Name}</v> - <v> Name = atom()</v> - <v>Module = atom()</v> - <v>Args = term()</v> - <v>Result = {ok,Pid} | ignore | {error,Error}</v> - <v> Pid = pid()</v> - <v> Error = {already_started,Pid}} | shutdown | term()</v> - </type> + <type name="startlink_ret"/> + <type name="startlink_err"/> + <type name="sup_name"/> <desc> <p>Creates a supervisor process as part of a supervision tree. The function will, among other things, ensure that the supervisor is linked to the calling process (its supervisor).</p> - <p>The created supervisor process calls <c>Module:init/1</c> to + <p>The created supervisor process calls <c><anno>Module</anno>:init/1</c> to find out about restart strategy, maximum restart frequency and child processes. To ensure a synchronized start-up procedure, <c>start_link/2,3</c> does not return until - <c>Module:init/1</c> has returned and all child processes + <c><anno>Module</anno>:init/1</c> has returned and all child processes have been started.</p> - <p>If <c>SupName={local,Name}</c> the supervisor is registered + <p>If <c><anno>SupName</anno>={local,Name}</c> the supervisor is registered locally as <c>Name</c> using <c>register/2</c>. If - <c>SupName={global,Name}</c> the supervisor is registered + <c><anno>SupName</anno>={global,Name}</c> the supervisor is registered globally as <c>Name</c> using <c>global:register_name/2</c>. If no name is provided, the supervisor is not registered.</p> - <p><c>Module</c> is the name of the callback module.</p> - <p><c>Args</c> is an arbitrary term which is passed as - the argument to <c>Module:init/1</c>.</p> + <p><c><anno>Module</anno></c> is the name of the callback module.</p> + <p><c><anno>Args</anno></c> is an arbitrary term which is passed as + the argument to <c><anno>Module</anno>:init/1</c>.</p> <p>If the supervisor and its child processes are successfully created (i.e. if all child process start functions return <c>{ok,Child}</c>, <c>{ok,Child,Info}</c>, or <c>ignore</c>) the function returns <c>{ok,Pid}</c>, where <c>Pid</c> is the pid of the supervisor. If there already exists a process - with the specified <c>SupName</c> the function returns + with the specified <c><anno>SupName</anno></c> the function returns <c>{error,{already_started,Pid}}</c>, where <c>Pid</c> is the pid of that process.</p> - <p>If <c>Module:init/1</c> returns <c>ignore</c>, this function + <p>If <c><anno>Module</anno>:init/1</c> returns <c>ignore</c>, this function returns <c>ignore</c> as well and the supervisor terminates with reason <c>normal</c>. - If <c>Module:init/1</c> fails or returns an incorrect value, + If <c><anno>Module</anno>:init/1</c> fails or returns an incorrect value, this function returns <c>{error,Term}</c> where <c>Term</c> is a term with information about the error, and the supervisor terminates with reason <c>Term</c>.</p> @@ -249,21 +285,15 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules} </desc> </func> <func> - <name>start_child(SupRef, ChildSpec) -> Result</name> + <name name="start_child" arity="2"/> <fsummary>Dynamically add a child process to a supervisor.</fsummary> - <type> - <v>SupRef = Name | {Name,Node} | {global,Name} | pid()</v> - <v> Name = Node = atom()</v> - <v>ChildSpec = child_spec() | [term()]</v> - <v>Result = {ok,Child} | {ok,Child,Info} | {error,Error}</v> - <v> Child = pid() | undefined</v> - <v> Info = term()</v> - <v> Error = already_present | {already_started,Child} | term()</v> - </type> + <type name="child_spec"/> + <type name="startchild_ret"/> + <type name="startchild_err"/> <desc> <p>Dynamically adds a child specification to the supervisor - <c>SupRef</c> which starts the corresponding child process.</p> - <p><c>SupRef</c> can be:</p> + <c><anno>SupRef</anno></c> which starts the corresponding child process.</p> + <p><marker id="SupRef"><c><anno>SupRef</anno></c></marker> can be:</p> <list type="bulleted"> <item>the pid,</item> <item><c>Name</c>, if the supervisor is locally registered,</item> @@ -272,26 +302,26 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules} <item><c>{global,Name}</c>, if the supervisor is globally registered.</item> </list> - <p><c>ChildSpec</c> should be a valid child specification + <p><c><anno>ChildSpec</anno></c> should be a valid child specification (unless the supervisor is a <c>simple_one_for_one</c> supervisor, see below). The child process will be started by using the start function as defined in the child specification.</p> <p>If the case of a <c>simple_one_for_one</c> supervisor, the child specification defined in <c>Module:init/1</c> will - be used and <c>ChildSpec</c> should instead be an arbitrary - list of terms <c>List</c>. The child process will then be - started by appending <c>List</c> to the existing start + be used and <c><anno>ChildSpec</anno></c> should instead be an arbitrary + list of terms <c><anno>List</anno></c>. The child process will then be + started by appending <c><anno>List</anno></c> to the existing start function arguments, i.e. by calling - <c>apply(M, F, A++List)</c> where <c>{M,F,A}</c> is the start + <c>apply(M, F, A++<anno>List</anno>)</c> where <c>{M,F,A}</c> is the start function defined in the child specification.</p> <p>If there already exists a child specification with - the specified <c>Id</c>, <c>ChildSpec</c> is discarded and + the specified <c><anno>Id</anno></c>, <c><anno>ChildSpec</anno></c> is discarded and the function returns <c>{error,already_present}</c> or - <c>{error,{already_started,Child}}</c>, depending on if + <c>{error,{already_started,<anno>Child</anno>}}</c>, depending on if the corresponding child process is running or not.</p> - <p>If the child process start function returns <c>{ok,Child}</c> - or <c>{ok,Child,Info}</c>, the child specification and pid is + <p>If the child process start function returns <c>{ok,<anno>Child</anno>}</c> + or <c>{ok,<anno>Child</anno>,<anno>Info</anno>}</c>, the child specification and pid is added to the supervisor and the function returns the same value.</p> <p>If the child process start function returns <c>ignore</c>, @@ -306,138 +336,114 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules} </desc> </func> <func> - <name>terminate_child(SupRef, Id) -> Result</name> + <name name="terminate_child" arity="2"/> <fsummary>Terminate a child process belonging to a supervisor.</fsummary> - <type> - <v>SupRef = Name | {Name,Node} | {global,Name} | pid()</v> - <v> Name = Node = atom()</v> - <v>Id = term()</v> - <v>Result = ok | {error,Error}</v> - <v> Error = not_found | simple_one_for_one</v> - </type> <desc> - <p>Tells the supervisor <c>SupRef</c> to terminate the child - process corresponding to the child specification identified - by <c>Id</c>. The process, if there is one, is terminated but - the child specification is kept by the supervisor. This means - that the child process may be later be restarted by - the supervisor. The child process can also be restarted - explicitly by calling <c>restart_child/2</c>. Use - <c>delete_child/2</c> to remove the child specification.</p> - <p>See <c>start_child/2</c> for a description of - <c>SupRef</c>.</p> + <p>Tells the supervisor <c><anno>SupRef</anno></c> to terminate the given + child.</p> + <p>If the supervisor is not <c>simple_one_for_one</c>, + <c><anno>Id</anno></c> must be the child specification identifier. The + process, if there is one, is terminated but the child + specification is kept by the supervisor. The child process + may later be restarted by the supervisor. The child process + can also be restarted explicitly by calling + <c>restart_child/2</c>. Use <c>delete_child/2</c> to remove + the child specification.</p> + <p>If the supervisor is <c>simple_one_for_one</c>, <c><anno>Id</anno></c> + must be the child process' <c>pid()</c>. I the specified + process is alive, but is not a child of the given + supervisor, the function will return + <c>{error,not_found}</c>. If the child specification + identifier is given instead instead of a <c>pid()</c>, the + function will return <c>{error,simple_one_for_one}</c>.</p> <p>If successful, the function returns <c>ok</c>. If there is - no child specification with the specified <c>Id</c>, - the function returns <c>{error,not_found}</c>.</p> + no child specification with the specified <c><anno>Id</anno></c>, the + function returns <c>{error,not_found}</c>.</p> + <p>See <c>start_child/2</c> for a description of + <c><anno>SupRef</anno></c>.</p> </desc> </func> <func> - <name>delete_child(SupRef, Id) -> Result</name> + <name name="delete_child" arity="2"/> <fsummary>Delete a child specification from a supervisor.</fsummary> - <type> - <v>SupRef = Name | {Name,Node} | {global,Name} | pid()</v> - <v> Name = Node = atom()</v> - <v>Id = term()</v> - <v>Result = ok | {error,Error}</v> - <v> Error = running | not_found | simple_one_for_one</v> - </type> <desc> - <p>Tells the supervisor <c>SupRef</c> to delete the child - specification identified by <c>Id</c>. The corresponding child + <p>Tells the supervisor <c><anno>SupRef</anno></c> to delete the child + specification identified by <c><anno>Id</anno></c>. The corresponding child process must not be running, use <c>terminate_child/2</c> to terminate it.</p> - <p>See <c>start_child/2</c> for a description of <c>SupRef</c>.</p> + <p>See <seealso marker="#SupRef"><c>start_child/2</c></seealso> for a description of + <c>SupRef</c>.</p> <p>If successful, the function returns <c>ok</c>. If the child - specification identified by <c>Id</c> exists but + specification identified by <c><anno>Id</anno></c> exists but the corresponding child process is running, the function returns <c>{error,running}</c>. If the child specification - identified by <c>Id</c> does not exist, the function returns + identified by <c><anno>Id</anno></c> does not exist, the function returns <c>{error,not_found}</c>.</p> </desc> </func> <func> - <name>restart_child(SupRef, Id) -> Result</name> + <name name="restart_child" arity="2"/> <fsummary>Restart a terminated child process belonging to a supervisor.</fsummary> - <type> - <v>SupRef = Name | {Name,Node} | {global,Name} | pid()</v> - <v> Name = Node = atom()</v> - <v>Id = term()</v> - <v>Result = {ok,Child} | {ok,Child,Info} | {error,Error}</v> - <v> Child = pid() | undefined</v> - <v> Error = running | not_found | simple_one_for_one | term()</v> - </type> <desc> - <p>Tells the supervisor <c>SupRef</c> to restart a child process + <p>Tells the supervisor <c><anno>SupRef</anno></c> to restart a child process corresponding to the child specification identified by - <c>Id</c>. The child specification must exist and + <c><anno>Id</anno></c>. The child specification must exist and the corresponding child process must not be running.</p> - <p>See <c>start_child/2</c> for a description of <c>SupRef</c>.</p> - <p>If the child specification identified by <c>Id</c> does not + <p>See <seealso marker="#SupRef"><c>start_child/2</c></seealso> for a description of + <c>SupRef</c>.</p> + <p>If the child specification identified by <c><anno>Id</anno></c> does not exist, the function returns <c>{error,not_found}</c>. If the child specification exists but the corresponding process is already running, the function returns <c>{error,running}</c>.</p> - <p>If the child process start function returns <c>{ok,Child}</c> - or <c>{ok,Child,Info}</c>, the pid is added to the supervisor + <p>If the child process start function returns <c>{ok,<anno>Child</anno>}</c> + or <c>{ok,<anno>Child</anno>,<anno>Info</anno>}</c>, the pid is added to the supervisor and the function returns the same value.</p> <p>If the child process start function returns <c>ignore</c>, the pid remains set to <c>undefined</c> and the function returns <c>{ok,undefined}</c>.</p> <p>If the child process start function returns an error tuple or an erroneous value, or if it fails, the function returns - <c>{error,Error}</c> where <c>Error</c> is a term containing + <c>{error,<anno>Error</anno>}</c> where <c><anno>Error</anno></c> is a term containing information about the error.</p> </desc> </func> <func> - <name>which_children(SupRef) -> [{Id,Child,Type,Modules}]</name> + <name name="which_children" arity="1"/> <fsummary>Return information about all children specifications and child processes belonging to a supervisor.</fsummary> - <type> - <v>SupRef = Name | {Name,Node} | {global,Name} | pid()</v> - <v> Name = Node = atom()</v> - <v>Id = term() | undefined</v> - <v>Child = pid() | undefined</v> - <v>Type = worker | supervisor</v> - <v>Modules = [Module] | dynamic</v> - <v> Module = atom()</v> - </type> <desc> <p>Returns a newly created list with information about all child specifications and child processes belonging to - the supervisor <c>SupRef</c>.</p> + the supervisor <c><anno>SupRef</anno></c>.</p> <p>Note that calling this function when supervising a large number of children under low memory conditions can cause an out of memory exception.</p> - <p>See <c>start_child/2</c> for a description of <c>SupRef</c>.</p> + <p>See <seealso marker="#SupRef"><c>start_child/2</c></seealso> for a description of + <c>SupRef</c>.</p> <p>The information given for each child specification/process is:</p> <list type="bulleted"> <item> - <p><c>Id</c> - as defined in the child specification or + <p><c><anno>Id</anno></c> - as defined in the child specification or <c>undefined</c> in the case of a <c>simple_one_for_one</c> supervisor.</p> </item> <item> - <p><c>Child</c> - the pid of the corresponding child + <p><c><anno>Child</anno></c> - the pid of the corresponding child process, or <c>undefined</c> if there is no such process.</p> </item> <item> - <p><c>Type</c> - as defined in the child specification.</p> + <p><c><anno>Type</anno></c> - as defined in the child specification.</p> </item> <item> - <p><c>Modules</c> - as defined in the child specification.</p> + <p><c><anno>Modules</anno></c> - as defined in the child specification.</p> </item> </list> </desc> </func> <func> - <name>count_children(SupRef) -> PropListOfCounts</name> + <name name="count_children" arity="1"/> <fsummary>Return counts for the number of childspecs, active children, supervisors and workers.</fsummary> - <type> - <v>SupRef = Name | {Name,Node} | {global,Name} | pid()</v> - <v> Name = Node = atom()</v> - <v>PropListOfCounts = [{specs, ChildSpecCount}, {active, ActiveProcessCount}, {supervisors, ChildSupervisorCount}, {workers, ChildWorkerCount}]</v> - </type> <desc> <p>Returns a property list (see <c>proplists</c>) containing the counts for each of the following elements of the supervisor's @@ -464,17 +470,12 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules} </desc> </func> <func> - <name>check_childspecs([ChildSpec]) -> Result</name> + <name name="check_childspecs" arity="1"/> <fsummary>Check if children specifications are syntactically correct.</fsummary> - <type> - <v>ChildSpec = child_spec()</v> - <v>Result = ok | {error,Error}</v> - <v> Error = term()</v> - </type> <desc> <p>This function takes a list of child specification as argument and returns <c>ok</c> if all of them are syntactically - correct, or <c>{error,Error}</c> otherwise.</p> + correct, or <c>{error,<anno>Error</anno>}</c> otherwise.</p> </desc> </func> </funcs> @@ -491,9 +492,9 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules} <type> <v>Args = term()</v> <v>Result = {ok,{{RestartStrategy,MaxR,MaxT},[ChildSpec]}} | ignore</v> - <v> RestartStrategy = one_for_all | one_for_one | rest_for_one | simple_one_for_one</v> - <v> MaxR = MaxT = int()>=0</v> - <v> ChildSpec = child_spec()</v> + <v> RestartStrategy = <seealso marker="#type-strategy">strategy()</seealso></v> + <v> MaxR = MaxT = integer()>=0</v> + <v> ChildSpec = <seealso marker="#type-child_spec">child_spec()</seealso></v> </type> <desc> <p>Whenever a supervisor is started using diff --git a/lib/stdlib/doc/src/supervisor_bridge.xml b/lib/stdlib/doc/src/supervisor_bridge.xml index b334f57caf..c1a5e7947f 100644 --- a/lib/stdlib/doc/src/supervisor_bridge.xml +++ b/lib/stdlib/doc/src/supervisor_bridge.xml @@ -5,7 +5,7 @@ <header> <copyright> <year>1996</year> - <year>2007</year> + <year>2011</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> @@ -50,46 +50,37 @@ </description> <funcs> <func> - <name>start_link(Module, Args) -> Result</name> - <name>start_link(SupBridgeName, Module, Args) -> Result</name> + <name name="start_link" arity="2"/> + <name name="start_link" arity="3"/> <fsummary>Create a supervisor bridge process.</fsummary> - <type> - <v>SupBridgeName = {local,Name} | {global,Name}</v> - <v> Name = atom()</v> - <v>Module = atom()</v> - <v>Args = term()</v> - <v>Result = {ok,Pid} | ignore | {error,Error}</v> - <v> Pid = pid()</v> - <v> Error = {already_started,Pid} | term()</v> - </type> <desc> <p>Creates a supervisor_bridge process, linked to the calling - process, which calls <c>Module:init/1</c> to start the subsystem. + process, which calls <c><anno>Module</anno>:init/1</c> to start the subsystem. To ensure a synchronized start-up procedure, this function does - not return until <c>Module:init/1</c> has returned.</p> - <p>If <c>SupBridgeName={local,Name}</c> the supervisor_bridge is - registered locally as <c>Name</c> using <c>register/2</c>. - If <c>SupBridgeName={global,Name}</c> the supervisor_bridge is - registered globally as <c>Name</c> using + not return until <c><anno>Module</anno>:init/1</c> has returned.</p> + <p>If <c><anno>SupBridgeName</anno>={local,<anno>Name</anno>}</c> the supervisor_bridge is + registered locally as <c><anno>Name</anno></c> using <c>register/2</c>. + If <c><anno>SupBridgeName</anno>={global,<anno>Name</anno>}</c> the supervisor_bridge is + registered globally as <c><anno>Name</anno></c> using <c>global:register_name/2</c>. If no name is provided, the supervisor_bridge is not registered. If there already exists a process with the specified - <c>SupBridgeName</c> the function returns - <c>{error,{already_started,Pid}}</c>, where <c>Pid</c> is the pid + <c><anno>SupBridgeName</anno></c> the function returns + <c>{error,{already_started,<anno>Pid</anno>}}</c>, where <c><anno>Pid</anno></c> is the pid of that process.</p> - <p><c>Module</c> is the name of the callback module.</p> - <p><c>Args</c> is an arbitrary term which is passed as the argument - to <c>Module:init/1</c>.</p> + <p><c><anno>Module</anno></c> is the name of the callback module.</p> + <p><c><anno>Args</anno></c> is an arbitrary term which is passed as the argument + to <c><anno>Module</anno>:init/1</c>.</p> <p>If the supervisor_bridge and the subsystem are successfully - started the function returns <c>{ok,Pid}</c>, where <c>Pid</c> is + started the function returns <c>{ok,<anno>Pid</anno>}</c>, where <c><anno>Pid</anno></c> is is the pid of the supervisor_bridge.</p> - <p>If <c>Module:init/1</c> returns <c>ignore</c>, this function + <p>If <c><anno>Module</anno>:init/1</c> returns <c>ignore</c>, this function returns <c>ignore</c> as well and the supervisor_bridge terminates with reason <c>normal</c>. - If <c>Module:init/1</c> fails or returns an error tuple or an - incorrect value, this function returns <c>{error,Term}</c> where - <c>Term</c> is a term with information about the error, and - the supervisor_bridge terminates with reason <c>Term</c>.</p> + If <c><anno>Module</anno>:init/1</c> fails or returns an error tuple or an + incorrect value, this function returns <c>{error,<anno>Error</anno>r}</c> where + <c><anno>Error</anno></c> is a term with information about the error, and + the supervisor_bridge terminates with reason <c><anno>Error</anno></c>.</p> </desc> </func> </funcs> diff --git a/lib/stdlib/doc/src/sys.xml b/lib/stdlib/doc/src/sys.xml index 10ead62073..073faf2df2 100644 --- a/lib/stdlib/doc/src/sys.xml +++ b/lib/stdlib/doc/src/sys.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2009</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -34,29 +34,17 @@ <module>sys</module> <modulesummary>A Functional Interface to System Messages</modulesummary> <description> - <p>This module contains functions for sending system messages used by programs, and messaged used for debugging purposes. + <p>This module contains functions for sending system messages used by programs, and messages used for debugging purposes. </p> <p>Functions used for implementation of processes should also understand system messages such as debugging messages and code change. These functions must be used to implement the use of system messages for a process; either directly, or through standard behaviours, such as <c>gen_server</c>.</p> - <p>The following types are used in the functions defined below:</p> - <list type="bulleted"> - <item> - <p><c>Name = pid() | atom() | {global, atom()}</c></p> - </item> - <item> - <p><c>Timeout = int() >= 0 | infinity</c></p> - </item> - <item> - <p><c>system_event() = {in, Msg} | {in, Msg, From} | {out, Msg, To} | term()</c></p> - </item> - </list> <p>The default timeout is 5000 ms, unless otherwise specified. The <c>timeout</c> defines the time period to wait for the process to respond to a request. If the process does not respond, the function evaluates <c>exit({timeout, {M, F, A}})</c>. </p> - <p>The functions make reference to a debug structure. + <p><marker id="dbg_opt"/>The functions make reference to a debug structure. The debug structure is a list of <c>dbg_opt()</c>. <c>dbg_opt()</c> is an internal data type used by the <c>handle_system_msg/6</c> function. No debugging is performed if it is an empty list. @@ -113,20 +101,31 @@ own system events. It is always up to the process itself to format these events.</p> </section> + <datatypes> + <datatype> + <name name="name"/> + </datatype> + <datatype> + <name name="system_event"/> + </datatype> + <datatype> + <name name="dbg_opt"/> + <desc><p>See <seealso marker="#dbg_opt">above</seealso>.</p></desc> + </datatype> + <datatype> + <name name="dbg_fun"/> + </datatype> + </datatypes> <funcs> <func> - <name>log(Name,Flag)</name> - <name>log(Name,Flag,Timeout) -> ok | {ok, [system_event()]}</name> + <name name="log" arity="2"/> + <name name="log" arity="3"/> <fsummary>Log system events in memory</fsummary> - <type> - <v>Flag = true | {true, N} | false | get | print</v> - <v>N = integer() > 0</v> - </type> <desc> <p>Turns the logging of system events On or Off. If On, a - maximum of <c>N</c> events are kept in the - debug structure (the default is 10). If <c>Flag</c> is <c>get</c>, a list of all - logged events is returned. If <c>Flag</c> is <c>print</c>, the + maximum of <c><anno>N</anno></c> events are kept in the + debug structure (the default is 10). If <c><anno>Flag</anno></c> is <c>get</c>, a list of all + logged events is returned. If <c><anno>Flag</anno></c> is <c>print</c>, the logged events are printed to <c>standard_io</c>. The events are formatted with a function that is defined by the process that generated the event (with a call to @@ -134,13 +133,9 @@ </desc> </func> <func> - <name>log_to_file(Name,Flag)</name> - <name>log_to_file(Name,Flag,Timeout) -> ok | {error, open_file}</name> + <name name="log_to_file" arity="2"/> + <name name="log_to_file" arity="3"/> <fsummary>Log system events to the specified file</fsummary> - <type> - <v>Flag = FileName | false</v> - <v>FileName = string()</v> - </type> <desc> <p>Enables or disables the logging of all system events in textual format to the file. The events are formatted with a function that is @@ -149,27 +144,18 @@ </desc> </func> <func> - <name>statistics(Name,Flag)</name> - <name>statistics(Name,Flag,Timeout) -> ok | {ok, Statistics} </name> + <name name="statistics" arity="2"/> + <name name="statistics" arity="3"/> <fsummary>Enable or disable the collections of statistics</fsummary> - <type> - <v>Flag = true | false | get</v> - <v>Statistics = [{start_time, {Date1, Time1}}, {current_time, {Date, Time2}}, {reductions, integer()}, {messages_in, integer()}, {messages_out, integer()}]</v> - <v>Date1 = Date2 = {Year, Month, Day}</v> - <v>Time1 = Time2 = {Hour, Min, Sec}</v> - </type> <desc> - <p>Enables or disables the collection of statistics. If <c>Flag</c> is + <p>Enables or disables the collection of statistics. If <c><anno>Flag</anno></c> is <c>get</c>, the statistical collection is returned.</p> </desc> </func> <func> - <name>trace(Name,Flag)</name> - <name>trace(Name,Flag,Timeout) -> void()</name> + <name name="trace" arity="2"/> + <name name="trace" arity="3"/> <fsummary>Print all system events on <c>standard_io</c></fsummary> - <type> - <v>Flag = boolean()</v> - </type> <desc> <p>Prints all system events on <c>standard_io</c>. The events are formatted with a function that is defined by the process that @@ -178,8 +164,8 @@ </desc> </func> <func> - <name>no_debug(Name)</name> - <name>no_debug(Name,Timeout) -> void()</name> + <name name="no_debug" arity="1"/> + <name name="no_debug" arity="2"/> <fsummary>Turn off debugging</fsummary> <desc> <p>Turns off all debugging for the process. This includes @@ -188,8 +174,8 @@ </desc> </func> <func> - <name>suspend(Name)</name> - <name>suspend(Name,Timeout) -> void()</name> + <name name="suspend" arity="1"/> + <name name="suspend" arity="2"/> <fsummary>Suspend the process</fsummary> <desc> <p>Suspends the process. When the process is suspended, it @@ -198,68 +184,48 @@ </desc> </func> <func> - <name>resume(Name)</name> - <name>resume(Name,Timeout) -> void()</name> + <name name="resume" arity="1"/> + <name name="resume" arity="2"/> <fsummary>Resume a suspended process</fsummary> <desc> <p>Resumes a suspended process.</p> </desc> </func> <func> - <name>change_code(Name, Module, OldVsn, Extra)</name> - <name>change_code(Name, Module, OldVsn, Extra, Timeout) -> ok | {error, Reason}</name> + <name name="change_code" arity="4"/> + <name name="change_code" arity="5"/> <fsummary>Send the code change system message to the process</fsummary> - <type> - <v>OldVsn = undefined | term()</v> - <v>Module = atom()</v> - <v>Extra = term()</v> - </type> <desc> <p>Tells the process to change code. The process must be - suspended to handle this message. The <c>Extra</c> argument is + suspended to handle this message. The <c><anno>Extra</anno></c> argument is reserved for each process to use as its own. The function - <c>Mod:system_code_change/4</c> is called. <c>OldVsn</c> is - the old version of the <c>Module</c>.</p> + <c><anno>Module</anno>:system_code_change/4</c> is called. <c><anno>OldVsn</anno></c> is + the old version of the <c><anno>Module</anno></c>.</p> </desc> </func> <func> - <name>get_status(Name)</name> - <name>get_status(Name,Timeout) -> {status, Pid, {module, Mod}, [PDict, SysState, Parent, Dbg, Misc]}</name> + <name name="get_status" arity="1"/> + <name name="get_status" arity="2"/> <fsummary>Get the status of the process</fsummary> - <type> - <v>PDict = [{Key, Value}]</v> - <v>SysState = running | suspended</v> - <v>Parent = pid()</v> - <v>Dbg = [dbg_opt()]</v> - <v>Misc = term()</v> - </type> <desc> <p>Gets the status of the process.</p> - <p>The value of <c>Misc</c> varies for different types of + <p>The value of <c><anno>Misc</anno></c> varies for different types of processes. For example, a <c>gen_server</c> process returns the callback module's state, and a <c>gen_fsm</c> process returns information such as its current state name. Callback modules for <c>gen_server</c> and <c>gen_fsm</c> can also - customise the value of <c>Misc</c> by exporting + customise the value of <c><anno>Misc</anno></c> by exporting a <c>format_status/2</c> function that contributes module-specific information; - see <seealso marker="gen_server#format_status/2">gen_server:format_status/2</seealso> - and <seealso marker="gen_fsm#format_status/2">gen_fsm:format_status/2</seealso> + see <seealso marker="gen_server#Module:format_status/2">gen_server:format_status/2</seealso> + and <seealso marker="gen_fsm#Module:format_status/2">gen_fsm:format_status/2</seealso> for more details.</p> </desc> </func> <func> - <name>install(Name,{Func,FuncState})</name> - <name>install(Name,{Func,FuncState},Timeout)</name> + <name name="install" arity="2"/> + <name name="install" arity="3"/> <fsummary>Install a debug function in the process</fsummary> - <type> - <v>Func = dbg_fun()</v> - <v>dbg_fun() = fun(FuncState, Event, ProcState) -> done | NewFuncState</v> - <v>FuncState = term()</v> - <v>Event = system_event()</v> - <v>ProcState = term()</v> - <v>NewFuncState = term()</v> - </type> <desc> <p>This function makes it possible to install other debug functions than the ones defined above. An example of such a @@ -267,22 +233,19 @@ special event and performs some action when the event is generated. This could, for example, be turning on low level tracing. </p> - <p><c>Func</c> is called whenever a system event is + <p><c><anno>Func</anno></c> is called whenever a system event is generated. This function should return <c>done</c>, or a new func state. In the first case, the function is removed. It is removed if the function fails.</p> </desc> </func> <func> - <name>remove(Name,Func)</name> - <name>remove(Name,Func,Timeout) -> void()</name> + <name name="remove" arity="2"/> + <name name="remove" arity="3"/> <fsummary>Remove a debug function from the process</fsummary> - <type> - <v>Func = dbg_fun()</v> - </type> <desc> <p>Removes a previously installed debug function from the - process. <c>Func</c> must be the same as previously + process. <c><anno>Func</anno></c> must be the same as previously installed.</p> </desc> </func> @@ -296,86 +259,65 @@ </section> <funcs> <func> - <name>debug_options(Options) -> [dbg_opt()]</name> + <name name="debug_options" arity="1"/> <fsummary>Convert a list of options to a debug structure</fsummary> - <type> - <v>Options = [Opt]</v> - <v>Opt = trace | log | statistics | {log_to_file, FileName} | {install, {Func, FuncState}}</v> - <v>Func = dbg_fun()</v> - <v>FuncState = term()</v> - </type> <desc> <p>This function can be used by a process that initiates a debug structure from a list of options. The values of the - <c>Opt</c> argument are the same as the corresponding + <c><anno>Opt</anno></c> argument are the same as the corresponding functions.</p> </desc> </func> <func> - <name>get_debug(Item,Debug,Default) -> term()</name> + <name name="get_debug" arity="3"/> <fsummary>Get the data associated with a debug option</fsummary> - <type> - <v>Item = log | statistics</v> - <v>Debug = [dbg_opt()]</v> - <v>Default = term()</v> - </type> <desc> - <p>This function gets the data associated with a debug option. <c>Default</c> is returned if the - <c>Item</c> is not found. Can be + <p>This function gets the data associated with a debug option. <c><anno>Default</anno></c> is returned if the + <c><anno>Item</anno></c> is not found. Can be used by the process to retrieve debug data for printing before it terminates.</p> </desc> </func> <func> - <name>handle_debug([dbg_opt()],FormFunc,Extra,Event) -> [dbg_opt()]</name> + <name name="handle_debug" arity="4"/> <fsummary>Generate a system event</fsummary> - <type> - <v>FormFunc = dbg_fun()</v> - <v>Extra = term()</v> - <v>Event = system_event()</v> - </type> <desc> - <p>This function is called by a process when it generates a system event. <c>FormFunc</c> is a formatting function which is called as <c>FormFunc(Device, Event, Extra)</c> in order to print the events, which is necessary if tracing is activated. <c>Extra</c> is any - extra information which the process needs in the format function, for example the name of the process.</p> + <p>This function is called by a process when it generates a + system event. <c><anno>FormFunc</anno></c> is a formatting + function which is called as <c><anno>FormFunc</anno>(Device, + <anno>Event</anno>, <anno>Extra</anno>)</c> in order to print + the events, which is necessary if tracing is activated. + <c><anno>Extra</anno></c> is any extra information which the + process needs in the format function, for example the name + of the process.</p> </desc> </func> <func> - <name>handle_system_msg(Msg,From,Parent,Module,Debug,Misc)</name> + <name name="handle_system_msg" arity="6"/> <fsummary>Take care of system messages</fsummary> - <type> - <v>Msg = term()</v> - <v>From = pid()</v> - <v>Parent = pid()</v> - <v>Module = atom()</v> - <v>Debug = [dbg_opt()]</v> - <v>Misc = term()</v> - </type> <desc> <p>This function is used by a process module that wishes to take care of system - messages. The process receives a <c>{system, From, Msg}</c> - message and passes the <c>Msg</c> and <c>From</c> to this + messages. The process receives a <c>{system, <anno>From</anno>, <anno>Msg</anno>}</c> + message and passes the <c><anno>Msg</anno></c> and <c><anno>From</anno></c> to this function. </p> <p>This function <em>never</em> returns. It calls the function - <c>Module:system_continue(Parent, NDebug, Misc)</c> where the + <c><anno>Module</anno>:system_continue(<anno>Parent</anno>, NDebug, <anno>Misc</anno>)</c> where the process continues the execution, or - <c>Module:system_terminate(Reason, Parent, Debug, Misc)</c> if - the process should terminate. The <c>Module</c> must export + <c><anno>Module</anno>:system_terminate(Reason, <anno>Parent</anno>, <anno>Debug</anno>, <anno>Misc</anno>)</c> if + the process should terminate. The <c><anno>Module</anno></c> must export <c>system_continue/3</c>, <c>system_terminate/4</c>, and <c>system_code_change/4</c> (see below). </p> - <p>The <c>Misc</c> argument can be used to save internal data + <p>The <c><anno>Misc</anno></c> argument can be used to save internal data in a process, for example its state. It is sent to - <c>Module:system_continue/3</c> or - <c>Module:system_terminate/4</c></p> + <c><anno>Module</anno>:system_continue/3</c> or + <c><anno>Module</anno>:system_terminate/4</c></p> </desc> </func> <func> - <name>print_log(Debug) -> void()</name> + <name name="print_log" arity="1"/> <fsummary>Print the logged events in the debug structure</fsummary> - <type> - <v>Debug = [dbg_opt()]</v> - </type> <desc> <p>Prints the logged system events in the debug structure using <c>FormFunc</c> as defined when the event was @@ -383,11 +325,11 @@ </desc> </func> <func> - <name>Mod:system_continue(Parent, Debug, Misc)</name> + <name>Mod:system_continue(Parent, Debug, Misc) -> none()</name> <fsummary>Called when the process should continue its execution</fsummary> <type> <v>Parent = pid()</v> - <v>Debug = [dbg_opt()]</v> + <v>Debug = [<seealso marker="#type-dbg_opt">dbg_opt()</seealso>]</v> <v>Misc = term()</v> </type> <desc> @@ -397,12 +339,12 @@ </desc> </func> <func> - <name>Mod:system_terminate(Reason, Parent, Debug, Misc)</name> + <name>Mod:system_terminate(Reason, Parent, Debug, Misc) -> none()</name> <fsummary>Called when the process should terminate</fsummary> <type> <v>Reason = term()</v> <v>Parent = pid()</v> - <v>Debug = [dbg_opt()]</v> + <v>Debug = [<seealso marker="#type-dbg_opt">dbg_opt()</seealso>]</v> <v>Misc = term()</v> </type> <desc> diff --git a/lib/stdlib/doc/src/timer.xml b/lib/stdlib/doc/src/timer.xml index 0b6807dd6c..b741ab7db1 100644 --- a/lib/stdlib/doc/src/timer.xml +++ b/lib/stdlib/doc/src/timer.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2009</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -49,9 +49,19 @@ as requested. </p> </description> + <datatypes> + <datatype> + <name name="time"/> + <desc><p>Time in milliseconds.</p></desc> + </datatype> + <datatype> + <name name="tref"/> + <desc><p>A timer reference.</p></desc> + </datatype> + </datatypes> <funcs> <func> - <name>start() -> ok</name> + <name name="start" arity="0"/> <fsummary>Start a global timer server (named <c>timer_server</c>).</fsummary> <desc> <p>Starts the timer server. Normally, the server does not need @@ -62,199 +72,181 @@ </desc> </func> <func> - <name>apply_after(Time, Module, Function, Arguments) -> {ok, Tref} | {error, Reason}</name> + <name name="apply_after" arity="4"/> <fsummary>Apply <c>Module:Function(Arguments)</c>after a specified <c>Time</c>.</fsummary> - <type> - <v>Time = integer() in Milliseconds</v> - <v>Module = Function = atom()</v> - <v>Arguments = [term()]</v> - </type> <desc> - <p>Evaluates <c>apply(M, F, A)</c> after <c>Time</c> amount of time - has elapsed. Returns <c>{ok, TRef}</c>, or <c>{error, Reason}</c>.</p> + <p>Evaluates <c>apply(<anno>Module</anno>, <anno>Function</anno>, <anno>Arguments</anno>)</c> after <c><anno>Time</anno></c> amount of time + has elapsed. Returns <c>{ok, <anno>TRef</anno>}</c>, or <c>{error, <anno>Reason</anno>}</c>.</p> </desc> </func> <func> - <name>send_after(Time, Pid, Message) -> {ok, TRef} | {error,Reason}</name> - <name>send_after(Time, Message) -> {ok, TRef} | {error,Reason}</name> + <name name="send_after" arity="2"/> + <name name="send_after" arity="3"/> <fsummary>Send <c>Message</c>to <c>Pid</c>after a specified <c>Time</c>.</fsummary> - <type> - <v>Time = integer() in Milliseconds</v> - <v>Pid = pid() | atom()</v> - <v>Message = term()</v> - <v>Result = {ok, TRef} | {error, Reason}</v> - </type> <desc> <p></p> <taglist> <tag><c>send_after/3</c></tag> <item> - <p>Evaluates <c>Pid ! Message</c> after <c>Time</c> amount - of time has elapsed. (<c>Pid</c> can also be an atom of a - registered name.) Returns <c>{ok, TRef}</c>, or - <c>{error, Reason}</c>.</p> + <p>Evaluates <c><anno>Pid</anno> ! <anno>Message</anno></c> after <c><anno>Time</anno></c> amount + of time has elapsed. (<c><anno>Pid</anno></c> can also be an atom of a + registered name.) Returns <c>{ok, <anno>TRef</anno>}</c>, or + <c>{error, <anno>Reason</anno>}</c>.</p> </item> <tag><c>send_after/2</c></tag> <item> - <p>Same as <c>send_after(Time, self(), Message)</c>.</p> + <p>Same as <c>send_after(<anno>Time</anno>, self(), <anno>Message</anno>)</c>.</p> </item> </taglist> </desc> </func> <func> - <name>exit_after(Time, Pid, Reason1) -> {ok, TRef} | {error,Reason2}</name> - <name>exit_after(Time, Reason1) -> {ok, TRef} | {error,Reason2}</name> - <name>kill_after(Time, Pid)-> {ok, TRef} | {error,Reason2}</name> - <name>kill_after(Time) -> {ok, TRef} | {error,Reason2}</name> + <name name="kill_after" arity="1"/> + <name name="kill_after" arity="2"/> + <name name="exit_after" arity="2"/> + <name name="exit_after" arity="3"/> <fsummary>Send an exit signal with <c>Reason</c>after a specified <c>Time</c>.</fsummary> - <type> - <v>Time = integer() in milliseconds</v> - <v>Pid = pid() | atom()</v> - <v>Reason1 = Reason2 = term()</v> - </type> <desc> <p></p> <taglist> <tag><c>exit_after/3</c></tag> <item> - <p>Send an exit signal with reason <c>Reason1</c> to Pid - <c>Pid</c>. Returns <c>{ok, TRef}</c>, or - <c>{error, Reason2}</c>.</p> + <p>Send an exit signal with reason <c><anno>Reason1</anno></c> to Pid + <c><anno>Pid</anno></c>. Returns <c>{ok, <anno>TRef</anno>}</c>, or + <c>{error, <anno>Reason2</anno>}</c>.</p> </item> <tag><c>exit_after/2</c></tag> <item> - <p>Same as <c>exit_after(Time, self(), Reason1)</c>. </p> + <p>Same as <c>exit_after(<anno>Time</anno>, self(), <anno>Reason1</anno>)</c>. </p> </item> <tag><c>kill_after/2</c></tag> <item> - <p>Same as <c>exit_after(Time, Pid, kill)</c>. </p> + <p>Same as <c>exit_after(<anno>Time</anno>, <anno>Pid</anno>, kill)</c>. </p> </item> <tag><c>kill_after/1</c></tag> <item> - <p>Same as <c>exit_after(Time, self(), kill)</c>. </p> + <p>Same as <c>exit_after(<anno>Time</anno>, self(), kill)</c>. </p> </item> </taglist> </desc> </func> <func> - <name>apply_interval(Time, Module, Function, Arguments) -> {ok, TRef} | {error, Reason}</name> + <name name="apply_interval" arity="4"/> <fsummary>Evaluate <c>Module:Function(Arguments)</c>repeatedly at intervals of <c>Time</c>.</fsummary> - <type> - <v>Time = integer() in milliseconds</v> - <v>Module = Function = atom()</v> - <v>Arguments = [term()]</v> - </type> <desc> - <p>Evaluates <c>apply(Module, Function, Arguments)</c> repeatedly at - intervals of <c>Time</c>. Returns <c>{ok, TRef}</c>, or - <c>{error, Reason}</c>.</p> + <p>Evaluates <c>apply(<anno>Module</anno>, <anno>Function</anno>, <anno>Arguments</anno>)</c> repeatedly at + intervals of <c><anno>Time</anno></c>. Returns <c>{ok, <anno>TRef</anno>}</c>, or + <c>{error, <anno>Reason</anno>}</c>.</p> </desc> </func> <func> - <name>send_interval(Time, Pid, Message) -> {ok, TRef} | {error, Reason}</name> - <name>send_interval(Time, Message) -> {ok, TRef} | {error, Reason}</name> + <name name="send_interval" arity="2"/> + <name name="send_interval" arity="3"/> <fsummary>Send <c>Message</c>repeatedly at intervals of <c>Time</c>.</fsummary> - <type> - <v>Time = integer() in milliseconds</v> - <v>Pid = pid() | atom()</v> - <v>Message = term()</v> - <v>Reason = term()</v> - </type> <desc> <p></p> <taglist> <tag><c>send_interval/3</c></tag> <item> - <p>Evaluates <c>Pid ! Message</c> repeatedly after <c>Time</c> - amount of time has elapsed. (<c>Pid</c> can also be an atom of - a registered name.) Returns <c>{ok, TRef}</c> or - <c>{error, Reason}</c>.</p> + <p>Evaluates <c><anno>Pid</anno> ! <anno>Message</anno></c> repeatedly after <c><anno>Time</anno></c> + amount of time has elapsed. (<c><anno>Pid</anno></c> can also be an atom of + a registered name.) Returns <c>{ok, <anno>TRef</anno>}</c> or + <c>{error, <anno>Reason</anno>}</c>.</p> </item> <tag><c>send_interval/2</c></tag> <item> - <p>Same as <c>send_interval(Time, self(), Message)</c>.</p> + <p>Same as <c>send_interval(<anno>Time</anno>, self(), <anno>Message</anno>)</c>.</p> </item> </taglist> </desc> </func> <func> - <name>cancel(TRef) -> {ok, cancel} | {error, Reason}</name> + <name name="cancel" arity="1"/> <fsummary>Cancel a previously requested timeout identified by <c>TRef</c>.</fsummary> <desc> - <p>Cancels a previously requested timeout. <c>TRef</c> is a unique + <p>Cancels a previously requested timeout. <c><anno>TRef</anno></c> is a unique timer reference returned by the timer function in question. Returns - <c>{ok, cancel}</c>, or <c>{error, Reason}</c> when <c>TRef</c> + <c>{ok, cancel}</c>, or <c>{error, <anno>Reason</anno>}</c> when <c><anno>TRef</anno></c> is not a timer reference.</p> </desc> </func> <func> - <name>sleep(Time) -> ok</name> + <name name="sleep" arity="1"/> <fsummary>Suspend the calling process for <c>Time</c>amount of milliseconds.</fsummary> - <type> - <v>Time = integer() in milliseconds or the atom infinity</v> - </type> <desc> - <p>Suspends the process calling this function for <c>Time</c> amount + <p>Suspends the process calling this function for <c><anno>Time</anno></c> amount of milliseconds and then returns <c>ok</c>, or suspend the process - forever if <c>Time</c> is the atom <c>infinity</c>. Naturally, this + forever if <c><anno>Time</anno></c> is the atom <c>infinity</c>. Naturally, this function does <em>not</em> return immediately.</p> </desc> </func> <func> - <name>tc(Module, Function, Arguments) -> {Time, Value}</name> - <fsummary>Measure the real time it takes to evaluate <c>apply(Module, Function, Arguments)</c></fsummary> - <type> - <v>Module = Function = atom()</v> - <v>Arguments = [term()]</v> - <v>Time = integer() in microseconds</v> - <v>Value = term()</v> - </type> + <name name="tc" arity="1"/> + <name name="tc" arity="2"/> + <name name="tc" arity="3"/> + <fsummary>Measure the real time it takes to evaluate <c>apply(Module, + Function, Arguments)</c> or <c>apply(Fun, Arguments)</c></fsummary> + <type_desc variable="Time">In microseconds</type_desc> <desc> - <p>Evaluates <c>apply(Module, Function, Arguments)</c> and measures - the elapsed real time. Returns <c>{Time, Value}</c>, where - <c>Time</c> is the elapsed real time in <em>microseconds</em>, - and <c>Value</c> is what is returned from the apply.</p> + <p></p> + <taglist> + <tag><c>tc/3</c></tag> + <item> + <p>Evaluates <c>apply(<anno>Module</anno>, <anno>Function</anno>, <anno>Arguments</anno>)</c> and measures + the elapsed real time as reported by <c>os:timestamp/0</c>. + Returns <c>{<anno>Time</anno>, <anno>Value</anno>}</c>, where + <c><anno>Time</anno></c> is the elapsed real time in <em>microseconds</em>, + and <c><anno>Value</anno></c> is what is returned from the apply.</p> + </item> + <tag><c>tc/2</c></tag> + <item> + <p>Evaluates <c>apply(<anno>Fun</anno>, <anno>Arguments</anno>)</c>. Otherwise works + like <c>tc/3</c>.</p> + </item> + <tag><c>tc/1</c></tag> + <item> + <p>Evaluates <c><anno>Fun</anno>()</c>. Otherwise works like <c>tc/2</c>.</p> + </item> + + </taglist> </desc> </func> <func> - <name>now_diff(T2, T1) -> Tdiff</name> + <name name="now_diff" arity="2"/> <fsummary>Calculate time difference between <c>now/0</c>timestamps</fsummary> - <type> - <v>T1 = T2 = {MegaSecs, Secs, MicroSecs}</v> - <v>Tdiff = MegaSecs = Secs = MicroSecs = integer()</v> - </type> + <type_desc variable="Tdiff">In microseconds</type_desc> <desc> - <p>Calculates the time difference <c>Tdiff = T2 - T1</c> in - <em>microseconds</em>, where <c>T1</c> and <c>T2</c> probably + <p>Calculates the time difference <c><anno>Tdiff</anno> = <anno>T2</anno> - <anno>T1</anno></c> in + <em>microseconds</em>, where <c><anno>T1</anno></c> and <c><anno>T2</anno></c> probably are timestamp tuples returned from <c>erlang:now/0</c>.</p> </desc> </func> <func> - <name>seconds(Seconds) -> Milliseconds</name> + <name name="seconds" arity="1"/> <fsummary>Convert <c>Seconds</c>to <c>Milliseconds</c>.</fsummary> <desc> - <p>Returns the number of milliseconds in <c>Seconds</c>.</p> + <p>Returns the number of milliseconds in <c><anno>Seconds</anno></c>.</p> </desc> </func> <func> - <name>minutes(Minutes) -> Milliseconds</name> + <name name="minutes" arity="1"/> <fsummary>Converts <c>Minutes</c>to <c>Milliseconds</c>.</fsummary> <desc> - <p>Return the number of milliseconds in <c>Minutes</c>.</p> + <p>Return the number of milliseconds in <c><anno>Minutes</anno></c>.</p> </desc> </func> <func> - <name>hours(Hours) -> Milliseconds</name> + <name name="hours" arity="1"/> <fsummary>Convert <c>Hours</c>to <c>Milliseconds</c>.</fsummary> <desc> - <p>Returns the number of milliseconds in <c>Hours</c>.</p> + <p>Returns the number of milliseconds in <c><anno>Hours</anno></c>.</p> </desc> </func> <func> - <name>hms(Hours, Minutes, Seconds) -> Milliseconds</name> + <name name="hms" arity="3"/> <fsummary>Convert <c>Hours</c>+<c>Minutes</c>+<c>Seconds</c>to <c>Milliseconds</c>.</fsummary> <desc> - <p>Returns the number of milliseconds in <c>Hours + Minutes + Seconds</c>.</p> + <p>Returns the number of milliseconds in <c><anno>Hours</anno> + <anno>Minutes</anno> + <anno>Seconds</anno></c>.</p> </desc> </func> </funcs> diff --git a/lib/stdlib/doc/src/unicode.xml b/lib/stdlib/doc/src/unicode.xml index 60edd8ade9..d02763f75c 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>2009</year> + <year>2011</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> @@ -38,50 +38,83 @@ <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> - <section> - <title>DATA TYPES</title> - <marker id="charlist_definition"></marker> - <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> - - <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> - - <code type="none"> -latin1_binary() = binary() with characters coded in iso-latin-1 -latin1_char() = integer() representing valid latin1 character (0-255) - -latin1_chardata() = latin1_charlist() | latin1_binary() + <datatypes> + <datatype> + <name name="encoding"/> + </datatype> + <datatype> + <name name="endian"/> + </datatype> + <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> + </desc> + </datatype> + <datatype> + <name name="chardata"/> + </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"/> + <desc> + <p>A <c>binary()</c> with characters coded in a user specified Unicode + encoding other than UTF-8 (UTF-16 or UTF-32).</p> + </desc> + </datatype> + <datatype> + <name name="external_chardata"/> + </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> + </datatype> + <datatype> + <name name="latin1_char"/> + <desc><p>An <c>integer()</c> representing valid latin1 + character (0-255).</p> + </desc> + </datatype> + <datatype> + <name name="latin1_chardata"/> + </datatype> + <datatype> + <name name="latin1_charlist"/> + <desc><p>A <c>latin1_binary()</c> is allowed as the tail of + the list.</p> + </desc> + </datatype> + </datatypes> -latin1_charlist() = [latin1_char() | latin1_binary() | latin1_charlist()] - a latin1_binary is allowed as the tail of the list</code> - </section> <funcs> <func> - <name>bom_to_encoding(Bin) -> {Encoding,Length}</name> + <name name="bom_to_encoding" arity="1"/> <fsummary>Identify UTF byte order marks in a binary.</fsummary> - <type> - <v>Bin = binary() of byte_size 4 or more</v> - <v>Encoding = latin1 | utf8 | {utf16,little} | {utf16,big} | {utf32,little} | {utf32,big}</v> - <v>Length = int()</v> - </type> + <type name="endian"/> + <type_desc variable="Bin">A binary() of byte_size 4 or more.</type_desc> <desc> <p>Check for a UTF byte order mark (BOM) in the beginning of a - binary. If the supplied binary <c>Bin</c> begins with a valid + binary. If the supplied binary <c><anno>Bin</anno></c> begins with a valid byte order mark for either UTF-8, UTF-16 or UTF-32, the function returns the encoding identified along with the length of the BOM in bytes.</p> @@ -90,23 +123,24 @@ latin1_charlist() = [latin1_char() | latin1_binary() | latin1_charlist()] </desc> </func> <func> - <name>characters_to_list(Data) -> list() | {error, list(), RestData} | {incomplete, list(), binary()} </name> + <name name="characters_to_list" arity="1"/> <fsummary>Convert a collection of characters to list of Unicode characters</fsummary> - <type> - <v>Data = latin1_chardata() | chardata() | external_chardata()</v> - <v>RestData = latin1_chardata() | chardata() | external_chardata()</v> - </type> <desc> - <p>Same as characters_to_list(Data,unicode).</p> + <p>Same as characters_to_list(<anno>Data</anno>,unicode).</p> </desc> </func> <func> - <name>characters_to_list(Data, InEncoding) -> list() | {error, list(), RestData} | {incomplete, list(), binary()} </name> + <name>characters_to_list(Data, InEncoding) -> Result</name> <fsummary>Convert a collection of characters to list of Unicode characters</fsummary> <type> - <v>Data = latin1_chardata() | chardata() | external_chardata()</v> - <v>RestData = latin1_chardata() | chardata() | external_chardata()</v> - <v>InEncoding = latin1 | unicode | utf8 | utf16 | utf32 | {utf16,little} | {utf16,big} | {utf32,little} | {utf32,big}</v> + <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> @@ -164,10 +198,16 @@ latin1_charlist() = [latin1_char() | latin1_binary() | latin1_charlist()] <item>Integers out of range - If <c>InEncoding</c> is <c>latin1</c>, an error occurs whenever an integer greater than 255 is found in the lists. If <c>InEncoding</c> is - of a Unicode type, error occurs whenever an integer greater than - <c>16#10FFFF</c> (the maximum unicode character) or in the - range <c>16#D800</c> to <c>16#DFFF</c> (invalid unicode - range) is found.</item> + 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> + <item>in the range <c>16#D800</c> to <c>16#DFFF</c> + (invalid unicode range)</item> + <item>or equal to 16#FFFE or 16#FFFF (non characters)</item> + </list> + is found. + </item> <item>UTF encoding incorrect - If <c>InEncoding</c> is one of the UTF types, the bytes in any binaries have to be valid @@ -228,44 +268,42 @@ latin1_charlist() = [latin1_char() | latin1_binary() | latin1_charlist()] </desc> </func> <func> - <name>characters_to_binary(Data) -> binary() | {error, binary(), RestData} | {incomplete, binary(), binary()} </name> - <fsummary>Convert a collection of characters to an UTF-8 binary</fsummary> <type> - <v>Data = latin1_chardata() | chardata() | external_chardata()</v> - <v>RestData = latin1_chardata() | chardata() | external_chardata()</v> - </type> + <name name="characters_to_binary" arity="1"/> + <fsummary>Convert a collection of characters to an UTF-8 binary</fsummary> <desc> <p>Same as characters_to_binary(Data, unicode, unicode).</p> </desc> </func> <func> - <name>characters_to_binary(Data,InEncoding) -> binary() | {error, binary(), RestData} | {incomplete, binary(), binary()} </name> - <fsummary>Convert a collection of characters to an UTF-8 binary</fsummary> <type> - <v>Data = latin1_chardata() | chardata() | external_chardata()</v> - <v>RestData = latin1_chardata() | chardata() | external_chardata()</v> - <v>InEncoding = latin1 | unicode | utf8 | utf16 | utf32 | {utf16,little} | {utf16,big} | {utf32,little} | {utf32,big}</v> + <name>characters_to_binary(Data,InEncoding) -> Result</name> + <fsummary>Convert a collection of characters to an 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> </desc> </func> <func> - <name>characters_to_binary(Data, InEncoding, OutEncoding) -> binary() | {error, binary(), RestData} | {incomplete, binary(), binary()} </name> + <name name="characters_to_binary" arity="3"/> <fsummary>Convert a collection of characters to an UTF-8 binary</fsummary> - <type> - <v>Data = latin1_chardata() | chardata() | external_chardata()</v> - <v>RestData = latin1_chardata() | chardata() | external_chardata()</v> - <v>InEncoding = latin1 | unicode | utf8 | utf16 | utf32 | {utf16,little} | {utf16,big} | {utf32,little} | {utf32,big}</v> - <v>OutEncoding = latin1 | unicode | utf8 | utf16 | utf32| {utf16,little} | {utf16,big} | {utf32,little} | {utf32,big}</v> - </type> <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 - <c>InEncoding</c> defines how input is to be interpreted if + <c><anno>InEncoding</anno></c> defines how input is to be interpreted if binaries are present in the <c>Data</c>, while - <c>OutEncoding</c> defines in what format output is to be + <c><anno>OutEncoding</anno></c> defines in what format output is to be generated.</p> <p>The option <c>unicode</c> is an alias for <c>utf8</c>, as this is the @@ -285,17 +323,13 @@ latin1_charlist() = [latin1_char() | latin1_binary() | latin1_charlist()] </desc> </func> <func> - <name>encoding_to_bom(InEncoding) -> Bin</name> + <name name="encoding_to_bom" arity="1"/> <fsummary>Create a binary UTF byte order mark from encoding.</fsummary> - <type> - <v>Bin = binary() of byte_size 4 or less</v> - <v>InEncoding = latin1 | unicode | utf8 | utf16 | utf32 | {utf16,little} | {utf16,big} | {utf32,little} | {utf32,big}</v> - <v>Length = int()</v> - </type> + <type_desc variable="Bin">A binary() of byte_size 4 or more.</type_desc> <desc> <p>Create an UTF byte order mark (BOM) as a binary from the - supplied <c>InEncoding</c>. The BOM is, if supported at all, + 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> diff --git a/lib/stdlib/doc/src/unicode_usage.xml b/lib/stdlib/doc/src/unicode_usage.xml index c5bf10b63d..416df1f02c 100644 --- a/lib/stdlib/doc/src/unicode_usage.xml +++ b/lib/stdlib/doc/src/unicode_usage.xml @@ -5,7 +5,7 @@ <header> <copyright> <year>1999</year> - <year>2009</year> + <year>2010</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -143,7 +143,7 @@ en_US.UTF-8</pre> <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 it's UTF-8 capacity, we have to rely on the language and character type settings.</p> +<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> $ <input>LC_CTYPE=en_US.ISO-8859-1 erl</input> @@ -168,6 +168,48 @@ Eshell V5.7 (abort with ^G) <image file="ushell2.gif"><icaption>Unicode characters in allowed and disallowed context</icaption></image> </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> + +<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>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>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> + +<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> + +<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> +</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> +</section> +</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> @@ -185,7 +227,7 @@ Eshell V5.7 (abort with ^G) <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 it's 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> +<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> diff --git a/lib/stdlib/doc/src/win32reg.xml b/lib/stdlib/doc/src/win32reg.xml index 28960cd098..99fd7fdeb6 100644 --- a/lib/stdlib/doc/src/win32reg.xml +++ b/lib/stdlib/doc/src/win32reg.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2000</year><year>2009</year> + <year>2000</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -85,14 +85,22 @@ hkdd HKEY_DYN_DATA</pre> <p>For additional information on the Windows registry consult the Win32 Programmer's Reference.</p> </description> + <datatypes> + <datatype> + <name name="reg_handle"/> + <desc><p>As returned by <seealso marker="#open/1">open/1</seealso>.</p></desc> + </datatype> + <datatype> + <name name="name"/> + </datatype> + <datatype> + <name name="value"/> + </datatype> + </datatypes> <funcs> <func> - <name>change_key(RegHandle, Key) -> ReturnValue</name> + <name name="change_key" arity="2"/> <fsummary>Move to a key in the registry</fsummary> - <type> - <v>RegHandle = term()</v> - <v>Key = string()</v> - </type> <desc> <p>Changes the current key to another key. Works like cd. The key can be specified as a relative path or as an @@ -100,12 +108,8 @@ hkdd HKEY_DYN_DATA</pre> </desc> </func> <func> - <name>change_key_create(RegHandle, Key) -> ReturnValue</name> + <name name="change_key_create" arity="2"/> <fsummary>Move to a key, create it if it is not there</fsummary> - <type> - <v>RegHandle = term()</v> - <v>Key = string()</v> - </type> <desc> <p>Creates a key, or just changes to it, if it is already there. Works like a combination of <c>mkdir</c> and <c>cd</c>. Calls the Win32 API function @@ -114,23 +118,16 @@ hkdd HKEY_DYN_DATA</pre> </desc> </func> <func> - <name>close(RegHandle)-> ReturnValue</name> + <name name="close" arity="1"/> <fsummary>Close the registry.</fsummary> - <type> - <v>RegHandle = term()</v> - </type> <desc> - <p>Closes the registry. After that, the <c>RegHandle</c> cannot + <p>Closes the registry. After that, the <c><anno>RegHandle</anno></c> cannot be used.</p> </desc> </func> <func> - <name>current_key(RegHandle) -> ReturnValue</name> + <name name="current_key" arity="1"/> <fsummary>Return the path to the current key.</fsummary> - <type> - <v>RegHandle = term()</v> - <v>ReturnValue = {ok, string()}</v> - </type> <desc> <p>Returns the path to the current key. This is the equivalent of <c>pwd</c>.</p> <p>Note that the current key is stored in the driver, and might be @@ -138,12 +135,8 @@ hkdd HKEY_DYN_DATA</pre> </desc> </func> <func> - <name>delete_key(RegHandle) -> ReturnValue</name> + <name name="delete_key" arity="1"/> <fsummary>Delete the current key</fsummary> - <type> - <v>RegHandle = term()</v> - <v>ReturnValue = ok | {error, ErrorId}</v> - </type> <desc> <p>Deletes the current key, if it is valid. Calls the Win32 API function <c>RegDeleteKey()</c>. Note that this call does not change the current key, @@ -152,12 +145,8 @@ hkdd HKEY_DYN_DATA</pre> </desc> </func> <func> - <name>delete_value(RegHandle, Name) -> ReturnValue</name> + <name name="delete_value" arity="2"/> <fsummary>Delete the named value on the current key.</fsummary> - <type> - <v>RegHandle = term()</v> - <v>ReturnValue = ok | {error, ErrorId}</v> - </type> <desc> <p>Deletes a named value on the current key. The atom <c>default</c> is used for the the default value.</p> @@ -165,12 +154,8 @@ hkdd HKEY_DYN_DATA</pre> </desc> </func> <func> - <name>expand(String) -> ExpandedString</name> + <name name="expand" arity="1"/> <fsummary>Expand a string with environment variables</fsummary> - <type> - <v>String = string()</v> - <v>ExpandedString = string()</v> - </type> <desc> <p>Expands a string containing environment variables between percent characters. Anything between two % is taken for a environment @@ -180,23 +165,15 @@ hkdd HKEY_DYN_DATA</pre> </desc> </func> <func> - <name>format_error(ErrorId) -> ErrorString</name> + <name name="format_error" arity="1"/> <fsummary>Convert an POSIX errorcode to a string</fsummary> - <type> - <v>ErrorId = atom()</v> - <v>ErrorString = string()</v> - </type> <desc> <p>Convert an POSIX errorcode to a string (by calling <c>erl_posix_msg:message</c>).</p> </desc> </func> <func> - <name>open(OpenModeList)-> ReturnValue</name> + <name name="open" arity="1"/> <fsummary>Open the registry for reading or writing</fsummary> - <type> - <v>OpenModeList = [OpenMode]</v> - <v>OpenMode = read | write</v> - </type> <desc> <p>Opens the registry for reading or writing. The current key will be the root (<c>HKEY_CLASSES_ROOT</c>). The <c>read</c> flag in the mode list can be omitted.</p> @@ -204,12 +181,8 @@ hkdd HKEY_DYN_DATA</pre> </desc> </func> <func> - <name>set_value(RegHandle, Name, Value) -> ReturnValue</name> + <name name="set_value" arity="3"/> <fsummary>Set value at the current registry key with specified name.</fsummary> - <type> - <v>Name = string() | default</v> - <v>Value = string() | integer() | binary()</v> - </type> <desc> <p>Sets the named (or default) value to value. Calls the Win32 API function <c>RegSetValueEx()</c>. The value can be of three types, and @@ -221,13 +194,8 @@ hkdd HKEY_DYN_DATA</pre> </desc> </func> <func> - <name>sub_keys(RegHandle) -> ReturnValue</name> + <name name="sub_keys" arity="1"/> <fsummary>Get subkeys to the current key.</fsummary> - <type> - <v>ReturnValue = {ok, SubKeys} | {error, ErrorId}</v> - <v>SubKeys = [SubKey]</v> - <v>SubKey = string()</v> - </type> <desc> <p>Returns a list of subkeys to the current key. Calls the Win32 API function <c>EnumRegKeysEx()</c>.</p> @@ -235,13 +203,8 @@ hkdd HKEY_DYN_DATA</pre> </desc> </func> <func> - <name>value(RegHandle, Name) -> ReturnValue</name> + <name name="value" arity="2"/> <fsummary>Get the named value on the current key.</fsummary> - <type> - <v>Name = string() | default</v> - <v>ReturnValue = {ok, Value}</v> - <v>Value = string() | integer() | binary()</v> - </type> <desc> <p>Retrieves the named value (or default) on the current key. Registry values of type <c>REG_SZ</c>, are returned as strings. Type <c>REG_DWORD</c> @@ -249,15 +212,8 @@ hkdd HKEY_DYN_DATA</pre> </desc> </func> <func> - <name>values(RegHandle) -> ReturnValue</name> + <name name="values" arity="1"/> <fsummary>Get all values on the current key.</fsummary> - <type> - <v>ReturnValue = {ok, ValuePairs} | {ok, ErrorId}</v> - <v>ValuePairs = [ValuePair]</v> - <v>ValuePair = {Name, Value}</v> - <v>Name = string | default</v> - <v>Value = string() | integer() | binary()</v> - </type> <desc> <p>Retrieves a list of all values on the current key. The values have types corresponding to the registry types, see <c>value</c>. diff --git a/lib/stdlib/doc/src/zip.xml b/lib/stdlib/doc/src/zip.xml index e2ecfec8f0..b03fc7f4e2 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>2009</year> + <year>2006</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -13,12 +13,12 @@ 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. - + </legalnotice> <title>zip</title> @@ -34,24 +34,26 @@ <module>zip</module> <modulesummary>Utility for reading and creating 'zip' archives.</modulesummary> <description> - <p>The <c>zip</c> module archives and extract files to and from a zip + <p>The <c>zip</c> module archives and extracts files to and from a zip archive. The zip format is specified by the "ZIP Appnote.txt" file available on PKWare's website www.pkware.com.</p> <p>The zip module supports zip archive versions up to 6.1. However, - password-protection and Zip64 is not supported.</p> + password-protection and Zip64 are not supported.</p> <p>By convention, the name of a zip file should end in "<c>.zip</c>". To abide to the convention, you'll need to add "<c>.zip</c>" yourself to the name.</p> - <p>Zip archives are created with the - <seealso marker="#zip_2">zip/2</seealso> or the + <p>Zip archives are created with the + <seealso marker="#zip_2">zip/2</seealso> or the <seealso marker="#zip_2">zip/3</seealso> function. (They are also available as <c>create</c>, to resemble the <c>erl_tar</c> module.)</p> - <p>To extract files from a zip archive, use the - <seealso marker="#unzip_1">unzip/1</seealso> or the + <p>To extract files from a zip archive, use the + <seealso marker="#unzip_1">unzip/1</seealso> or the <seealso marker="#unzip_2">unzip/2</seealso> function. (They are also available as <c>extract</c>.)</p> - <p>To return a list of the files in a zip archive, use the + <p>To fold a function over all files in a zip archive, use the + <seealso marker="#foldl_3">foldl_3</seealso> function.</p> + <p>To return a list of the files in a zip archive, use the <seealso marker="#list_dir_1">list_dir/1</seealso> or the <seealso marker="#list_dir_2">list_dir/2</seealso> function. (They are also available as <c>table</c>.)</p> @@ -83,67 +85,55 @@ recreated.</p> </section> - <section> - <title>DATA TYPES</title> - <code type="none"> -zip_file() </code> - <p>The record <c>zip_file</c> contains the following fields.</p> - <taglist> - <tag><c>name = string()</c></tag> - <item> - <p>the name of the file</p> - </item> - <tag><c>info = file_info()</c></tag> - <item> - <p>file info as in - <seealso marker="erts:file#read_file_info/1">file:read_file_info/1</seealso></p> - </item> - <tag><c>comment = string()</c></tag> - <item> - <p>the comment for the file in the zip archive</p> - </item> - <tag><c>offset = integer()</c></tag> - <item> - <p>the offset of the file in the zip archive (used internally)</p> - </item> - <tag><c>comp_size = integer()</c></tag> - <item> - <p>the compressed size of the file (the uncompressed size is found - in <c>info</c>)</p> - </item> - </taglist> - <code type="none">zip_comment</code> - <p>The record <c>zip_comment</c> just contains the archive comment for - a zip archive</p> - <taglist> - <tag><c>comment = string()</c></tag> - <item> - <p>the comment for the zip archive</p> - </item> - </taglist> - </section> + <datatypes> + <datatype> + <name name="zip_comment"/> + <desc> + <p>The record <c>zip_comment</c> just contains the archive comment for + a zip archive</p> + </desc> + </datatype> + <datatype> + <name name="zip_file"/> + <desc> + <p>The record <c>zip_file</c> contains the following fields.</p> + <taglist> + <tag><c>name</c></tag> + <item> + <p>the name of the file</p> + </item> + <tag><c>info</c></tag> + <item> + <p>file info as in + <seealso marker="kernel:file#read_file_info/1">file:read_file_info/1</seealso></p> + </item> + <tag><c>comment</c></tag> + <item> + <p>the comment for the file in the zip archive</p> + </item> + <tag><c>offset</c></tag> + <item> + <p>the offset of the file in the zip archive (used internally)</p> + </item> + <tag><c>comp_size</c></tag> + <item> + <p>the compressed size of the file (the uncompressed size is found + in <c>info</c>)</p> + </item> + </taglist> + </desc> + </datatype> + </datatypes> <funcs> <func> - <name>zip(Name, FileList) -> RetValue</name> - <name>zip(Name, FileList, Options) -> RetValue</name> - <name>create(Name, FileList) -> RetValue</name> - <name>create(Name, FileList, Options) -> RetValue</name> + <name name="zip" arity="2"/> + <name name="zip" arity="3"/> + <name name="create" arity="2"/> + <name name="create" arity="3"/> <fsummary>Create a zip archive with options</fsummary> - <type> - <v>Name = filename()</v> - <v>FileList = [FileSpec]</v> - <v>FileSpec = filename() | {filename(), binary()}</v> - <v>Options = [Option]</v> - <v>Option = memory | cooked | verbose | {comment, Comment} | {cwd, CWD} | {compress, What} | {uncompress, What}</v> - <v>What = all | [Extension] | {add, [Extension]} | {del, [Extension]}</v> - <v>Extension = string()</v> - <v>Comment = CWD = string()</v> - <v>RetValue = {ok, Name} | {ok, {Name, binary()}} | {error, Reason}</v> - <v>Reason = term()</v> - </type> <desc> <p>The <marker id="zip_2"></marker><c>zip</c> function creates a - zip archive containing the files specified in <c>FileList</c>.</p> + zip archive containing the files specified in <c><anno>FileList</anno></c>.</p> <p>As synonyms, the functions <c>create/2</c> and <c>create/3</c> are provided, to make it resemble the <c>erl_tar</c> module.</p> <p>The file-list is a list of files, with paths relative to the @@ -153,15 +143,15 @@ zip_file() </code> <p>Files will be compressed using the DEFLATE compression, as described in the Appnote.txt file. However, files will be stored without compression if they already are compressed. - The <c>zip/2</c> and <c>zip/3</c> checks the file extension + The <c>zip/2</c> and <c>zip/3</c> functions check the file extension to see whether the file should be stored without compression. Files with the following extensions are not compressed: <c>.Z</c>, <c>.zip</c>, <c>.zoo</c>, <c>.arc</c>, <c>.lzh</c>, <c>.arj</c>.</p> <p>It is possible to override the default behavior and explicitly control what types of files that should be - compressed by using the <c>{compress, What}</c> and - <c>{uncompress, What}</c> options. It is possible to have + compressed by using the <c>{compress, <anno>What</anno>}</c> and + <c>{uncompress, <anno>What</anno>}</c> options. It is possible to have several <c>compress</c> and <c>uncompress</c> options. In order to trigger compression of a file, its extension must match with the @@ -189,81 +179,69 @@ zip_file() </code> <tag><c>memory</c></tag> <item> <p>The output will not be to a file, but instead as a tuple - <c>{FileName, binary()}</c>. The binary will be a full zip + <c>{<anno>FileName</anno>, binary()}</c>. The binary will be a full zip archive with header, and can be extracted with for instance <c>unzip/2</c>.</p> </item> - <tag><c>{comment, Comment}</c></tag> + <tag><c>{comment, <anno>Comment</anno>}</c></tag> <item> <p>Add a comment to the zip-archive.</p> </item> - <tag><c>{cwd, CWD}</c></tag> + <tag><c>{cwd, <anno>CWD</anno>}</c></tag> <item> <p>Use the given directory as current directory, it will be prepended to file names when adding them, although it will not be in the zip-archive. (Acting like a file:set_cwd/1, but without changing the global cwd property.)</p> </item> - <tag><c>{compress, What}</c></tag> + <tag><c>{compress, <anno>What</anno>}</c></tag> <item> - <p>Controls what types of files that will be + <p>Controls what types of files will be compressed. It is by default set to <c>all</c>. The following values of <c>What</c> are allowed:</p> <taglist> <tag><c>all</c></tag> <item><p> means that all files will be compressed (as long - as they pass the <c>uncompress</c> condition).</p></item> - <tag><c>[Extension]</c></tag> + as they pass the <c>uncompress</c> condition).</p></item> + <tag><c>[<anno>Extension</anno>]</c></tag> <item><p>means that only files with exactly these extensions - will be compressed.</p></item> - <tag><c>{add,[Extension]}</c></tag> + will be compressed.</p></item> + <tag><c>{add,[<anno>Extension</anno>]}</c></tag> <item><p>adds these extensions to the list of compress - extensions.</p></item> - <tag><c>{del,[Extension]}</c></tag> + extensions.</p></item> + <tag><c>{del,[<anno>Extension</anno>]}</c></tag> <item><p>deletes these extensions from the list of compress - extensions.</p></item> + extensions.</p></item> </taglist> </item> - <tag><c>{uncompress, What}</c></tag> + <tag><c>{uncompress, <anno>What</anno>}</c></tag> <item> - <p>Controls what types of files that will be uncompressed. It is by + <p>Controls what types of files will be uncompressed. It is by 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> - <item><p> means that no files will be compressed.</p></item> - <tag><c>[Extension]</c></tag> + <item><p> means that no files will be compressed.</p></item> + <tag><c>[<anno>Extension</anno>]</c></tag> <item><p>means that files with these extensions will be - uncompressed.</p></item> - <tag><c>{add,[Extension]}</c></tag> + uncompressed.</p></item> + <tag><c>{add,[<anno>Extension</anno>]}</c></tag> <item><p>adds these extensions to the list of uncompress - extensions.</p></item> - <tag><c>{del,[Extension]}</c></tag> + extensions.</p></item> + <tag><c>{del,[<anno>Extension</anno>]}</c></tag> <item><p>deletes these extensions from the list of uncompress - extensions.</p></item> + extensions.</p></item> </taglist> </item> </taglist> </desc> </func> <func> - <name>unzip(Archive) -> RetValue</name> - <name>unzip(Archive, Options) -> RetValue</name> - <name>extract(Archive) -> RetValue</name> - <name>extract(Archive, Options) -> RetValue</name> + <name name="unzip" arity="1"/> + <name name="unzip" arity="2"/> + <name name="extract" arity="1"/> + <name name="extract" arity="2"/> <fsummary>Extract files from a zip archive</fsummary> - <type> - <v>Archive = filename() | binary()</v> - <v>Options = [Option]</v> - <v>Option = {file_list, FileList} | keep_old_files | verbose | memory | {file_filter, FileFilter} | {cwd, CWD}</v> - <v>FileList = [filename()]</v> - <v>FileBinList = [{filename(),binary()}]</v> - <v>FileFilter = fun(ZipFile) -> true | false</v> - <v>CWD = string()</v> - <v>ZipFile = zip_file()</v> - <v>RetValue = {ok,FileList} | {ok,FileBinList} | {error, Reason} | {error, {Name, Reason}}</v> - <v>Reason = term()</v> - </type> <desc> <p>The <marker id="unzip_1"></marker> <c>unzip/1</c> function extracts @@ -271,26 +249,26 @@ zip_file() </code> <marker id="unzip_2"></marker> <c>unzip/2</c> function provides options to extract some files, and more.</p> - <p>If the <c>Archive</c> argument is given as a binary, + <p>If the <c><anno>Archive</anno></c> argument is given as a binary, the contents of the binary is assumed to be a zip archive, otherwise it should be a filename.</p> <p>The following options are available:</p> <taglist> - <tag><c>{file_list, FileList}</c></tag> + <tag><c>{file_list, <anno>FileList</anno>}</c></tag> <item> <p>By default, all files will be extracted from the zip - archive. With the <c>{file_list,FileList}</c> option, + archive. With the <c>{file_list, <anno>FileList</anno>}</c> option, the <c>unzip/2</c> function will only extract the files - whose names are included in <c>FileList</c>. The full + whose names are included in <c><anno>FileList</anno></c>. The full paths, including the names of all sub directories within - the zip archive, must be specified.</p> + the zip archive, must be specified.</p> </item> <tag><c>cooked</c></tag> <item> <p>By default, the <c>open/2</c> function will open the zip file in <c>raw</c> mode, which is faster but does not allow a remote (erlang) file server to be used. Adding <c>cooked</c> - to the mode list will override the default and open zip file + to the mode list will override the default and open the zip file without the <c>raw</c> option. The same goes for the files extracted.</p> </item> @@ -299,7 +277,7 @@ zip_file() </code> <p>By default, all existing files with the same name as file in the zip archive will be overwritten. With the <c>keep_old_files</c> option, the <c>unzip/2</c> function will not overwrite any existing - files. Not that even with the <c>memory</c> option given, which + files. Note that even with the <c>memory</c> option given, which means that no files will be overwritten, files existing will be excluded from the result.</p> </item> @@ -327,26 +305,74 @@ zip_file() </code> </desc> </func> <func> - <name>list_dir(Archive) -> RetValue</name> - <name>list_dir(Archive, Options)</name> - <name>table(Archive) -> RetValue</name> - <name>table(Archive, Options)</name> + <name name="foldl" arity="3"/> + <fsummary>Fold a function over all files in a zip archive</fsummary> + <desc> + <p>The <marker id="foldl_3"></marker> <c>foldl/3</c> function + calls <c><anno>Fun</anno>(<anno>FileInArchive</anno>, <anno>GetInfo + </anno>, <anno>GetBin</anno>, <anno>AccIn</anno>)</c> on + successive files in the <c>Archive</c>, starting with + <c><anno>AccIn</anno> + == <anno>Acc0</anno></c>. <c><anno>FileInArchive</anno></c> is + the name that the file + has in the archive. <c><anno>GetInfo</anno></c> is a fun that + returns info + about the the file. <c><anno>GetBin</anno></c> returns the contents + of the + file. Both <c><anno>GetInfo</anno></c> and <c><anno>GetBin</anno></c> + must be called + within the <c><anno>Fun</anno></c>. Their behavior is undefined if + they are + called outside the context of the <c><anno>Fun</anno></c>. + The <c><anno>Fun</anno></c> + must return a new accumulator which is passed to the next + call. <c>foldl/3</c> returns the final value of the + accumulator. <c><anno>Acc0</anno></c> is returned if the archive is + empty. It is not necessary to iterate over all files in the + archive. The iteration may be ended prematurely in a + controlled manner by throwing an exception.</p> + + <p>For example:</p> + <pre> +> <input>Name = "dummy.zip".</input> +"dummy.zip" +> <input>{ok, {Name, Bin}} = zip:create(Name, [{"foo", <<"FOO">>}, {"bar", <<"BAR">>}], [memory]).</input> +{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>{ok, FileSpec} = zip:foldl(fun(N, I, B, Acc) -> [{N, B(), I()} | Acc] end, [], {Name, Bin}).</input> +{ok,[{"bar",<<"BAR">>, + {file_info,3,regular,read_write, + {{2010,3,1},{19,2,10}}, + {{2010,3,1},{19,2,10}}, + {{2010,3,1},{19,2,10}}, + 54,1,0,0,0,0,0}}, + {"foo",<<"FOO">>, + {file_info,3,regular,read_write, + {{2010,3,1},{19,2,10}}, + {{2010,3,1},{19,2,10}}, + {{2010,3,1},{19,2,10}}, + 54,1,0,0,0,0,0}}]} +> <input>{ok, {Name, Bin}} = zip:create(Name, lists:reverse(FileSpec), [memory]).</input> +{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> +<<"FOO">> +</pre> + </desc> + </func> + <func> + <name name="list_dir" arity="1"/> + <name name="list_dir" arity="2"/> + <name name="table" arity="1" /> + <name name="table" arity="2"/> <fsummary>Retrieve the name of all files in a zip archive</fsummary> - <type> - <v>Archive = filename() | binary()</v> - <v>RetValue = {ok, [Comment, Files]} | {error, Reason}</v> - <v>Comment = zip_comment()</v> - <v>Files = [zip_file()]</v> - <v>Options = [Option]</v> - <v>Option = cooked</v> - <v>Reason = term()</v> - </type> <desc> - <p>The <marker id="list_dir_1"></marker> -<c>list_dir/1</c> function retrieves - the names of all files in the zip archive <c>Archive</c>. The - <marker id="list_dir_2"></marker> -<c>list_dir/2</c> function provides options.</p> + <p>The <marker id="list_dir_1"></marker><c>list_dir/1</c> + function retrieves the names of all files in the zip archive + <c><anno>Archive</anno></c>. The <marker id="list_dir_2"></marker> + <c>list_dir/2</c> function provides options.</p> <p>As synonyms, the functions <c>table/2</c> and <c>table/3</c> are provided, to make it resemble the <c>erl_tar</c> module.</p> <p>The result value is the tuple <c>{ok, List}</c>, where <c>List</c> @@ -358,50 +384,34 @@ zip_file() </code> <p>By default, the <c>open/2</c> function will open the zip file in <c>raw</c> mode, which is faster but does not allow a remote (erlang) file server to be used. Adding <c>cooked</c> - to the mode list will override the default and open zip file + to the mode list will override the default and open the zip file without the <c>raw</c> option.</p> </item> </taglist> </desc> </func> <func> - <name>t(Archive)</name> + <name name="t" arity="1"/> <fsummary>Print the name of each file in a zip archive</fsummary> - <type> - <v>Archive = filename() | binary() | ZipHandle</v> - <v>ZipHandle = pid()</v> - </type> <desc> - <p>The <marker id="t_1"></marker> -<c>t/1</c> function prints the names - of all files in the zip archive <c>Archive</c> to the Erlang shell. + <p>The <marker id="t_1"></marker><c>t/1</c> function prints the names + of all files in the zip archive <c><anno>Archive</anno></c> to the Erlang shell. (Similar to "<c>tar t</c>".)</p> </desc> </func> <func> - <name>tt(Archive)</name> + <name name="tt" arity="1"/> <fsummary>Print name and information for each file in a zip archive</fsummary> - <type> - <v>Archive = filename() | binary()</v> - </type> <desc> - <p>The <marker id="tt_1"></marker> -<c>tt/1</c> function prints names and - information about all files in the zip archive <c>Archive</c> to + <p>The <marker id="tt_1"></marker><c>tt/1</c> function prints names and + information about all files in the zip archive <c><anno>Archive</anno></c> to the Erlang shell. (Similar to "<c>tar tv</c>".)</p> </desc> </func> <func> - <name>zip_open(Archive) -> {ok, ZipHandle} | {error, Reason}</name> - <name>zip_open(Archive, Options) -> {ok, ZipHandle} | {error, Reason}</name> + <name name="zip_open" arity="1"/> + <name name="zip_open" arity="2"/> <fsummary>Open an archive and return a handle to it</fsummary> - <type> - <v>Archive = filename() | binary()</v> - <v>Options = [Option]</v> - <v>Options = cooked | memory | {cwd, CWD}</v> - <v>CWD = string()</v> - <v>ZipHandle = pid()</v> - </type> <desc> <p>The <marker id="zip_open"></marker> <c>zip_open</c> function opens a @@ -412,29 +422,19 @@ zip_file() </code> </desc> </func> <func> - <name>zip_list_dir(ZipHandle) -> Result | {error, Reason}</name> + <name name="zip_list_dir" arity="1"/> <fsummary>Return a table of files in open zip archive</fsummary> - <type> - <v>Result = [ZipComment, ZipFile...]</v> - <v>ZipComment = #zip_comment{}</v> - <v>ZipFile = #zip_file{}</v> - <v>ZipHandle = pid()</v> - </type> <desc> - <p>The <marker id="zip_list_dir"></marker> -<c>zip_list_dir/1</c> function - returns the file list of an open zip archive.</p> + <p>The <marker id="zip_list_dir"></marker> + <c>zip_list_dir/1</c> function + returns the file list of an open zip archive. The first returned + element is the zip archive comment.</p> </desc> </func> <func> - <name>zip_get(ZipHandle) -> {ok, [Result]} | {error, Reason}</name> - <name>zip_get(FileName, ZipHandle) -> {ok, Result} | {error, Reason}</name> + <name name="zip_get" arity="1"/> + <name name="zip_get" arity="2"/> <fsummary>Extract files from an open archive</fsummary> - <type> - <v>FileName = filename()</v> - <v>ZipHandle = pid()</v> - <v>Result = filename() | {filename(), binary()}</v> - </type> <desc> <p>The <marker id="zip_get"></marker> <c>zip_get</c> function extracts @@ -445,11 +445,8 @@ zip_file() </code> </desc> </func> <func> - <name>zip_close(ZipHandle) -> ok | {error, einval}</name> + <name name="zip_close" arity="1"/> <fsummary>Close an open archive</fsummary> - <type> - <v>ZipHandle = pid()</v> - </type> <desc> <p>The <marker id="zip_close"></marker> <c>zip_close/1</c> function closes diff --git a/lib/stdlib/include/zip.hrl b/lib/stdlib/include/zip.hrl index 2b5ddc1dfe..6e3ed9c78a 100644 --- a/lib/stdlib/include/zip.hrl +++ b/lib/stdlib/include/zip.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2009. All Rights Reserved. +%% Copyright Ericsson AB 2006-2011. 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 +19,7 @@ -record(zip_file, { name :: string(), % file name - info :: #file_info{}, + info :: file:file_info(), comment :: string(), % zip file comment offset :: non_neg_integer(), % offset of file's local header in archive comp_size :: non_neg_integer() % compressed size diff --git a/lib/stdlib/src/Makefile b/lib/stdlib/src/Makefile index 237818c08b..600303d7e1 100644 --- a/lib/stdlib/src/Makefile +++ b/lib/stdlib/src/Makefile @@ -43,6 +43,7 @@ MODULES= \ array \ base64 \ beam_lib \ + binary \ c \ calendar \ dets \ diff --git a/lib/stdlib/src/array.erl b/lib/stdlib/src/array.erl index 83576c9fd3..2f69e2b0a4 100644 --- a/lib/stdlib/src/array.erl +++ b/lib/stdlib/src/array.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2009. All Rights Reserved. +%% Copyright Ericsson AB 2007-2011. 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 @@ -163,17 +163,17 @@ -type array_indx() :: non_neg_integer(). --type array_opt() :: 'fixed' | non_neg_integer() - | {'default', term()} | {'fixed', boolean()} - | {'size', non_neg_integer()}. +-type array_opt() :: {'fixed', boolean()} | 'fixed' + | {'default', Value :: term()} + | {'size', N :: non_neg_integer()} + | (N :: non_neg_integer()). -type array_opts() :: array_opt() | [array_opt()]. --type indx_pair() :: {array_indx(), term()}. +-type indx_pair() :: {Index :: array_indx(), Value :: term()}. -type indx_pairs() :: [indx_pair()]. %%-------------------------------------------------------------------------- -%% @spec () -> array() %% @doc Create a new, extendible array with initial size zero. %% @equiv new([]) %% @@ -185,7 +185,6 @@ new() -> new([]). -%% @spec (Options::term()) -> array() %% @doc Create a new array according to the given options. By default, %% the array is extendible and has initial size zero. Array indices %% start at 0. @@ -224,12 +223,11 @@ new() -> %% @see from_list/2 %% @see fix/1 --spec new(array_opts()) -> array(). +-spec new(Options :: array_opts()) -> array(). new(Options) -> new_0(Options, 0, false). -%% @spec (Size::integer(), Options::term()) -> array() %% @doc Create a new array according to the given size and options. If %% `Size' is not a nonnegative integer, the call fails with reason %% `badarg'. By default, the array has fixed size. Note that any size @@ -245,7 +243,7 @@ new(Options) -> %% %% @see new/1 --spec new(non_neg_integer(), array_opts()) -> array(). +-spec new(Size :: non_neg_integer(), Options :: array_opts()) -> array(). new(Size, Options) when is_integer(Size), Size >= 0 -> new_0(Options, Size, true); @@ -293,13 +291,12 @@ find_max(_I, M) -> M. -%% @spec (X::term()) -> boolean() %% @doc Returns `true' if `X' appears to be an array, otherwise `false'. %% Note that the check is only shallow; there is no guarantee that `X' %% is a well-formed array representation even if this function returns %% `true'. --spec is_array(term()) -> boolean(). +-spec is_array(X :: term()) -> boolean(). is_array(#array{size = Size, max = Max}) when is_integer(Size), is_integer(Max) -> @@ -308,25 +305,23 @@ is_array(_) -> false. -%% @spec (array()) -> integer() %% @doc Get the number of entries in the array. Entries are numbered %% from 0 to `size(Array)-1'; hence, this is also the index of the first %% entry that is guaranteed to not have been previously set. %% @see set/3 %% @see sparse_size/1 --spec size(array()) -> non_neg_integer(). +-spec size(Array :: array()) -> non_neg_integer(). size(#array{size = N}) -> N; size(_) -> erlang:error(badarg). -%% @spec (array()) -> term() %% @doc Get the value used for uninitialized entries. %% %% @see new/2 --spec default(array()) -> term(). +-spec default(Array :: array()) -> term(). default(#array{default = D}) -> D; default(_) -> erlang:error(badarg). @@ -405,23 +400,21 @@ new_test_() -> -endif. -%% @spec (array()) -> array() %% @doc Fix the size of the array. This prevents it from growing %% automatically upon insertion; see also {@link set/3}. %% @see relax/1 --spec fix(array()) -> array(). +-spec fix(Array :: array()) -> array(). fix(#array{}=A) -> A#array{max = 0}. -%% @spec (array()) -> boolean() %% @doc Check if the array has fixed size. %% Returns `true' if the array is fixed, otherwise `false'. %% @see fix/1 --spec is_fix(array()) -> boolean(). +-spec is_fix(Array :: array()) -> boolean(). is_fix(#array{max = 0}) -> true; is_fix(#array{}) -> false. @@ -455,12 +448,11 @@ fix_test_() -> -endif. -%% @spec (array()) -> array() %% @doc Make the array resizable. (Reverses the effects of {@link %% fix/1}.) %% @see fix/1 --spec relax(array()) -> array(). +-spec relax(Array :: array()) -> array(). relax(#array{size = N}=A) -> A#array{max = find_max(N-1, ?LEAFSIZE)}. @@ -481,12 +473,11 @@ relax_test_() -> -endif. -%% @spec (integer(), array()) -> array() %% @doc Change the size of the array. If `Size' is not a nonnegative %% integer, the call fails with reason `badarg'. If the given array has %% fixed size, the resulting array will also have fixed size. --spec resize(non_neg_integer(), array()) -> array(). +-spec resize(Size :: non_neg_integer(), Array :: array()) -> array(). resize(Size, #array{size = N, max = M, elements = E}=A) when is_integer(Size), Size >= 0 -> @@ -510,8 +501,6 @@ resize(_Size, _) -> erlang:error(badarg). -%% @spec (array()) -> array() - %% @doc Change the size of the array to that reported by {@link %% sparse_size/1}. If the given array has fixed size, the resulting %% array will also have fixed size. @@ -519,7 +508,7 @@ resize(_Size, _) -> %% @see resize/2 %% @see sparse_size/1 --spec resize(array()) -> array(). +-spec resize(Array :: array()) -> array(). resize(Array) -> resize(sparse_size(Array), Array). @@ -559,7 +548,6 @@ resize_test_() -> -endif. -%% @spec (integer(), term(), array()) -> array() %% @doc Set entry `I' of the array to `Value'. If `I' is not a %% nonnegative integer, or if the array has fixed size and `I' is larger %% than the maximum index, the call fails with reason `badarg'. @@ -570,7 +558,7 @@ resize_test_() -> %% @see get/2 %% @see reset/2 --spec set(array_indx(), term(), array()) -> array(). +-spec set(I :: array_indx(), Value :: term(), Array :: array()) -> array(). set(I, Value, #array{size = N, max = M, default = D, elements = E}=A) when is_integer(I), I >= 0 -> @@ -624,7 +612,6 @@ expand(I, _S, X, D) -> setelement(I+1, ?NEW_LEAF(D), X). -%% @spec (integer(), array()) -> term() %% @doc Get the value of entry `I'. If `I' is not a nonnegative %% integer, or if the array has fixed size and `I' is larger than the %% maximum index, the call fails with reason `badarg'. @@ -634,7 +621,7 @@ expand(I, _S, X, D) -> %% @see set/3 --spec get(array_indx(), array()) -> term(). +-spec get(I :: array_indx(), Array :: array()) -> term(). get(I, #array{size = N, max = M, elements = E, default = D}) when is_integer(I), I >= 0 -> @@ -660,7 +647,6 @@ get_1(I, E, _D) -> element(I+1, E). -%% @spec (integer(), array()) -> array() %% @doc Reset entry `I' to the default value for the array. %% If the value of entry `I' is the default value the array will be %% returned unchanged. Reset will never change size of the array. @@ -675,7 +661,7 @@ get_1(I, E, _D) -> %% TODO: a reset_range function --spec reset(array_indx(), array()) -> array(). +-spec reset(I :: array_indx(), Array :: array()) -> array(). reset(I, #array{size = N, max = M, default = D, elements = E}=A) when is_integer(I), I >= 0 -> @@ -756,13 +742,12 @@ set_get_test_() -> -endif. -%% @spec (array()) -> list() %% @doc Converts the array to a list. %% %% @see from_list/2 %% @see sparse_to_list/1 --spec to_list(array()) -> list(). +-spec to_list(Array :: array()) -> list(). to_list(#array{size = 0}) -> []; @@ -831,12 +816,11 @@ to_list_test_() -> -endif. -%% @spec (array()) -> list() %% @doc Converts the array to a list, skipping default-valued entries. %% %% @see to_list/1 --spec sparse_to_list(array()) -> list(). +-spec sparse_to_list(Array :: array()) -> list(). sparse_to_list(#array{size = 0}) -> []; @@ -901,15 +885,13 @@ sparse_to_list_test_() -> -endif. -%% @spec (list()) -> array() %% @equiv from_list(List, undefined) --spec from_list(list()) -> array(). +-spec from_list(List :: list()) -> array(). from_list(List) -> from_list(List, undefined). -%% @spec (list(), term()) -> array() %% @doc Convert a list to an extendible array. `Default' is used as the value %% for uninitialized entries of the array. If `List' is not a proper list, %% the call fails with reason `badarg'. @@ -917,7 +899,7 @@ from_list(List) -> %% @see new/2 %% @see to_list/1 --spec from_list(list(), term()) -> array(). +-spec from_list(List :: list(), Default :: term()) -> array(). from_list([], Default) -> new({default,Default}); @@ -1011,13 +993,12 @@ from_list_test_() -> -endif. -%% @spec (array()) -> [{Index::integer(), Value::term()}] %% @doc Convert the array to an ordered list of pairs `{Index, Value}'. %% %% @see from_orddict/2 %% @see sparse_to_orddict/1 --spec to_orddict(array()) -> indx_pairs(). +-spec to_orddict(Array :: array()) -> indx_pairs(). to_orddict(#array{size = 0}) -> []; @@ -1104,13 +1085,12 @@ to_orddict_test_() -> -endif. -%% @spec (array()) -> [{Index::integer(), Value::term()}] %% @doc Convert the array to an ordered list of pairs `{Index, Value}', %% skipping default-valued entries. %% %% @see to_orddict/1 --spec sparse_to_orddict(array()) -> indx_pairs(). +-spec sparse_to_orddict(Array :: array()) -> indx_pairs(). sparse_to_orddict(#array{size = 0}) -> []; @@ -1188,15 +1168,13 @@ sparse_to_orddict_test_() -> -endif. -%% @spec (list()) -> array() %% @equiv from_orddict(Orddict, undefined) --spec from_orddict(indx_pairs()) -> array(). +-spec from_orddict(Orddict :: indx_pairs()) -> array(). from_orddict(Orddict) -> from_orddict(Orddict, undefined). -%% @spec (list(), term()) -> array() %% @doc Convert an ordered list of pairs `{Index, Value}' to a %% corresponding extendible array. `Default' is used as the value for %% uninitialized entries of the array. If `List' is not a proper, @@ -1206,7 +1184,7 @@ from_orddict(Orddict) -> %% @see new/2 %% @see to_orddict/1 --spec from_orddict(indx_pairs(), term()) -> array(). +-spec from_orddict(Orddict :: indx_pairs(), Default :: term()) -> array(). from_orddict([], Default) -> new({default,Default}); @@ -1392,7 +1370,6 @@ from_orddict_test_() -> -endif. -%% @spec (Function, array()) -> array() %% Function = (Index::integer(), Value::term()) -> term() %% @doc Map the given function onto each element of the array. The %% elements are visited in order from the lowest index to the highest. @@ -1402,7 +1379,8 @@ from_orddict_test_() -> %% @see foldr/3 %% @see sparse_map/2 --spec map(fun((array_indx(), _) -> _), array()) -> array(). +-spec map(Function, Array :: array()) -> array() when + Function :: fun((Index :: array_indx(), Value :: _) -> _). map(Function, Array=#array{size = N, elements = E, default = D}) when is_function(Function, 2) -> @@ -1485,7 +1463,6 @@ map_test_() -> -endif. -%% @spec (Function, array()) -> array() %% Function = (Index::integer(), Value::term()) -> term() %% @doc Map the given function onto each element of the array, skipping %% default-valued entries. The elements are visited in order from the @@ -1494,7 +1471,8 @@ map_test_() -> %% %% @see map/2 --spec sparse_map(fun((array_indx(), _) -> _), array()) -> array(). +-spec sparse_map(Function, Array :: array()) -> array() when + Function :: fun((Index :: array_indx(), Value :: _) -> _). sparse_map(Function, Array=#array{size = N, elements = E, default = D}) when is_function(Function, 2) -> @@ -1580,9 +1558,6 @@ sparse_map_test_() -> -endif. -%% @spec (Function, InitialAcc::term(), array()) -> term() -%% Function = (Index::integer(), Value::term(), Acc::term()) -> -%% term() %% @doc Fold the elements of the array using the given function and %% initial accumulator value. The elements are visited in order from the %% lowest index to the highest. If `Function' is not a function, the @@ -1592,7 +1567,8 @@ sparse_map_test_() -> %% @see map/2 %% @see sparse_foldl/3 --spec foldl(fun((array_indx(), _, A) -> B), A, array()) -> B. +-spec foldl(Function, InitialAcc :: A, Array :: array()) -> B when + Function :: fun((Index :: array_indx(), Value :: _, Acc :: A) -> B). foldl(Function, A, #array{size = N, elements = E, default = D}) when is_function(Function, 3) -> @@ -1656,9 +1632,6 @@ foldl_test_() -> -endif. -%% @spec (Function, InitialAcc::term(), array()) -> term() -%% Function = (Index::integer(), Value::term(), Acc::term()) -> -%% term() %% @doc Fold the elements of the array using the given function and %% initial accumulator value, skipping default-valued entries. The %% elements are visited in order from the lowest index to the highest. @@ -1667,7 +1640,8 @@ foldl_test_() -> %% @see foldl/3 %% @see sparse_foldr/3 --spec sparse_foldl(fun((array_indx(), _, A) -> B), A, array()) -> B. +-spec sparse_foldl(Function, InitialAcc :: A, Array :: array()) -> B when + Function :: fun((Index :: array_indx(), Value :: _, Acc :: A) -> B). sparse_foldl(Function, A, #array{size = N, elements = E, default = D}) when is_function(Function, 3) -> @@ -1735,9 +1709,6 @@ sparse_foldl_test_() -> -endif. -%% @spec (Function, InitialAcc::term(), array()) -> term() -%% Function = (Index::integer(), Value::term(), Acc::term()) -> -%% term() %% @doc Fold the elements of the array right-to-left using the given %% function and initial accumulator value. The elements are visited in %% order from the highest index to the lowest. If `Function' is not a @@ -1746,7 +1717,8 @@ sparse_foldl_test_() -> %% @see foldl/3 %% @see map/2 --spec foldr(fun((array_indx(), _, A) -> B), A, array()) -> B. +-spec foldr(Function, InitialAcc :: A, Array :: array()) -> B when + Function :: fun((Index :: array_indx(), Value :: _, Acc :: A) -> B). foldr(Function, A, #array{size = N, elements = E, default = D}) when is_function(Function, 3) -> @@ -1815,9 +1787,6 @@ foldr_test_() -> -endif. -%% @spec (Function, InitialAcc::term(), array()) -> term() -%% Function = (Index::integer(), Value::term(), Acc::term()) -> -%% term() %% @doc Fold the elements of the array right-to-left using the given %% function and initial accumulator value, skipping default-valued %% entries. The elements are visited in order from the highest index to @@ -1827,7 +1796,8 @@ foldr_test_() -> %% @see foldr/3 %% @see sparse_foldl/3 --spec sparse_foldr(fun((array_indx(), _, A) -> B), A, array()) -> B. +-spec sparse_foldr(Function, InitialAcc :: A, Array :: array()) -> B when + Function :: fun((Index :: array_indx(), Value :: _, Acc :: A) -> B). sparse_foldr(Function, A, #array{size = N, elements = E, default = D}) when is_function(Function, 3) -> @@ -1870,7 +1840,6 @@ sparse_foldr_3(I, T, Ix, A, F, D) -> end. -%% @spec (array()) -> integer() %% @doc Get the number of entries in the array up until the last %% non-default valued entry. In other words, returns `I+1' if `I' is the %% last non-default valued entry in the array, or zero if no such entry @@ -1878,7 +1847,7 @@ sparse_foldr_3(I, T, Ix, A, F, D) -> %% @see size/1 %% @see resize/1 --spec sparse_size(array()) -> non_neg_integer(). +-spec sparse_size(Array :: array()) -> non_neg_integer(). sparse_size(A) -> F = fun (I, _V, _A) -> throw({value, I}) end, diff --git a/lib/stdlib/src/base64.erl b/lib/stdlib/src/base64.erl index ebef998ee1..5d800e87b8 100644 --- a/lib/stdlib/src/base64.erl +++ b/lib/stdlib/src/base64.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2009. All Rights Reserved. +%% Copyright Ericsson AB 2007-2011. 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,9 @@ %% Description: Encodes a plain ASCII string (or binary) into base64. %%------------------------------------------------------------------------- --spec encode_to_string(string() | binary()) -> ascii_string(). +-spec encode_to_string(Data) -> Base64String when + Data :: string() | binary(), + Base64String :: ascii_string(). encode_to_string(Bin) when is_binary(Bin) -> encode_to_string(binary_to_list(Bin)); @@ -53,7 +55,9 @@ encode_to_string(List) when is_list(List) -> %% Description: Encodes a plain ASCII string (or binary) into base64. %%------------------------------------------------------------------------- --spec encode(string() | binary()) -> binary(). +-spec encode(Data) -> Base64 when + Data :: string() | binary(), + Base64 :: binary(). encode(Bin) when is_binary(Bin) -> encode_binary(Bin); @@ -102,19 +106,23 @@ encode_binary(Bin) -> %% whereas decode crashes if an illegal character is found %%------------------------------------------------------------------------- --spec decode(string() | binary()) -> binary(). +-spec decode(Base64) -> Data when + Base64 :: string() | binary(), + Data :: binary(). decode(Bin) when is_binary(Bin) -> decode_binary(<<>>, Bin); decode(List) when is_list(List) -> list_to_binary(decode_l(List)). --spec mime_decode(string() | binary()) -> binary(). +-spec mime_decode(Base64) -> Data when + Base64 :: string() | binary(), + Data :: binary(). mime_decode(Bin) when is_binary(Bin) -> mime_decode_binary(<<>>, Bin); mime_decode(List) when is_list(List) -> - list_to_binary(mime_decode_l(List)). + mime_decode(list_to_binary(List)). -spec decode_l(string()) -> string(). @@ -125,7 +133,7 @@ decode_l(List) -> -spec mime_decode_l(string()) -> string(). mime_decode_l(List) -> - L = strip_illegal(List, []), + L = strip_illegal(List, [], 0), decode(L, []). %%------------------------------------------------------------------------- @@ -139,14 +147,18 @@ mime_decode_l(List) -> %% whereas decode crashes if an illegal character is found %%------------------------------------------------------------------------- --spec decode_to_string(string() | binary()) -> string(). +-spec decode_to_string(Base64) -> DataString when + Base64 :: string() | binary(), + DataString :: string(). decode_to_string(Bin) when is_binary(Bin) -> decode_to_string(binary_to_list(Bin)); decode_to_string(List) when is_list(List) -> decode_l(List). --spec mime_decode_to_string(string() | binary()) -> string(). +-spec mime_decode_to_string(Base64) -> DataString when + Base64 :: string() | binary(), + DataString :: string(). mime_decode_to_string(Bin) when is_binary(Bin) -> mime_decode_to_string(binary_to_list(Bin)); @@ -198,6 +210,9 @@ decode_binary(Result, <<>>) -> true = is_binary(Result), Result. +%% Skipping pad character if not at end of string. Also liberal about +%% excess padding and skipping of other illegal (non-base64 alphabet) +%% characters. See section 3.3 of RFC4648 mime_decode_binary(Result, <<0:8,T/bits>>) -> mime_decode_binary(Result, T); mime_decode_binary(Result0, <<C:8,T/bits>>) -> @@ -205,15 +220,27 @@ mime_decode_binary(Result0, <<C:8,T/bits>>) -> Bits when is_integer(Bits) -> mime_decode_binary(<<Result0/bits,Bits:6>>, T); eq -> - case tail_contains_equal(T) of - true -> - Split = byte_size(Result0) - 1, - <<Result:Split/bytes,_:4>> = Result0, - Result; - false -> - Split = byte_size(Result0) - 1, - <<Result:Split/bytes,_:2>> = Result0, - Result + case tail_contains_more(T, false) of + {<<>>, Eq} -> + %% No more valid data. + case bit_size(Result0) rem 8 of + 0 -> + %% '====' is not uncommon. + Result0; + 4 when Eq -> + %% enforce at least one more '=' only ignoring illegals and spacing + Split = byte_size(Result0) - 1, + <<Result:Split/bytes,_:4>> = Result0, + Result; + 2 -> + %% remove 2 bits + Split = byte_size(Result0) - 1, + <<Result:Split/bytes,_:2>> = Result0, + Result + end; + {More, _} -> + %% More valid data, skip the eq as invalid + mime_decode_binary(Result0, More) end; _ -> mime_decode_binary(Result0, T) @@ -262,31 +289,63 @@ strip_ws(<<$\s,T/binary>>) -> strip_ws(T); strip_ws(T) -> T. -strip_illegal([0|Cs], A) -> - strip_illegal(Cs, A); -strip_illegal([C|Cs], A) -> +%% Skipping pad character if not at end of string. Also liberal about +%% excess padding and skipping of other illegal (non-base64 alphabet) +%% characters. See section 3.3 of RFC4648 +strip_illegal([], A, _Cnt) -> + A; +strip_illegal([0|Cs], A, Cnt) -> + strip_illegal(Cs, A, Cnt); +strip_illegal([C|Cs], A, Cnt) -> case element(C, ?DECODE_MAP) of - bad -> strip_illegal(Cs, A); - ws -> strip_illegal(Cs, A); - eq -> strip_illegal_end(Cs, [$=|A]); - _ -> strip_illegal(Cs, [C|A]) - end; -strip_illegal([], A) -> A. + bad -> + strip_illegal(Cs, A, Cnt); + ws -> + strip_illegal(Cs, A, Cnt); + eq -> + case {tail_contains_more(Cs, false), Cnt rem 4} of + {{[], _}, 0} -> + A; %% Ignore extra = + {{[], true}, 2} -> + [$=|[$=|A]]; %% 'XX==' + {{[], _}, 3} -> + [$=|A]; %% 'XXX=' + {{[H|T], _}, _} -> + %% more data, skip equals + strip_illegal(T, [H|A], Cnt+1) + end; + _ -> + strip_illegal(Cs, [C|A], Cnt+1) + end. -strip_illegal_end([0|Cs], A) -> - strip_illegal_end(Cs, A); -strip_illegal_end([C|Cs], A) -> +%% Search the tail for more valid data and remember if we saw +%% another equals along the way. +tail_contains_more([], Eq) -> + {[], Eq}; +tail_contains_more(<<>>, Eq) -> + {<<>>, Eq}; +tail_contains_more([C|T]=More, Eq) -> case element(C, ?DECODE_MAP) of - bad -> strip_illegal(Cs, A); - ws -> strip_illegal(Cs, A); - eq -> [C|A]; - _ -> strip_illegal(Cs, [C|A]) + bad -> + tail_contains_more(T, Eq); + ws -> + tail_contains_more(T, Eq); + eq -> + tail_contains_more(T, true); + _ -> + {More, Eq} end; -strip_illegal_end([], A) -> A. - -tail_contains_equal(<<$=,_/binary>>) -> true; -tail_contains_equal(<<_,T/binary>>) -> tail_contains_equal(T); -tail_contains_equal(<<>>) -> false. +tail_contains_more(<<C:8,T/bits>> =More, Eq) -> + case element(C, ?DECODE_MAP) of + bad -> + tail_contains_more(T, Eq); + ws -> + tail_contains_more(T, Eq); + eq -> + tail_contains_more(T, true); + _ -> + {More, Eq} + end. %% accessors b64e(X) -> diff --git a/lib/stdlib/src/beam_lib.erl b/lib/stdlib/src/beam_lib.erl index 820afd3739..d9c645d787 100644 --- a/lib/stdlib/src/beam_lib.erl +++ b/lib/stdlib/src/beam_lib.erl @@ -1,24 +1,28 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2000-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2000-2011. 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(beam_lib). -behaviour(gen_server). +%% Avoid warning for local function error/1 clashing with autoimported BIF. +-compile({no_auto_import,[error/1]}). +%% Avoid warning for local function error/2 clashing with autoimported BIF. +-compile({no_auto_import,[error/2]}). -export([info/1, cmp/2, cmp_dirs/2, @@ -41,23 +45,18 @@ terminate/2,code_change/3]). -export([make_crypto_key/2, get_crypto_key/1]). %Utilities used by compiler +-export_type([attrib_entry/0, compinfo_entry/0, labeled_entry/0]). + -import(lists, [append/1, delete/2, foreach/2, keysort/2, member/2, reverse/1, sort/1, splitwith/2]). --include_lib("kernel/include/file.hrl"). --include("erl_compile.hrl"). - %%------------------------------------------------------------------------- -type beam() :: module() | file:filename() | binary(). -%% XXX: THE FOLLOWING SHOULD BE IMPORTED FROM SOMEWHERE ELSE --type forms() :: term(). +-type forms() :: [erl_parse:abstract_form()]. --type abst_vsn() :: atom(). --type abst_code() :: {abst_vsn(), forms()} | 'no_abstract_code'. --type attribute() :: atom(). --type attrvalue() :: term(). +-type abst_code() :: {AbstVersion :: atom(), forms()} | 'no_abstract_code'. -type dataB() :: binary(). -type index() :: non_neg_integer(). -type label() :: integer(). @@ -71,9 +70,9 @@ | 'atoms'. -type chunkref() :: chunkname() | chunkid(). --type attrib_entry() :: {attribute(), [attrvalue()]}. --type compinfo_entry() :: {atom(), term()}. --type labeled_entry() :: {atom(), arity(), label()}. +-type attrib_entry() :: {Attribute :: atom(), [AttributeValue :: term()]}. +-type compinfo_entry() :: {InfoKey :: atom(), term()}. +-type labeled_entry() :: {Function :: atom(), arity(), label()}. -type chunkdata() :: {chunkid(), dataB()} | {'abstract_code', abst_code()} @@ -82,20 +81,17 @@ | {'exports', [{atom(), arity()}]} | {'labeled_exports', [labeled_entry()]} | {'imports', [mfa()]} - | {'indexed_imports', [{index(), module(), atom(), arity()}]} + | {'indexed_imports', [{index(), module(), Function :: atom(), arity()}]} | {'locals', [{atom(), arity()}]} | {'labeled_locals', [labeled_entry()]} | {'atoms', [{integer(), atom()}]}. --type info_pair() :: {'file', file:filename()} - | {'binary', binary()} - | {'module', module()} - | {'chunks', [{chunkid(), integer(), integer()}]}. - %% Error reasons -type info_rsn() :: {'chunk_too_big', file:filename(), - chunkid(), integer(), integer()} - | {'invalid_beam_file', file:filename(), integer()} + chunkid(), ChunkSize :: non_neg_integer(), + FileSize :: non_neg_integer()} + | {'invalid_beam_file', file:filename(), + Position :: non_neg_integer()} | {'invalid_chunk', file:filename(), chunkid()} | {'missing_chunk', file:filename(), chunkid()} | {'not_a_beam_file', file:filename()} @@ -106,6 +102,7 @@ | info_rsn(). -type cmp_rsn() :: {'modules_different', module(), module()} | {'chunks_different', chunkid()} + | 'different_chunks' | info_rsn(). %%------------------------------------------------------------------------- @@ -114,20 +111,34 @@ %% Exported functions %% --spec info(beam()) -> [info_pair()] | {'error', 'beam_lib', info_rsn()}. +-spec info(Beam) -> [InfoPair] | {'error', 'beam_lib', info_rsn()} when + Beam :: beam(), + InfoPair :: {'file', Filename :: file:filename()} + | {'binary', Binary :: binary()} + | {'module', Module :: module()} + | {'chunks', [{ChunkId :: chunkid(), + Pos :: non_neg_integer(), + Size :: non_neg_integer()}]}. info(File) -> read_info(beam_filename(File)). --spec chunks(beam(), [chunkref()]) -> - {'ok', {module(), [chunkdata()]}} | {'error', 'beam_lib', chnk_rsn()}. +-spec chunks(Beam, ChunkRefs) -> + {'ok', {module(), [chunkdata()]}} | + {'error', 'beam_lib', chnk_rsn()} when + Beam :: beam(), + ChunkRefs :: [chunkref()]. chunks(File, Chunks) -> read_chunk_data(File, Chunks). --spec chunks(beam(), [chunkref()], ['allow_missing_chunks']) -> - {'ok', {module(), [{chunkref(), chunkdata() | 'missing_chunk'}]}} - | {'error', 'beam_lib', chnk_rsn()}. +-spec chunks(Beam, ChunkRefs, Options) -> + {'ok', {module(), [ChunkResult]}} | + {'error', 'beam_lib', chnk_rsn()} when + Beam :: beam(), + ChunkRefs :: [chunkref()], + Options :: ['allow_missing_chunks'], + ChunkResult :: chunkdata() | {ChunkRef :: chunkref(), 'missing_chunk'}. chunks(File, Chunks, Options) -> try read_chunk_data(File, Chunks, Options) @@ -138,49 +149,65 @@ chunks(File, Chunks, Options) -> all_chunks(File) -> read_all_chunks(File). --spec cmp(beam(), beam()) -> 'ok' | {'error', 'beam_lib', cmp_rsn()}. +-spec cmp(Beam1, Beam2) -> 'ok' | {'error', 'beam_lib', cmp_rsn()} when + Beam1 :: beam(), + Beam2 :: beam(). cmp(File1, File2) -> try cmp_files(File1, File2) catch Error -> Error end. --spec cmp_dirs(atom() | file:filename(), atom() | file:filename()) -> - {[file:filename()], [file:filename()], - [{file:filename(), file:filename()}]} - | {'error', 'beam_lib', {'not_a_directory', term()} | info_rsn()}. +-spec cmp_dirs(Dir1, Dir2) -> + {Only1, Only2, Different} | {'error', 'beam_lib', Reason} when + Dir1 :: atom() | file:filename(), + Dir2 :: atom() | file:filename(), + Only1 :: [file:filename()], + Only2 :: [file:filename()], + Different :: [{Filename1 :: file:filename(), Filename2 :: file:filename()}], + Reason :: {'not_a_directory', term()} | info_rsn(). cmp_dirs(Dir1, Dir2) -> catch compare_dirs(Dir1, Dir2). --spec diff_dirs(atom() | file:filename(), atom() | file:filename()) -> - 'ok' | {'error', 'beam_lib', {'not_a_directory', term()} | info_rsn()}. +-spec diff_dirs(Dir1, Dir2) -> 'ok' | {'error', 'beam_lib', Reason} when + Dir1 :: atom() | file:filename(), + Dir2 :: atom() | file:filename(), + Reason :: {'not_a_directory', term()} | info_rsn(). diff_dirs(Dir1, Dir2) -> catch diff_directories(Dir1, Dir2). --spec strip(beam()) -> - {'ok', {module(), beam()}} | {'error', 'beam_lib', info_rsn()}. +-spec strip(Beam1) -> + {'ok', {module(), Beam2}} | {'error', 'beam_lib', info_rsn()} when + Beam1 :: beam(), + Beam2 :: beam(). strip(FileName) -> try strip_file(FileName) catch Error -> Error end. --spec strip_files([beam()]) -> - {'ok', [{module(), beam()}]} | {'error', 'beam_lib', info_rsn()}. +-spec strip_files(Files) -> + {'ok', [{module(), Beam}]} | {'error', 'beam_lib', info_rsn()} when + Files :: [beam()], + Beam :: beam(). strip_files(Files) when is_list(Files) -> try strip_fils(Files) catch Error -> Error end. --spec strip_release(atom() | file:filename()) -> +-spec strip_release(Dir) -> {'ok', [{module(), file:filename()}]} - | {'error', 'beam_lib', {'not_a_directory', term()} | info_rsn()}. + | {'error', 'beam_lib', Reason} when + Dir :: atom() | file:filename(), + Reason :: {'not_a_directory', term()} | info_rsn(). strip_release(Root) -> catch strip_rel(Root). --spec version(beam()) -> - {'ok', {module(), [term()]}} | {'error', 'beam_lib', chnk_rsn()}. +-spec version(Beam) -> + {'ok', {module(), [Version :: term()]}} | + {'error', 'beam_lib', chnk_rsn()} when + Beam :: beam(). version(File) -> case catch read_chunk_data(File, [attributes]) of @@ -191,8 +218,10 @@ version(File) -> Error end. --spec md5(beam()) -> - {'ok', {module(), binary()}} | {'error', 'beam_lib', chnk_rsn()}. +-spec md5(Beam) -> + {'ok', {module(), MD5}} | {'error', 'beam_lib', chnk_rsn()} when + Beam :: beam(), + MD5 :: binary(). md5(File) -> case catch read_significant_chunks(File) of @@ -203,7 +232,8 @@ md5(File) -> Error end. --spec format_error(term()) -> [char() | string()]. +-spec format_error(Reason) -> io_lib:chars() when + Reason :: term(). format_error({error, Error}) -> format_error(Error); @@ -256,12 +286,15 @@ format_error(E) -> | {'debug_info', mode(), module(), file:filename()}. -type crypto_fun() :: fun((crypto_fun_arg()) -> term()). --spec crypto_key_fun(crypto_fun()) -> 'ok' | {'error', term()}. +-spec crypto_key_fun(CryptoKeyFun) -> 'ok' | {'error', Reason} when + CryptoKeyFun :: crypto_fun(), + Reason :: badfun | exists | term(). crypto_key_fun(F) -> call_crypto_server({crypto_key_fun, F}). --spec clear_crypto_key_fun() -> 'undefined' | {'ok', term()}. +-spec clear_crypto_key_fun() -> 'undefined' | {'ok', Result} when + Result :: 'undefined' | term(). clear_crypto_key_fun() -> call_crypto_server(clear_crypto_key_fun). @@ -331,13 +364,11 @@ beam_files(Dir) -> %% -> ok | throw(Error) cmp_files(File1, File2) -> - {ok, {M1, L1}} = read_significant_chunks(File1), - {ok, {M2, L2}} = read_significant_chunks(File2), + {ok, {M1, L1}} = read_all_but_useless_chunks(File1), + {ok, {M2, L2}} = read_all_but_useless_chunks(File2), if M1 =:= M2 -> - List1 = filter_funtab(L1), - List2 = filter_funtab(L2), - cmp_lists(List1, List2); + cmp_lists(L1, L2); true -> error({modules_different, M1, M2}) end. @@ -408,6 +439,20 @@ pad(Size) -> end. %% -> {ok, {Module, Chunks}} | throw(Error) +read_all_but_useless_chunks(File0) when is_atom(File0); + is_list(File0); + is_binary(File0) -> + File = beam_filename(File0), + {ok, Module, ChunkIds0} = scan_beam(File, info), + ChunkIds = [Name || {Name,_,_} <- ChunkIds0, + not is_useless_chunk(Name)], + {ok, Module, Chunks} = scan_beam(File, ChunkIds), + {ok, {Module, lists:reverse(Chunks)}}. + +is_useless_chunk("CInf") -> true; +is_useless_chunk(_) -> false. + +%% -> {ok, {Module, Chunks}} | throw(Error) read_significant_chunks(File) -> case read_chunk_data(File, significant_chunks(), [allow_missing_chunks]) of {ok, {Module, Chunks0}} -> diff --git a/lib/stdlib/src/binary.erl b/lib/stdlib/src/binary.erl new file mode 100644 index 0000000000..cb1e12ae46 --- /dev/null +++ b/lib/stdlib/src/binary.erl @@ -0,0 +1,212 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2010-2011. 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(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(). +-type part() :: {Start :: non_neg_integer(), Length :: integer()}. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% split +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-spec split(Subject, Pattern) -> Parts when + Subject :: binary(), + Pattern :: binary() | [binary()] | cp(), + Parts :: [binary()]. + +split(H,N) -> + split(H,N,[]). + +-spec split(Subject, Pattern, Options) -> Parts when + Subject :: binary(), + Pattern :: binary() | [binary()] | cp(), + Options :: [Option], + Option :: {scope, part()} | trim | global, + Parts :: [binary()]. + +split(Haystack,Needles,Options) -> + try + {Part,Global,Trim} = get_opts_split(Options,{no,false,false}), + Moptlist = case Part of + no -> + []; + {A,B} -> + [{scope,{A,B}}] + end, + MList = if + Global -> + binary:matches(Haystack,Needles,Moptlist); + true -> + case binary:match(Haystack,Needles,Moptlist) of + nomatch -> []; + Match -> [Match] + end + end, + do_split(Haystack,MList,0,Trim) + catch + _:_ -> + erlang:error(badarg) + end. + +do_split(H,[],N,true) when N >= byte_size(H) -> + []; +do_split(H,[],N,_) -> + [binary:part(H,{N,byte_size(H)-N})]; +do_split(H,[{A,B}|T],N,Trim) -> + case binary:part(H,{N,A-N}) of + <<>> -> + Rest = do_split(H,T,A+B,Trim), + case {Trim, Rest} of + {true,[]} -> + []; + _ -> + [<<>> | Rest] + end; + Oth -> + [Oth | do_split(H,T,A+B,Trim)] + end. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% replace +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-spec replace(Subject, Pattern, Replacement) -> Result when + Subject :: binary(), + Pattern :: binary() | [ binary() ] | cp(), + Replacement :: binary(), + Result :: binary(). + +replace(H,N,R) -> + replace(H,N,R,[]). + +-spec replace(Subject, Pattern, Replacement, Options) -> Result when + Subject :: binary(), + Pattern :: binary() | [ binary() ] | cp(), + Replacement :: binary(), + Options :: [Option], + Option :: global | {scope, part()} | {insert_replaced, InsPos}, + InsPos :: OnePos | [ OnePos ], + OnePos :: non_neg_integer(), + Result :: binary(). + +replace(Haystack,Needles,Replacement,Options) -> + try + true = is_binary(Replacement), % Make badarg instead of function clause + {Part,Global,Insert} = get_opts_replace(Options,{no,false,[]}), + Moptlist = case Part of + no -> + []; + {A,B} -> + [{scope,{A,B}}] + end, + MList = if + Global -> + binary:matches(Haystack,Needles,Moptlist); + true -> + case binary:match(Haystack,Needles,Moptlist) of + nomatch -> []; + Match -> [Match] + end + end, + ReplList = case Insert of + [] -> + Replacement; + Y when is_integer(Y) -> + splitat(Replacement,0,[Y]); + Li when is_list(Li) -> + splitat(Replacement,0,lists:sort(Li)) + end, + erlang:iolist_to_binary(do_replace(Haystack,MList,ReplList,0)) + catch + _:_ -> + erlang:error(badarg) + end. + + +do_replace(H,[],_,N) -> + [binary:part(H,{N,byte_size(H)-N})]; +do_replace(H,[{A,B}|T],Replacement,N) -> + [binary:part(H,{N,A-N}), + if + is_list(Replacement) -> + do_insert(Replacement, binary:part(H,{A,B})); + true -> + Replacement + end + | do_replace(H,T,Replacement,A+B)]. + +do_insert([X],_) -> + [X]; +do_insert([H|T],R) -> + [H,R|do_insert(T,R)]. + +splitat(H,N,[]) -> + [binary:part(H,{N,byte_size(H)-N})]; +splitat(H,N,[I|T]) -> + [binary:part(H,{N,I-N})|splitat(H,I,T)]. + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Simple helper functions +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +get_opts_split([],{Part,Global,Trim}) -> + {Part,Global,Trim}; +get_opts_split([{scope,{A,B}} | T],{_Part,Global,Trim}) -> + get_opts_split(T,{{A,B},Global,Trim}); +get_opts_split([global | T],{Part,_Global,Trim}) -> + get_opts_split(T,{Part,true,Trim}); +get_opts_split([trim | T],{Part,Global,_Trim}) -> + get_opts_split(T,{Part,Global,true}); +get_opts_split(_,_) -> + throw(badopt). + +get_opts_replace([],{Part,Global,Insert}) -> + {Part,Global,Insert}; +get_opts_replace([{scope,{A,B}} | T],{_Part,Global,Insert}) -> + get_opts_replace(T,{{A,B},Global,Insert}); +get_opts_replace([global | T],{Part,_Global,Insert}) -> + get_opts_replace(T,{Part,true,Insert}); +get_opts_replace([{insert_replaced,N} | T],{Part,Global,_Insert}) -> + get_opts_replace(T,{Part,Global,N}); +get_opts_replace(_,_) -> + throw(badopt). + diff --git a/lib/stdlib/src/c.erl b/lib/stdlib/src/c.erl index 433833e233..febfdd6285 100644 --- a/lib/stdlib/src/c.erl +++ b/lib/stdlib/src/c.erl @@ -1,25 +1,27 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1996-2011. 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(c). %% Utilities to use from shell. +%% Avoid warning for local function error/2 clashing with autoimported BIF. +-compile({no_auto_import,[error/2]}). -export([help/0,lc/1,c/1,c/2,nc/1,nc/2, nl/1,l/1,i/0,i/1,ni/0, y/1, y/2, lc_batch/0, lc_batch/1, @@ -31,42 +33,55 @@ -export([display_info/1]). -export([appcall/4]). --import(lists, [reverse/1,flatten/1,sublist/3,sort/1,keysearch/3,keysort/2, +-import(lists, [reverse/1,flatten/1,sublist/3,sort/1,keysort/2, concat/1,max/1,min/1,foreach/2,foldl/3,flatmap/2]). -import(io, [format/1, format/2]). +%%----------------------------------------------------------------------- + +-spec help() -> 'ok'. + help() -> - format("bt(Pid) -- stack backtrace for a process\n" - "c(File) -- compile and load code in <File>\n" - "cd(Dir) -- change working directory\n" - "flush() -- flush any messages sent to the shell\n" - "help() -- help info\n" - "i() -- information about the system\n" - "ni() -- information about the networked system\n" - "i(X,Y,Z) -- information about pid <X,Y,Z>\n" - "l(Module) -- load or reload module\n" - "lc([File]) -- compile a list of Erlang modules\n" - "ls() -- list files in the current directory\n" - "ls(Dir) -- list files in directory <Dir>\n" - "m() -- which modules are loaded\n" - "m(Mod) -- information about module <Mod>\n" - "memory() -- memory allocation information\n" - "memory(T) -- memory allocation information of type <T>\n" - "nc(File) -- compile and load code in <File> on all nodes\n" - "nl(Module) -- load module on all nodes\n" - "pid(X,Y,Z) -- convert X,Y,Z to a Pid\n" - "pwd() -- print working directory\n" - "q() -- quit - shorthand for init:stop()\n" - "regs() -- information about registered processes\n" - "nregs() -- information about all registered processes\n" - "xm(M) -- cross reference check a module\n" - "y(File) -- generate a Yecc parser\n"). + io:put_chars(<<"bt(Pid) -- stack backtrace for a process\n" + "c(File) -- compile and load code in <File>\n" + "cd(Dir) -- change working directory\n" + "flush() -- flush any messages sent to the shell\n" + "help() -- help info\n" + "i() -- information about the system\n" + "ni() -- information about the networked system\n" + "i(X,Y,Z) -- information about pid <X,Y,Z>\n" + "l(Module) -- load or reload module\n" + "lc([File]) -- compile a list of Erlang modules\n" + "ls() -- list files in the current directory\n" + "ls(Dir) -- list files in directory <Dir>\n" + "m() -- which modules are loaded\n" + "m(Mod) -- information about module <Mod>\n" + "memory() -- memory allocation information\n" + "memory(T) -- memory allocation information of type <T>\n" + "nc(File) -- compile and load code in <File> on all nodes\n" + "nl(Module) -- load module on all nodes\n" + "pid(X,Y,Z) -- convert X,Y,Z to a Pid\n" + "pwd() -- print working directory\n" + "q() -- quit - shorthand for init:stop()\n" + "regs() -- information about registered processes\n" + "nregs() -- information about all registered processes\n" + "xm(M) -- cross reference check a module\n" + "y(File) -- generate a Yecc parser\n">>). %% c(FileName) %% Compile a file/module. +-spec c(File) -> {'ok', Module} | 'error' when + File :: file:name(), + Module :: module(). + c(File) -> c(File, []). +-spec c(File, Options) -> {'ok', Module} | 'error' when + File :: file:name(), + Options :: [compile:option()], + Module :: module(). + c(File, Opts0) when is_list(Opts0) -> Opts = [report_errors,report_warnings|Opts0], case compile:file(File, Opts) of @@ -82,6 +97,8 @@ c(File, Opt) -> %%% Obtain the 'outdir' option from the argument. Return "." if no %%% such option was given. +-spec outdir([compile:option()]) -> file:filename(). + outdir([]) -> "."; outdir([Opt|Rest]) -> @@ -118,8 +135,8 @@ machine_load(Mod, File, Opts) -> %%% loaded from some other place than current directory. %%% Now, loading from other than current directory is supposed to work. %%% so this function does nothing special. -check_load({error, R}, _) -> {error, R}; -check_load(_, X) -> {ok, X}. +check_load({error, _R} = Error, _) -> Error; +check_load(_, Mod) -> {ok, Mod}. %% Compile a list of modules %% enables the nice unix shell cmd @@ -128,6 +145,9 @@ check_load(_, X) -> {ok, X}. %% with constant c2 defined, c1=v1 (v1 must be a term!), include dir %% IDir, outdir ODir. +-spec lc(Files) -> 'ok' | 'error' when + Files :: [File :: erl_compile:cmd_line_arg()]. + lc(Args) -> case catch split(Args, [], []) of error -> error; @@ -145,7 +165,7 @@ lc_batch() -> io:format("Error: no files to compile~n"), halt(1). --spec lc_batch([_]) -> no_return(). +-spec lc_batch([erl_compile:cmd_line_arg()]) -> no_return(). lc_batch(Args) -> try split(Args, [], []) of @@ -191,8 +211,18 @@ make_term(Str) -> throw(error) end. +-spec nc(File) -> {'ok', Module} | 'error' when + File :: file:name(), + Module :: module(). + nc(File) -> nc(File, []). +-spec nc(File, Options) -> {'ok', Module} | 'error' when + File :: file:name(), + Options :: [Option] | Option, + Option:: compile:option(), + Module :: module(). + nc(File, Opts0) when is_list(Opts0) -> Opts = Opts0 ++ [report_errors, report_warnings], case compile:file(File, Opts) of @@ -215,26 +245,40 @@ nc(File, Opt) when is_atom(Opt) -> %% l(Mod) %% Reload module Mod from file of same name +-spec l(Module) -> code:load_ret() when + Module :: module(). l(Mod) -> code:purge(Mod), code:load_file(Mod). %% Network version of l/1 +-spec nl(Module) -> abcast | error when + Module :: module(). + nl(Mod) -> case code:get_object_code(Mod) of {_Module, Bin, Fname} -> - rpc:eval_everywhere(code,load_binary,[Mod,Fname,Bin]); + rpc:eval_everywhere(code, load_binary, [Mod, Fname, Bin]); Other -> Other end. +-spec i() -> 'ok'. + i() -> i(processes()). + +-spec ni() -> 'ok'. + ni() -> i(all_procs()). +-spec i([pid()]) -> 'ok'. + i(Ps) -> i(Ps, length(Ps)). +-spec i([pid()], non_neg_integer()) -> 'ok'. + i(Ps, N) when N =< 100 -> iformat("Pid", "Initial Call", "Heap", "Reds", "Msgs"), @@ -275,7 +319,6 @@ paged_i(Ps, Acc, N, Page) -> paged_i([], NewAcc, 0, Page) end. - choice(F) -> case get_line('(c)ontinue (q)uit -->', "c\n") of "c\n" -> @@ -285,7 +328,6 @@ choice(F) -> _ -> choice(F) end. - get_line(P, Default) -> case io:get_line(P) of @@ -305,7 +347,6 @@ mfa_string({M,F,A}) -> mfa_string(X) -> w(X). - display_info(Pid) -> case pinfo(Pid) of undefined -> {0,0,0,0}; @@ -317,7 +358,7 @@ display_info(Pid) -> Other -> Other end, - Reds = fetch(reductions, Info), + Reds = fetch(reductions, Info), LM = length(fetch(messages, Info)), HS = fetch(heap_size, Info), SS = fetch(stack_size, Info), @@ -364,21 +405,36 @@ pinfo(Pid) -> end. fetch(Key, Info) -> - case keysearch(Key, 1, Info) of - {value, {_, Val}} -> Val; + case lists:keyfind(Key, 1, Info) of + {_, Val} -> Val; false -> 0 end. -pid(X,Y,Z) -> +-spec pid(X, Y, Z) -> pid() when + X :: non_neg_integer(), + Y :: non_neg_integer(), + Z :: non_neg_integer(). + +pid(X, Y, Z) -> list_to_pid("<" ++ integer_to_list(X) ++ "." ++ integer_to_list(Y) ++ "." ++ integer_to_list(Z) ++ ">"). -i(X,Y,Z) -> pinfo(pid(X,Y,Z)). +-spec i(X, Y, Z) -> [{atom(), term()}] when + X :: non_neg_integer(), + Y :: non_neg_integer(), + Z :: non_neg_integer(). + +i(X, Y, Z) -> pinfo(pid(X, Y, Z)). + +-spec q() -> no_return(). q() -> init:stop(). +-spec bt(Pid) -> 'ok' | 'undefined' when + Pid :: pid(). + bt(Pid) -> case catch erlang:process_display(Pid, backtrace) of {'EXIT', _} -> @@ -387,6 +443,8 @@ bt(Pid) -> ok end. +-spec m() -> 'ok'. + m() -> mformat("Module", "File"), foreach(fun ({Mod,File}) -> mformat(Mod, File) end, sort(code:all_loaded())). @@ -414,8 +472,8 @@ error(Fmt, Args) -> f_p_e(P, F) -> case file:path_eval(P, F) of - {error, enoent} -> - {error, enoent}; + {error, enoent} = Enoent -> + Enoent; {error, E={Line, _Mod, _Term}} -> error("file:path_eval(~p,~p): error on line ~p: ~s~n", [P, F, Line, file:format_error(E)]), @@ -438,10 +496,12 @@ bi(I) -> %% %% Short and nice form of module info %% +-spec m(Module) -> 'ok' when + Module :: module(). m(M) -> L = M:module_info(), - {value,{exports,E}} = keysearch(exports, 1, L), + {exports,E} = lists:keyfind(exports, 1, L), Time = get_compile_time(L), COpts = get_compile_options(L), format("Module ~w compiled: ",[M]), print_time(Time), @@ -470,10 +530,10 @@ get_compile_options(L) -> end. get_compile_info(L, Tag) -> - case keysearch(compile, 1, L) of - {value, {compile, I}} -> - case keysearch(Tag, 1, I) of - {value, {Tag, Val}} -> {ok,Val}; + case lists:keyfind(compile, 1, L) of + {compile, I} -> + case lists:keyfind(Tag, 1, I) of + {Tag, Val} -> {ok,Val}; false -> error end; false -> error @@ -523,6 +583,8 @@ month(11) -> "November"; month(12) -> "December". %% Just because we can't eval receive statements... +-spec flush() -> 'ok'. + flush() -> receive X -> @@ -533,9 +595,13 @@ flush() -> end. %% Print formatted info about all registered names in the system +-spec nregs() -> 'ok'. + nregs() -> foreach(fun (N) -> print_node_regs(N) end, all_regs()). +-spec regs() -> 'ok'. + regs() -> print_node_regs({node(),registered()}). @@ -609,14 +675,19 @@ portformat(Name, Id, Cmd) -> %% cd(Directory) %% These are just wrappers around the file:get/set_cwd functions. +-spec pwd() -> 'ok'. + pwd() -> case file:get_cwd() of {ok, Str} -> - ok = io:format("~s\n", [Str]); + ok = io:format("~ts\n", [fixup_one_bin(Str)]); {error, _} -> ok = io:format("Cannot determine current directory\n") end. +-spec cd(Dir) -> 'ok' when + Dir :: file:name(). + cd(Dir) -> file:set_cwd(Dir), pwd(). @@ -625,17 +696,38 @@ cd(Dir) -> %% ls(Directory) %% The strategy is to print in fixed width files. +-spec ls() -> 'ok'. + ls() -> ls("."). +-spec ls(Dir) -> 'ok' when + Dir :: file:name(). + ls(Dir) -> case file:list_dir(Dir) of {ok, Entries} -> - ls_print(sort(Entries)); + ls_print(sort(fixup_bin(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, @@ -645,7 +737,7 @@ ls_print(X, Width, Len) when Width + Len >= 80 -> io:nl(), ls_print(X, Width, 0); ls_print([H|T], Width, Len) -> - io:format("~-*s",[Width,H]), + io:format("~-*ts",[Width,H]), ls_print(T, Width, Len+Width); ls_print([], _, _) -> io:nl(). @@ -660,24 +752,38 @@ w(X) -> %% memory/[0,1] %% -memory() -> erlang:memory(). +-spec memory() -> [{Type, Size}] when + Type :: atom(), + Size :: non_neg_integer(). + +memory() -> erlang:memory(). + +-spec memory(Type) -> Size when + Type :: atom(), + Size :: non_neg_integer() + ; (Types) -> [{Type, Size}] when + Types :: [Type], + Type :: atom(), + Size :: non_neg_integer(). + memory(TypeSpec) -> erlang:memory(TypeSpec). %% %% Cross Reference Check %% - +%%-spec xm(module() | file:filename()) -> xref:m/1 return xm(M) -> appcall(tools, xref, m, [M]). %% %% Call yecc %% - +%%-spec y(file:name()) -> yecc:file/2 return y(File) -> y(File, []). +%%-spec y(file:name(), [yecc:option()]) -> yecc:file/2 return y(File, Opts) -> - appcall(parsetools, yecc, file, [File,Opts]). + appcall(parsetools, yecc, file, [File, Opts]). %% @@ -699,4 +805,3 @@ appcall(App, M, F, Args) -> erlang:raise(error, undef, Stk) end end. - diff --git a/lib/stdlib/src/calendar.erl b/lib/stdlib/src/calendar.erl index ddc0666f77..8d1071209e 100644 --- a/lib/stdlib/src/calendar.erl +++ b/lib/stdlib/src/calendar.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. 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,6 +28,8 @@ gregorian_days_to_date/1, gregorian_seconds_to_datetime/1, is_leap_year/1, + iso_week_number/0, + iso_week_number/1, last_day_of_the_month/2, local_time/0, local_time_to_universal_time/1, @@ -61,6 +63,8 @@ %% Types %%---------------------------------------------------------------------- +-export_type([t_now/0]). + -type year() :: non_neg_integer(). -type year1970() :: 1970..10000. % should probably be 1970.. -type month() :: 1..12. @@ -70,13 +74,17 @@ -type second() :: 0..59. -type daynum() :: 1..7. -type ldom() :: 28 | 29 | 30 | 31. % last day of month +-type weeknum() :: 1..53. --type t_now() :: {non_neg_integer(),non_neg_integer(),non_neg_integer()}. +-type t_now() :: {MegaSecs :: non_neg_integer(), + Secs :: non_neg_integer(), + MicroSecs :: non_neg_integer()}. -type t_date() :: {year(),month(),day()}. -type t_time() :: {hour(),minute(),second()}. -type t_datetime() :: {t_date(),t_time()}. -type t_datetime1970() :: {{year1970(),month(),day()},t_time()}. +-type t_yearweeknum() :: {year(),weeknum()}. %%---------------------------------------------------------------------- @@ -102,7 +110,11 @@ %% January 1st. %% %% df/2 catches the case Year<0 --spec date_to_gregorian_days(year(),month(),day()) -> non_neg_integer(). +-spec date_to_gregorian_days(Year, Month, Day) -> Days when + Year :: year(), + Month :: month(), + Day :: day(), + Days :: non_neg_integer(). date_to_gregorian_days(Year, Month, Day) when is_integer(Day), Day > 0 -> Last = last_day_of_the_month(Year, Month), if @@ -110,7 +122,9 @@ date_to_gregorian_days(Year, Month, Day) when is_integer(Day), Day > 0 -> dy(Year) + dm(Month) + df(Year, Month) + Day - 1 end. --spec date_to_gregorian_days(t_date()) -> non_neg_integer(). +-spec date_to_gregorian_days(Date) -> Days when + Date :: t_date(), + Days :: non_neg_integer(). date_to_gregorian_days({Year, Month, Day}) -> date_to_gregorian_days(Year, Month, Day). @@ -120,7 +134,9 @@ date_to_gregorian_days({Year, Month, Day}) -> %% Computes the total number of seconds starting from year 0, %% January 1st. %% --spec datetime_to_gregorian_seconds(t_datetime()) -> non_neg_integer(). +-spec datetime_to_gregorian_seconds(DateTime) -> Seconds when + DateTime :: t_datetime(), + Seconds :: non_neg_integer(). datetime_to_gregorian_seconds({Date, Time}) -> ?SECONDS_PER_DAY*date_to_gregorian_days(Date) + time_to_seconds(Time). @@ -131,18 +147,23 @@ datetime_to_gregorian_seconds({Date, Time}) -> %% %% Returns: 1 | .. | 7. Monday = 1, Tuesday = 2, ..., Sunday = 7. %% --spec day_of_the_week(year(), month(), day()) -> daynum(). +-spec day_of_the_week(Year, Month, Day) -> daynum() when + Year :: year(), + Month :: month(), + Day :: day(). day_of_the_week(Year, Month, Day) -> (date_to_gregorian_days(Year, Month, Day) + 5) rem 7 + 1. --spec day_of_the_week(t_date()) -> daynum(). +-spec day_of_the_week(Date) -> daynum() when + Date:: t_date(). day_of_the_week({Year, Month, Day}) -> day_of_the_week(Year, Month, Day). %% gregorian_days_to_date(Days) = {Year, Month, Day} %% --spec gregorian_days_to_date(non_neg_integer()) -> t_date(). +-spec gregorian_days_to_date(Days) -> t_date() when + Days :: non_neg_integer(). gregorian_days_to_date(Days) -> {Year, DayOfYear} = day_to_year(Days), {Month, DayOfMonth} = year_day_to_date(Year, DayOfYear), @@ -151,7 +172,8 @@ gregorian_days_to_date(Days) -> %% gregorian_seconds_to_datetime(Secs) %% --spec gregorian_seconds_to_datetime(non_neg_integer()) -> t_datetime(). +-spec gregorian_seconds_to_datetime(Seconds) -> t_datetime() when + Seconds :: non_neg_integer(). gregorian_seconds_to_datetime(Secs) when Secs >= 0 -> Days = Secs div ?SECONDS_PER_DAY, Rest = Secs rem ?SECONDS_PER_DAY, @@ -160,7 +182,8 @@ gregorian_seconds_to_datetime(Secs) when Secs >= 0 -> %% is_leap_year(Year) = true | false %% --spec is_leap_year(year()) -> boolean(). +-spec is_leap_year(Year) -> boolean() when + Year :: year(). is_leap_year(Y) when is_integer(Y), Y >= 0 -> is_leap_year1(Y). @@ -172,11 +195,51 @@ is_leap_year1(Year) when Year rem 400 =:= 0 -> is_leap_year1(_) -> false. +%% +%% Calculates the iso week number for the current date. +%% +-spec iso_week_number() -> t_yearweeknum(). +iso_week_number() -> + {Date, _} = local_time(), + iso_week_number(Date). + + +%% +%% Calculates the iso week number for the given date. +%% +-spec iso_week_number(Date) -> t_yearweeknum() when + Date :: t_date(). +iso_week_number({Year, Month, Day}) -> + D = date_to_gregorian_days({Year, Month, Day}), + W01_1_Year = gregorian_days_of_iso_w01_1(Year), + W01_1_NextYear = gregorian_days_of_iso_w01_1(Year + 1), + if W01_1_Year =< D andalso D < W01_1_NextYear -> + % Current Year Week 01..52(,53) + {Year, (D - W01_1_Year) div 7 + 1}; + D < W01_1_Year -> + % Previous Year 52 or 53 + PWN = case day_of_the_week(Year - 1, 1, 1) of + 4 -> 53; + _ -> case day_of_the_week(Year - 1, 12, 31) of + 4 -> 53; + _ -> 52 + end + end, + {Year - 1, PWN}; + W01_1_NextYear =< D -> + % Next Year, Week 01 + {Year + 1, 1} + end. + + %% last_day_of_the_month(Year, Month) %% %% Returns the number of days in a month. %% --spec last_day_of_the_month(year(), month()) -> ldom(). +-spec last_day_of_the_month(Year, Month) -> LastDay when + Year :: year(), + Month :: month(), + LastDay :: ldom(). last_day_of_the_month(Y, M) when is_integer(Y), Y >= 0 -> last_day_of_the_month1(Y, M). @@ -204,7 +267,9 @@ local_time() -> %% local_time_to_universal_time(DateTime) %% --spec local_time_to_universal_time(t_datetime1970()) -> t_datetime1970(). +-spec local_time_to_universal_time(DateTime1) -> DateTime2 when + DateTime1 :: t_datetime1970(), + DateTime2 :: t_datetime1970(). local_time_to_universal_time(DateTime) -> erlang:localtime_to_universaltime(DateTime). @@ -214,7 +279,9 @@ local_time_to_universal_time(DateTime) -> local_time_to_universal_time(DateTime, IsDst) -> erlang:localtime_to_universaltime(DateTime, IsDst). --spec local_time_to_universal_time_dst(t_datetime1970()) -> [t_datetime1970()]. +-spec local_time_to_universal_time_dst(DateTime1) -> [DateTime] when + DateTime1 :: t_datetime1970(), + DateTime :: t_datetime1970(). local_time_to_universal_time_dst(DateTime) -> UtDst = erlang:localtime_to_universaltime(DateTime, true), Ut = erlang:localtime_to_universaltime(DateTime, false), @@ -242,12 +309,14 @@ local_time_to_universal_time_dst(DateTime) -> %% = MilliSec = integer() %% Returns: {date(), time()}, date() = {Y, M, D}, time() = {H, M, S}. %% --spec now_to_datetime(t_now()) -> t_datetime1970(). +-spec now_to_datetime(Now) -> t_datetime1970() when + Now :: t_now(). now_to_datetime({MSec, Sec, _uSec}) -> Sec0 = MSec*1000000 + Sec + ?DAYS_FROM_0_TO_1970*?SECONDS_PER_DAY, gregorian_seconds_to_datetime(Sec0). --spec now_to_universal_time(t_now()) -> t_datetime1970(). +-spec now_to_universal_time(Now) -> t_datetime1970() when + Now :: t_now(). now_to_universal_time(Now) -> now_to_datetime(Now). @@ -256,7 +325,8 @@ now_to_universal_time(Now) -> %% %% Args: Now = now() %% --spec now_to_local_time(t_now()) -> t_datetime1970(). +-spec now_to_local_time(Now) -> t_datetime1970() when + Now :: t_now(). now_to_local_time({MSec, Sec, _uSec}) -> erlang:universaltime_to_localtime( now_to_universal_time({MSec, Sec, _uSec})). @@ -265,7 +335,10 @@ now_to_local_time({MSec, Sec, _uSec}) -> %% seconds_to_daystime(Secs) = {Days, {Hour, Minute, Second}} %% --spec seconds_to_daystime(integer()) -> {integer(), t_time()}. +-spec seconds_to_daystime(Seconds) -> {Days, Time} when + Seconds :: integer(), + Days :: integer(), + Time :: t_time(). seconds_to_daystime(Secs) -> Days0 = Secs div ?SECONDS_PER_DAY, Secs0 = Secs rem ?SECONDS_PER_DAY, @@ -283,7 +356,8 @@ seconds_to_daystime(Secs) -> %% Wraps. %% -type secs_per_day() :: 0..?SECONDS_PER_DAY. --spec seconds_to_time(secs_per_day()) -> t_time(). +-spec seconds_to_time(Seconds) -> t_time() when + Seconds :: secs_per_day(). seconds_to_time(Secs) when Secs >= 0, Secs < ?SECONDS_PER_DAY -> Secs0 = Secs rem ?SECONDS_PER_DAY, Hour = Secs0 div ?SECONDS_PER_HOUR, @@ -300,8 +374,11 @@ seconds_to_time(Secs) when Secs >= 0, Secs < ?SECONDS_PER_DAY -> %% Date = {Year, Month, Day}, Time = {Hour, Minute, Sec}, %% Year = Month = Day = Hour = Minute = Sec = integer() %% --type timediff() :: {integer(), t_time()}. --spec time_difference(t_datetime(), t_datetime()) -> timediff(). +-spec time_difference(T1, T2) -> {Days, Time} when + T1 :: t_datetime(), + T2 :: t_datetime(), + Days :: integer(), + Time :: t_time(). time_difference({{Y1, Mo1, D1}, {H1, Mi1, S1}}, {{Y2, Mo2, D2}, {H2, Mi2, S2}}) -> Secs = datetime_to_gregorian_seconds({{Y2, Mo2, D2}, {H2, Mi2, S2}}) - @@ -312,7 +389,8 @@ time_difference({{Y1, Mo1, D1}, {H1, Mi1, S1}}, %% %% time_to_seconds(Time) %% --spec time_to_seconds(t_time()) -> secs_per_day(). +-spec time_to_seconds(Time) -> secs_per_day() when + Time :: t_time(). time_to_seconds({H, M, S}) when is_integer(H), is_integer(M), is_integer(S) -> H * ?SECONDS_PER_HOUR + M * ?SECONDS_PER_MINUTE + S. @@ -328,7 +406,8 @@ universal_time() -> %% universal_time_to_local_time(DateTime) %% --spec universal_time_to_local_time(t_datetime()) -> t_datetime(). +-spec universal_time_to_local_time(DateTime) -> t_datetime() when + DateTime :: t_datetime1970(). universal_time_to_local_time(DateTime) -> erlang:universaltime_to_localtime(DateTime). @@ -336,7 +415,10 @@ universal_time_to_local_time(DateTime) -> %% valid_date(Year, Month, Day) = true | false %% valid_date({Year, Month, Day}) = true | false %% --spec valid_date(integer(), integer(), integer()) -> boolean(). +-spec valid_date(Year, Month, Day) -> boolean() when + Year :: integer(), + Month :: integer(), + Day :: integer(). valid_date(Y, M, D) when is_integer(Y), is_integer(M), is_integer(D) -> valid_date1(Y, M, D). @@ -346,7 +428,8 @@ valid_date1(Y, M, D) when Y >= 0, M > 0, M < 13, D > 0 -> valid_date1(_, _, _) -> false. --spec valid_date({integer(),integer(),integer()}) -> boolean(). +-spec valid_date(Date) -> boolean() when + Date :: t_date(). valid_date({Y, M, D}) -> valid_date(Y, M, D). @@ -377,6 +460,19 @@ dty(Y, D1, D2) when D1 < D2 -> dty(Y, _D1, D2) -> {Y, D2}. +%% +%% The Gregorian days of the iso week 01 day 1 for a given year. +%% +-spec gregorian_days_of_iso_w01_1(year()) -> non_neg_integer(). +gregorian_days_of_iso_w01_1(Year) -> + D0101 = date_to_gregorian_days(Year, 1, 1), + DOW = day_of_the_week(Year, 1, 1), + if DOW =< 4 -> + D0101 - DOW + 1; + true -> + D0101 + 7 - DOW + 1 + end. + %% year_day_to_date(Year, DayOfYear) = {Month, DayOfMonth} %% %% Note: 1 is the first day of the month. diff --git a/lib/stdlib/src/dets.erl b/lib/stdlib/src/dets.erl index 7f1c13770b..671b5a9dd4 100644 --- a/lib/stdlib/src/dets.erl +++ b/lib/stdlib/src/dets.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1996-2011. 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(dets). @@ -88,6 +88,7 @@ %% Not documented, or not ready for publication. -export([lookup_keys/2]). +-export_type([tab_name/0]). -compile({inline, [{einval,2},{badarg,2},{undefined,1}, {badarg_exit,2},{lookup_reply,2}]}). @@ -96,10 +97,6 @@ -include("dets.hrl"). --type object() :: tuple(). --type pattern() :: atom() | tuple(). --type tab_name() :: atom() | reference(). - %%% This is the implementation of the mnesia file storage. Each (non %%% ram-copy) table is maintained in a corresponding .DAT file. The %%% dat file is organized as a segmented linear hashlist. The head of @@ -146,6 +143,7 @@ bin, % small chunk not consumed, or 'eof' at end-of-file alloc, % the part of the file not yet scanned, mostly a binary tab, + proc, % the pid of the Dets process match_program % true | compiled_match_spec() | undefined }). @@ -177,6 +175,21 @@ %%-define(PROFILE(C), C). -define(PROFILE(C), void). +-type access() :: 'read' | 'read_write'. +-type auto_save() :: 'infinity' | non_neg_integer(). +-opaque bindings_cont() :: #dets_cont{}. +-opaque cont() :: #dets_cont{}. +-type keypos() :: pos_integer(). +-type match_spec() :: ets:match_spec(). +-type object() :: tuple(). +-type no_slots() :: non_neg_integer() | 'default'. +-opaque object_cont() :: #dets_cont{}. +-type pattern() :: atom() | tuple(). +-opaque select_cont() :: #dets_cont{}. +-type tab_name() :: term(). +-type type() :: 'bag' | 'duplicate_bag' | 'set'. +-type version() :: 8 | 9 | 'default'. + %%% Some further debug code was added in R12B-1 (stdlib-1.15.1): %%% - there is a new open_file() option 'debug'; %%% - there is a new OS environment variable 'DETS_DEBUG'; @@ -201,20 +214,24 @@ add_user(Pid, Tab, Args) -> all() -> dets_server:all(). --type cont() :: #dets_cont{}. --spec bchunk(tab_name(), 'start' | cont()) -> - {cont(), binary() | tuple()} | '$end_of_table' | {'error', term()}. +-spec bchunk(Name, Continuation) -> + {Continuation2, Data} | '$end_of_table' | {'error', Reason} when + Name :: tab_name(), + Continuation :: 'start' | cont(), + Continuation2 :: cont(), + Data :: binary() | tuple(), + Reason :: term(). bchunk(Tab, start) -> badarg(treq(Tab, {bchunk_init, Tab}), [Tab, start]); -bchunk(Tab, #dets_cont{bin = eof, tab = Tab}) -> - '$end_of_table'; bchunk(Tab, #dets_cont{what = bchunk, tab = Tab} = State) -> badarg(treq(Tab, {bchunk, State}), [Tab, State]); bchunk(Tab, Term) -> erlang:error(badarg, [Tab, Term]). --spec close(tab_name()) -> 'ok' | {'error', term()}. +-spec close(Name) -> 'ok' | {'error', Reason} when + Name :: tab_name(), + Reason :: term(). close(Tab) -> case dets_server:close(Tab) of @@ -224,12 +241,17 @@ close(Tab) -> Reply end. --spec delete(tab_name(), term()) -> 'ok' | {'error', term()}. +-spec delete(Name, Key) -> 'ok' | {'error', Reason} when + Name :: tab_name(), + Key :: term(), + Reason :: term(). delete(Tab, Key) -> badarg(treq(Tab, {delete_key, [Key]}), [Tab, Key]). --spec delete_all_objects(tab_name()) -> 'ok' | {'error', term()}. +-spec delete_all_objects(Name) -> 'ok' | {'error', Reason} when + Name :: tab_name(), + Reason :: term(). delete_all_objects(Tab) -> case treq(Tab, delete_all_objects) of @@ -241,7 +263,10 @@ delete_all_objects(Tab) -> Reply end. --spec delete_object(tab_name(), object()) -> 'ok' | {'error', term()}. +-spec delete_object(Name, Object) -> 'ok' | {'error', Reason} when + Name :: tab_name(), + Object :: object(), + Reason :: term(). delete_object(Tab, O) -> badarg(treq(Tab, {delete_object, [O]}), [Tab, O]). @@ -264,23 +289,42 @@ fsck(Fname, Version) -> end end. --spec first(tab_name()) -> term() | '$end_of_table'. +-spec first(Name) -> Key | '$end_of_table' when + Name :: tab_name(), + Key :: term(). first(Tab) -> badarg_exit(treq(Tab, first), [Tab]). --spec foldr(fun((object(), Acc) -> Acc), Acc, tab_name()) -> Acc | {'error', term()}. +-spec foldr(Function, Acc0, Name) -> Acc | {'error', Reason} when + Name :: tab_name(), + Function :: fun((Object :: object(), AccIn) -> AccOut), + Acc0 :: term(), + Acc :: term(), + AccIn :: term(), + AccOut :: term(), + Reason :: term(). foldr(Fun, Acc, Tab) -> foldl(Fun, Acc, Tab). --spec foldl(fun((object(), Acc) -> Acc), Acc, tab_name()) -> Acc | {'error', term()}. +-spec foldl(Function, Acc0, Name) -> Acc | {'error', Reason} when + Name :: tab_name(), + Function :: fun((Object :: object(), AccIn) -> AccOut), + Acc0 :: term(), + Acc :: term(), + AccIn :: term(), + AccOut :: term(), + Reason :: term(). foldl(Fun, Acc, Tab) -> Ref = make_ref(), do_traverse(Fun, Acc, Tab, Ref). --spec from_ets(tab_name(), ets:tab()) -> 'ok' | {'error', term()}. +-spec from_ets(Name, EtsTab) -> 'ok' | {'error', Reason} when + Name :: tab_name(), + EtsTab :: ets:tab(), + Reason :: term(). from_ets(DTab, ETab) -> ets:safe_fixtable(ETab, true), @@ -304,6 +348,15 @@ from_ets_fun(LC, ETab) -> {L, from_ets_fun(ets:select(C), ETab)} end. +-spec info(Name) -> InfoList | 'undefined' when + Name :: tab_name(), + InfoList :: [InfoTuple], + InfoTuple :: {'file_size', non_neg_integer()} + | {'filename', file:name()} + | {'keypos', keypos()} + | {'size', non_neg_integer()} + | {'type', type()}. + info(Tab) -> case catch dets_server:get_pid(Tab) of {'EXIT', _Reason} -> @@ -312,6 +365,14 @@ info(Tab) -> undefined(req(Pid, info)) end. +-spec info(Name, Item) -> Value | 'undefined' when + Name :: tab_name(), + Item :: 'access' | 'auto_save' | 'bchunk_format' + | 'hash' | 'file_size' | 'filename' | 'keypos' | 'memory' + | 'no_keys' | 'no_objects' | 'no_slots' | 'owner' | 'ram_file' + | 'safe_fixed' | 'size' | 'type' | 'version', + Value :: term(). + info(Tab, owner) -> case catch dets_server:get_pid(Tab) of Pid when is_pid(Pid) -> @@ -334,9 +395,26 @@ info(Tab, Tag) -> undefined(req(Pid, {info, Tag})) end. +-spec init_table(Name, InitFun) -> ok | {'error', Reason} when + Name :: tab_name(), + InitFun :: fun((Arg) -> Res), + Arg :: read | close, + Res :: end_of_input | {[object()], InitFun} | {Data, InitFun} | term(), + Reason :: term(), + Data :: binary() | tuple(). + init_table(Tab, InitFun) -> init_table(Tab, InitFun, []). +-spec init_table(Name, InitFun, Options) -> ok | {'error', Reason} when + Name :: tab_name(), + InitFun :: fun((Arg) -> Res), + Arg :: read | close, + Res :: end_of_input | {[object()], InitFun} | {Data, InitFun} | term(), + Options :: [{min_no_slots,no_slots()} | {format,term | bchunk}], + Reason :: term(), + Data :: binary() | tuple(). + init_table(Tab, InitFun, Options) when is_function(InitFun) -> case options(Options, [format, min_no_slots]) of {badarg,_} -> @@ -350,11 +428,20 @@ init_table(Tab, InitFun, Options) when is_function(InitFun) -> init_table(Tab, InitFun, Options) -> erlang:error(badarg, [Tab, InitFun, Options]). +-spec insert(Name, Objects) -> 'ok' | {'error', Reason} when + Name :: tab_name(), + Objects :: object() | [object()], + Reason :: term(). + insert(Tab, Objs) when is_list(Objs) -> badarg(treq(Tab, {insert, Objs}), [Tab, Objs]); insert(Tab, Obj) -> badarg(treq(Tab, {insert, [Obj]}), [Tab, Obj]). +-spec insert_new(Name, Objects) -> boolean() when + Name :: tab_name(), + Objects :: object() | [object()]. + insert_new(Tab, Objs) when is_list(Objs) -> badarg(treq(Tab, {insert_new, Objs}), [Tab, Objs]); insert_new(Tab, Obj) -> @@ -366,9 +453,17 @@ internal_close(Pid) -> internal_open(Pid, Ref, Args) -> req(Pid, {internal_open, Ref, Args}). +-spec is_compatible_bchunk_format(Name, BchunkFormat) -> boolean() when + Name :: tab_name(), + BchunkFormat :: binary(). + is_compatible_bchunk_format(Tab, Term) -> badarg(treq(Tab, {is_compatible_bchunk_format, Term}), [Tab, Term]). +-spec is_dets_file(Filename) -> boolean() | {'error', Reason} when + Filename :: file:name(), + Reason :: term(). + is_dets_file(FileName) -> case catch read_file_header(FileName, read, false) of {ok, Fd, FH} -> @@ -382,6 +477,12 @@ is_dets_file(FileName) -> Other end. +-spec lookup(Name, Key) -> Objects | {'error', Reason} when + Name :: tab_name(), + Key :: term(), + Objects :: [object()], + Reason :: term(). + lookup(Tab, Key) -> badarg(treq(Tab, {lookup_keys, [Key]}), [Tab, Key]). @@ -394,19 +495,43 @@ lookup_keys(Tab, Keys) -> erlang:error(badarg, [Tab, Keys]) end. +-spec match(Name, Pattern) -> [Match] | {'error', Reason} when + Name :: tab_name(), + Pattern :: pattern(), + Match :: [term()], + Reason :: term(). + match(Tab, Pat) -> badarg(safe_match(Tab, Pat, bindings), [Tab, Pat]). +-spec match(Name, Pattern, N) -> + {[Match], Continuation} | '$end_of_table' | {'error', Reason} when + Name :: tab_name(), + Pattern :: pattern(), + N :: 'default' | non_neg_integer(), + Continuation :: bindings_cont(), + Match :: [term()], + Reason :: term(). + match(Tab, Pat, N) -> badarg(init_chunk_match(Tab, Pat, bindings, N), [Tab, Pat, N]). +-spec match(Continuation) -> + {[Match], Continuation2} | '$end_of_table' | {'error', Reason} when + Continuation :: bindings_cont(), + Continuation2 :: bindings_cont(), + Match :: [term()], + Reason :: term(). + match(State) when State#dets_cont.what =:= bindings -> badarg(chunk_match(State), [State]); match(Term) -> erlang:error(badarg, [Term]). --spec match_delete(tab_name(), pattern()) -> - non_neg_integer() | 'ok' | {'error', term()}. +-spec match_delete(Name, Pattern) -> 'ok' | {'error', Reason} when + Name :: tab_name(), + Pattern :: pattern(), + Reason :: term(). match_delete(Tab, Pat) -> badarg(match_delete(Tab, Pat, delete), [Tab, Pat]). @@ -434,23 +559,60 @@ do_match_delete(Tab, _Proc, Error, _What, _N) -> safe_fixtable(Tab, false), Error. +-spec match_object(Name, Pattern) -> Objects | {'error', Reason} when + Name :: tab_name(), + Pattern :: pattern(), + Objects :: [object()], + Reason :: term(). + match_object(Tab, Pat) -> badarg(safe_match(Tab, Pat, object), [Tab, Pat]). +-spec match_object(Name, Pattern, N) -> + {Objects, Continuation} | '$end_of_table' | {'error', Reason} when + Name :: tab_name(), + Pattern :: pattern(), + N :: 'default' | non_neg_integer(), + Continuation :: object_cont(), + Objects :: [object()], + Reason :: term(). + match_object(Tab, Pat, N) -> badarg(init_chunk_match(Tab, Pat, object, N), [Tab, Pat, N]). +-spec match_object(Continuation) -> + {Objects, Continuation2} | '$end_of_table' | {'error', Reason} when + Continuation :: object_cont(), + Continuation2 :: object_cont(), + Objects :: [object()], + Reason :: term(). + match_object(State) when State#dets_cont.what =:= object -> badarg(chunk_match(State), [State]); match_object(Term) -> erlang:error(badarg, [Term]). +-spec member(Name, Key) -> boolean() | {'error', Reason} when + Name :: tab_name(), + Key :: term(), + Reason :: term(). + member(Tab, Key) -> badarg(treq(Tab, {member, Key}), [Tab, Key]). +-spec next(Name, Key1) -> Key2 | '$end_of_table' when + Name :: tab_name(), + Key1 :: term(), + Key2 :: term(). + next(Tab, Key) -> badarg_exit(treq(Tab, {next, Key}), [Tab, Key]). +-spec open_file(Filename) -> {'ok', Reference} | {'error', Reason} when + Filename :: file:name(), + Reference :: reference(), + Reason :: term(). + %% Assuming that a file already exists, open it with the %% parameters as already specified in the file itself. %% Return a ref leading to the file. @@ -462,6 +624,22 @@ open_file(File) -> einval(Reply, [File]) end. +-spec open_file(Name, Args) -> {'ok', Name} | {'error', Reason} when + Name :: tab_name(), + Args :: [OpenArg], + OpenArg :: {'access', access()} + | {'auto_save', auto_save()} + | {'estimated_no_objects', non_neg_integer()} + | {'file', file:name()} + | {'max_no_slots', no_slots()} + | {'min_no_slots', no_slots()} + | {'keypos', keypos()} + | {'ram_file', boolean()} + | {'repair', boolean() | 'force'} + | {'type', type()} + | {'version', version()}, + Reason :: term(). + open_file(Tab, Args) when is_list(Args) -> case catch defaults(Tab, Args) of OpenArgs when is_record(OpenArgs, open_args) -> @@ -477,12 +655,21 @@ open_file(Tab, Args) when is_list(Args) -> open_file(Tab, Arg) -> open_file(Tab, [Arg]). +-spec pid2name(Pid) -> {'ok', Name} | 'undefined' when + Pid :: pid(), + Name :: tab_name(). + pid2name(Pid) -> dets_server:pid2name(Pid). remove_user(Pid, From) -> req(Pid, {close, From}). +-spec repair_continuation(Continuation, MatchSpec) -> Continuation2 when + Continuation :: select_cont(), + Continuation2 :: select_cont(), + MatchSpec :: match_spec(). + repair_continuation(#dets_cont{match_program = B}=Cont, MS) when is_binary(B) -> case ets:is_compiled_ms(B) of @@ -496,25 +683,63 @@ repair_continuation(#dets_cont{}=Cont, _MS) -> repair_continuation(T, MS) -> erlang:error(badarg, [T, MS]). +-spec safe_fixtable(Name, Fix) -> 'ok' when + Name :: tab_name(), + Fix :: boolean(). + safe_fixtable(Tab, Bool) when Bool; not Bool -> badarg(treq(Tab, {safe_fixtable, Bool}), [Tab, Bool]); safe_fixtable(Tab, Term) -> erlang:error(badarg, [Tab, Term]). +-spec select(Name, MatchSpec) -> Selection | {'error', Reason} when + Name :: tab_name(), + MatchSpec :: match_spec(), + Selection :: [term()], + Reason :: term(). + select(Tab, Pat) -> badarg(safe_match(Tab, Pat, select), [Tab, Pat]). +-spec select(Name, MatchSpec, N) -> + {Selection, Continuation} | '$end_of_table' | {'error', Reason} when + Name :: tab_name(), + MatchSpec :: match_spec(), + N :: 'default' | non_neg_integer(), + Continuation :: select_cont(), + Selection :: [term()], + Reason :: term(). + select(Tab, Pat, N) -> badarg(init_chunk_match(Tab, Pat, select, N), [Tab, Pat, N]). +-spec select(Continuation) -> + {Selection, Continuation2} | '$end_of_table' | {'error', Reason} when + Continuation :: select_cont(), + Continuation2 :: select_cont(), + Selection :: [term()], + Reason :: term(). + select(State) when State#dets_cont.what =:= select -> badarg(chunk_match(State), [State]); select(Term) -> erlang:error(badarg, [Term]). +-spec select_delete(Name, MatchSpec) -> N | {'error', Reason} when + Name :: tab_name(), + MatchSpec :: match_spec(), + N :: non_neg_integer(), + Reason :: term(). + select_delete(Tab, Pat) -> badarg(match_delete(Tab, Pat, select), [Tab, Pat]). +-spec slot(Name, I) -> '$end_of_table' | Objects | {'error', Reason} when + Name :: tab_name(), + I :: non_neg_integer(), + Objects :: [object()], + Reason :: term(). + slot(Tab, Slot) when is_integer(Slot), Slot >= 0 -> badarg(treq(Tab, {slot, Slot}), [Tab, Slot]); slot(Tab, Term) -> @@ -529,12 +754,29 @@ stop() -> istart_link(Server) -> {ok, proc_lib:spawn_link(dets, init, [self(), Server])}. +-spec sync(Name) -> 'ok' | {'error', Reason} when + Name :: tab_name(), + Reason :: term(). + sync(Tab) -> badarg(treq(Tab, sync), [Tab]). +-spec table(Name) -> QueryHandle when + Name :: tab_name(), + QueryHandle :: qlc:query_handle(). + table(Tab) -> table(Tab, []). +-spec table(Name, Options) -> QueryHandle when + Name :: tab_name(), + Options :: Option | [Option], + Option :: {'n_objects', Limit} + | {'traverse', TraverseMethod}, + Limit :: 'default' | pos_integer(), + TraverseMethod :: 'first_next' | 'select' | {'select', match_spec()}, + QueryHandle :: qlc:query_handle(). + table(Tab, Opts) -> case options(Opts, [traverse, n_objects]) of {badarg,_} -> @@ -612,6 +854,11 @@ table_info(_Tab, _) -> %% End of table/2. +-spec to_ets(Name, EtsTab) -> EtsTab | {'error', Reason} when + Name :: tab_name(), + EtsTab :: ets:tab(), + Reason :: term(). + to_ets(DTab, ETab) -> case ets:info(ETab, protection) of undefined -> @@ -621,6 +868,16 @@ to_ets(DTab, ETab) -> foldl(Fun, ETab, DTab) end. +-spec traverse(Name, Fun) -> Return | {'error', Reason} when + Name :: tab_name(), + Fun :: fun((Object) -> FunReturn), + FunReturn :: 'continue' | {'continue', Val} | {'done', Value}, + Val :: term(), + Value :: term(), + Object :: object(), + Return :: [term()], + Reason :: term(). + traverse(Tab, Fun) -> Ref = make_ref(), TFun = @@ -638,6 +895,14 @@ traverse(Tab, Fun) -> end, do_traverse(TFun, [], Tab, Ref). +-spec update_counter(Name, Key, Increment) -> Result when + Name :: tab_name(), + Key :: term(), + Increment :: {Pos, Incr} | Incr, + Pos :: integer(), + Incr :: integer(), + Result :: integer(). + update_counter(Tab, Key, C) -> badarg(treq(Tab, {update_counter, Key, C}), [Tab, Key, C]). @@ -721,11 +986,14 @@ init_chunk_match(Tab, Pat, What, N) when is_integer(N), N >= 0; N =:= default -> case compile_match_spec(What, Pat) of {Spec, MP} -> - case req(dets_server:get_pid(Tab), {match, MP, Spec, N}) of + Proc = dets_server:get_pid(Tab), + case req(Proc, {match, MP, Spec, N}) of {done, L} -> - {L, #dets_cont{tab = Tab, what = What, bin = eof}}; + {L, #dets_cont{tab = Tab, proc = Proc, what = What, + bin = eof}}; {cont, State} -> - chunk_match(State#dets_cont{what = What, tab = Tab}); + chunk_match(State#dets_cont{what = What, tab = Tab, + proc = Proc}); Error -> Error end; @@ -735,34 +1003,28 @@ init_chunk_match(Tab, Pat, What, N) when is_integer(N), N >= 0; init_chunk_match(_Tab, _Pat, _What, _) -> badarg. -chunk_match(State) -> - case catch dets_server:get_pid(State#dets_cont.tab) of - {'EXIT', _Reason} -> - badarg; - _Proc when State#dets_cont.bin =:= eof -> - '$end_of_table'; - Proc -> - case req(Proc, {match_init, State}) of - {cont, {Bins, NewState}} -> - MP = NewState#dets_cont.match_program, - case catch do_foldl_bins(Bins, MP) of - {'EXIT', _} -> - case ets:is_compiled_ms(MP) of - true -> - Bad = dets_utils:bad_object(chunk_match, - Bins), - req(Proc, {corrupt, Bad}); - false -> - badarg - end; - [] -> - chunk_match(NewState); - Terms -> - {Terms, NewState} - end; - Error -> - Error - end +chunk_match(#dets_cont{proc = Proc}=State) -> + case req(Proc, {match_init, State}) of + '$end_of_table'=Reply -> + Reply; + {cont, {Bins, NewState}} -> + MP = NewState#dets_cont.match_program, + case catch do_foldl_bins(Bins, MP) of + {'EXIT', _} -> + case ets:is_compiled_ms(MP) of + true -> + Bad = dets_utils:bad_object(chunk_match, Bins), + req(Proc, {corrupt, Bad}); + false -> + badarg + end; + [] -> + chunk_match(NewState); + Terms -> + {Terms, NewState} + end; + Error -> + Error end. do_foldl_bins(Bins, true) -> @@ -1093,7 +1355,9 @@ do_apply_op(Op, From, Head, N) -> {N2, H2} when is_record(H2, head), is_integer(N2) -> open_file_loop(H2, N2); H2 when is_record(H2, head) -> - open_file_loop(H2, N) + open_file_loop(H2, N); + {{more,From1,Op1,N1}, NewHead} -> + do_apply_op(Op1, From1, NewHead, N1) catch exit:normal -> exit(normal); @@ -1362,37 +1626,35 @@ start_auto_save_timer(Head) -> %% lookup requests in parallel. Evalute delete_object, delete and %% insert as well. stream_op(Op, Pid, Pids, Head, N) -> - stream_op(Head, Pids, [], N, Pid, Op, Head#head.fixed). + #head{fixed = Fxd, update_mode = M} = Head, + stream_op(Head, Pids, [], N, Pid, Op, Fxd, M). -stream_loop(Head, Pids, C, N, false = Fxd) -> +stream_loop(Head, Pids, C, N, false = Fxd, M) -> receive ?DETS_CALL(From, Message) -> - stream_op(Head, Pids, C, N, From, Message, Fxd) + stream_op(Head, Pids, C, N, From, Message, Fxd, M) after 0 -> stream_end(Head, Pids, C, N, no_more) end; -stream_loop(Head, Pids, C, N, _Fxd) -> +stream_loop(Head, Pids, C, N, _Fxd, _M) -> stream_end(Head, Pids, C, N, no_more). -stream_op(Head, Pids, C, N, Pid, {lookup_keys,Keys}, Fxd) -> +stream_op(Head, Pids, C, N, Pid, {lookup_keys,Keys}, Fxd, M) -> NC = [{{lookup,Pid},Keys} | C], - stream_loop(Head, Pids, NC, N, Fxd); -stream_op(Head, Pids, C, N, Pid, {insert, _Objects} = Op, Fxd) -> - NC = [Op | C], - stream_loop(Head, [Pid | Pids], NC, N, Fxd); -stream_op(Head, Pids, C, N, Pid, {insert_new, _Objects} = Op, Fxd) -> + stream_loop(Head, Pids, NC, N, Fxd, M); +stream_op(Head, Pids, C, N, Pid, {insert, _Objects} = Op, Fxd, dirty = M) -> NC = [Op | C], - stream_loop(Head, [Pid | Pids], NC, N, Fxd); -stream_op(Head, Pids, C, N, Pid, {delete_key, _Keys} = Op, Fxd) -> + stream_loop(Head, [Pid | Pids], NC, N, Fxd, M); +stream_op(Head, Pids, C, N, Pid, {delete_key, _Keys} = Op, Fxd, dirty = M) -> NC = [Op | C], - stream_loop(Head, [Pid | Pids], NC, N, Fxd); -stream_op(Head, Pids, C, N, Pid, {delete_object, _Objects} = Op, Fxd) -> + stream_loop(Head, [Pid | Pids], NC, N, Fxd, M); +stream_op(Head, Pids, C, N, Pid, {delete_object, _Os} = Op, Fxd, dirty = M) -> NC = [Op | C], - stream_loop(Head, [Pid | Pids], NC, N, Fxd); -stream_op(Head, Pids, C, N, Pid, {member, Key}, Fxd) -> + stream_loop(Head, [Pid | Pids], NC, N, Fxd, M); +stream_op(Head, Pids, C, N, Pid, {member, Key}, Fxd, M) -> NC = [{{lookup,[Pid]},[Key]} | C], - stream_loop(Head, Pids, NC, N, Fxd); -stream_op(Head, Pids, C, N, Pid, Op, _Fxd) -> + stream_loop(Head, Pids, NC, N, Fxd, M); +stream_op(Head, Pids, C, N, Pid, Op, _Fxd, _M) -> stream_end(Head, Pids, C, N, {Pid,Op}). stream_end(Head, Pids0, C, N, Next) -> @@ -1437,7 +1699,7 @@ stream_end2([], Ps, no_more, N, C, Head, _Reply) -> penalty(Head, Ps, C), {N, Head}; stream_end2([], _Ps, {From, Op}, N, _C, Head, _Reply) -> - apply_op(Op, From, Head, N). + {{more,From,Op,N},Head}. penalty(H, _Ps, _C) when H#head.fixed =:= false -> ok; @@ -1577,13 +1839,18 @@ do_bchunk_init(Head, Tab) -> L = dets_utils:all_allocated(H2), C0 = #dets_cont{no_objs = default, bin = <<>>, alloc = L}, BinParms = term_to_binary(Parms), - {H2, {C0#dets_cont{tab = Tab, what = bchunk}, [BinParms]}} + {H2, {C0#dets_cont{tab = Tab, proc = self(),what = bchunk}, + [BinParms]}} end; {NewHead, _} = HeadError when is_record(NewHead, head) -> HeadError end. %% -> {NewHead, {cont(), [binary()]}} | {NewHead, Error} +do_bchunk(Head, #dets_cont{proc = Proc}) when Proc =/= self() -> + {Head, badarg}; +do_bchunk(Head, #dets_cont{bin = eof}) -> + {Head, '$end_of_table'}; do_bchunk(Head, State) -> case dets_v9:read_bchunks(Head, State#dets_cont.alloc) of {error, Reason} -> @@ -1953,6 +2220,8 @@ flookup_keys(Head, Keys) -> end. %% -> {NewHead, Result} +fmatch_init(Head, #dets_cont{bin = eof}) -> + {Head, '$end_of_table'}; fmatch_init(Head, C) -> case scan(Head, C) of {scan_error, Reason} -> diff --git a/lib/stdlib/src/dets.hrl b/lib/stdlib/src/dets.hrl index 6e59770753..fbffc9d008 100644 --- a/lib/stdlib/src/dets.hrl +++ b/lib/stdlib/src/dets.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2009. All Rights Reserved. +%% Copyright Ericsson AB 2001-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 @@ -18,7 +18,7 @@ %% -define(DEFAULT_MIN_NO_SLOTS, 256). --define(DEFAULT_MAX_NO_SLOTS, 2*1024*1024). +-define(DEFAULT_MAX_NO_SLOTS, 32*1024*1024). -define(DEFAULT_AUTOSAVE, 3). % minutes -define(DEFAULT_CACHE, {3000, 14000}). % {delay,size} in {milliseconds,bytes} diff --git a/lib/stdlib/src/dets_sup.erl b/lib/stdlib/src/dets_sup.erl index 5c6caa787d..8ea2ba9b3f 100644 --- a/lib/stdlib/src/dets_sup.erl +++ b/lib/stdlib/src/dets_sup.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2002-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2002-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(dets_sup). @@ -22,9 +22,16 @@ -export([start_link/0, init/1]). +-spec start_link() -> {'ok', pid()} | 'ignore' | {'error', term()}. + start_link() -> supervisor:start_link({local, dets_sup}, dets_sup, []). +-spec init([]) -> + {'ok', {{'simple_one_for_one', 4, 3600}, + [{'dets', {'dets', 'istart_link', []}, + 'temporary', 30000, 'worker', ['dets']}]}}. + init([]) -> SupFlags = {simple_one_for_one, 4, 3600}, Child = {dets, {dets, istart_link, []}, temporary, 30000, worker, [dets]}, diff --git a/lib/stdlib/src/dets_v8.erl b/lib/stdlib/src/dets_v8.erl index 1f9f84cd27..af36958c1c 100644 --- a/lib/stdlib/src/dets_v8.erl +++ b/lib/stdlib/src/dets_v8.erl @@ -1074,6 +1074,8 @@ wl([], _Type, Del, Lookup, I, Objs) -> [{Del, Lookup, Objs} | I]. %% -> {NewHead, ok} | {NewHead, Error} +may_grow(Head, 0, once) -> + {Head, ok}; may_grow(Head, _N, _How) when Head#head.fixed =/= false -> {Head, ok}; may_grow(#head{access = read}=Head, _N, _How) -> diff --git a/lib/stdlib/src/dets_v9.erl b/lib/stdlib/src/dets_v9.erl index 53238e962f..132af01f79 100644 --- a/lib/stdlib/src/dets_v9.erl +++ b/lib/stdlib/src/dets_v9.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2009. All Rights Reserved. +%% Copyright Ericsson AB 2001-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 @@ -1908,6 +1908,9 @@ write_cache(Head) -> end. %% -> {NewHead, ok} | {NewHead, Error} +may_grow(Head, 0, once) -> + %% Do not re-hash if there is a chance that the file is not dirty. + {Head, ok}; may_grow(Head, _N, _How) when Head#head.fixed =/= false -> {Head, ok}; may_grow(#head{access = read}=Head, _N, _How) -> diff --git a/lib/stdlib/src/dict.erl b/lib/stdlib/src/dict.erl index 7e51141098..2e9eba4bfa 100644 --- a/lib/stdlib/src/dict.erl +++ b/lib/stdlib/src/dict.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2009. All Rights Reserved. +%% Copyright Ericsson AB 2000-2011. 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 @@ -80,7 +80,9 @@ new() -> Empty = mk_seg(?seg_size), #dict{empty=Empty,segs={Empty}}. --spec is_key(term(), dict()) -> boolean(). +-spec is_key(Key, Dict) -> boolean() when + Key :: term(), + Dict :: dict(). is_key(Key, D) -> Slot = get_slot(D, Key), @@ -91,21 +93,29 @@ find_key(K, [?kv(K,_Val)|_]) -> true; find_key(K, [_|Bkt]) -> find_key(K, Bkt); find_key(_, []) -> false. --spec to_list(dict()) -> [{term(), term()}]. +-spec to_list(Dict) -> List when + Dict :: dict(), + List :: [{Key :: term(), Value :: term()}]. to_list(D) -> fold(fun (Key, Val, List) -> [{Key,Val}|List] end, [], D). --spec from_list([{term(), term()}]) -> dict(). +-spec from_list(List) -> Dict when + List :: [{Key :: term(), Value :: term()}], + Dict :: dict(). from_list(L) -> lists:foldl(fun ({K,V}, D) -> store(K, V, D) end, new(), L). --spec size(dict()) -> non_neg_integer(). +-spec size(Dict) -> non_neg_integer() when + Dict :: dict(). size(#dict{size=N}) when is_integer(N), N >= 0 -> N. --spec fetch(term(), dict()) -> term(). +-spec fetch(Key, Dict) -> Value when + Key :: term(), + Dict :: dict(), + Value :: term(). fetch(Key, D) -> Slot = get_slot(D, Key), @@ -119,7 +129,10 @@ fetch_val(K, [?kv(K,Val)|_]) -> Val; fetch_val(K, [_|Bkt]) -> fetch_val(K, Bkt); fetch_val(_, []) -> throw(badarg). --spec find(term(), dict()) -> {'ok', term()} | 'error'. +-spec find(Key, Dict) -> {'ok', Value} | 'error' when + Key :: term(), + Dict :: dict(), + Value :: term(). find(Key, D) -> Slot = get_slot(D, Key), @@ -130,12 +143,17 @@ find_val(K, [?kv(K,Val)|_]) -> {ok,Val}; find_val(K, [_|Bkt]) -> find_val(K, Bkt); find_val(_, []) -> error. --spec fetch_keys(dict()) -> [term()]. +-spec fetch_keys(Dict) -> Keys when + Dict :: dict(), + Keys :: [term()]. fetch_keys(D) -> fold(fun (Key, _Val, Keys) -> [Key|Keys] end, [], D). --spec erase(term(), dict()) -> dict(). +-spec erase(Key, Dict1) -> Dict2 when + Key :: term(), + Dict1 :: dict(), + Dict2 :: dict(). %% Erase all elements with key Key. erase(Key, D0) -> @@ -150,7 +168,11 @@ erase_key(Key, [E|Bkt0]) -> {[E|Bkt1],Dc}; erase_key(_, []) -> {[],0}. --spec store(term(), term(), dict()) -> dict(). +-spec store(Key, Value, Dict1) -> Dict2 when + Key :: term(), + Value :: term(), + Dict1 :: dict(), + Dict2 :: dict(). store(Key, Val, D0) -> Slot = get_slot(D0, Key), @@ -166,7 +188,11 @@ store_bkt_val(Key, New, [Other|Bkt0]) -> {[Other|Bkt1],Ic}; store_bkt_val(Key, New, []) -> {[?kv(Key,New)],1}. --spec append(term(), term(), dict()) -> dict(). +-spec append(Key, Value, Dict1) -> Dict2 when + Key :: term(), + Value :: term(), + Dict1 :: dict(), + Dict2 :: dict(). append(Key, Val, D0) -> Slot = get_slot(D0, Key), @@ -182,7 +208,11 @@ append_bkt(Key, Val, [Other|Bkt0]) -> {[Other|Bkt1],Ic}; append_bkt(Key, Val, []) -> {[?kv(Key,[Val])],1}. --spec append_list(term(), [term()], dict()) -> dict(). +-spec append_list(Key, ValList, Dict1) -> Dict2 when + Key :: term(), + ValList :: [Value :: term()], + Dict1 :: dict(), + Dict2 :: dict(). append_list(Key, L, D0) -> Slot = get_slot(D0, Key), @@ -252,7 +282,11 @@ app_list_bkt(Key, L, []) -> {[?kv(Key,L)],1}. %% {Bkt1,Dc} = on_key_bkt(Key, F, Bkt0), %% {[Other|Bkt1],Dc}. --spec update(term(), fun((term()) -> term()), dict()) -> dict(). +-spec update(Key, Fun, Dict1) -> Dict2 when + Key :: term(), + Fun :: fun((Value1 :: term()) -> Value2 :: term()), + Dict1 :: dict(), + Dict2 :: dict(). update(Key, F, D0) -> Slot = get_slot(D0, Key), @@ -271,7 +305,12 @@ update_bkt(Key, F, [Other|Bkt0]) -> update_bkt(_Key, _F, []) -> throw(badarg). --spec update(term(), fun((term()) -> term()), term(), dict()) -> dict(). +-spec update(Key, Fun, Initial, Dict1) -> Dict2 when + Key :: term(), + Initial :: term(), + Fun :: fun((Value1 :: term()) -> Value2 :: term()), + Dict1 :: dict(), + Dict2 :: dict(). update(Key, F, Init, D0) -> Slot = get_slot(D0, Key), @@ -286,7 +325,11 @@ update_bkt(Key, F, I, [Other|Bkt0]) -> {[Other|Bkt1],Ic}; update_bkt(Key, F, I, []) when is_function(F, 1) -> {[?kv(Key,I)],1}. --spec update_counter(term(), number(), dict()) -> dict(). +-spec update_counter(Key, Increment, Dict1) -> Dict2 when + Key :: term(), + Increment :: number(), + Dict1 :: dict(), + Dict2 :: dict(). update_counter(Key, Incr, D0) when is_number(Incr) -> Slot = get_slot(D0, Key), @@ -301,20 +344,38 @@ counter_bkt(Key, I, [Other|Bkt0]) -> {[Other|Bkt1],Ic}; counter_bkt(Key, I, []) -> {[?kv(Key,I)],1}. --spec fold(fun((term(), term(), term()) -> term()), term(), dict()) -> term(). +-spec fold(Fun, Acc0, Dict) -> Acc1 when + Fun :: fun((Key, Value, AccIn) -> AccOut), + Key :: term(), + Value :: term(), + Acc0 :: term(), + Acc1 :: term(), + AccIn :: term(), + AccOut :: term(), + Dict :: dict(). %% Fold function Fun over all "bags" in Table and return Accumulator. fold(F, Acc, D) -> fold_dict(F, Acc, D). --spec map(fun((term(), term()) -> term()), dict()) -> dict(). +-spec map(Fun, Dict1) -> Dict2 when + Fun :: fun((Key :: term(), Value1 :: term()) -> Value2 :: term()), + Dict1 :: dict(), + Dict2 :: dict(). map(F, D) -> map_dict(F, D). --spec filter(fun((term(), term()) -> boolean()), dict()) -> dict(). +-spec filter(Pred, Dict1) -> Dict2 when + Pred :: fun((Key :: term(), Value :: term()) -> boolean()), + Dict1 :: dict(), + Dict2 :: dict(). filter(F, D) -> filter_dict(F, D). --spec merge(fun((term(), term(), term()) -> term()), dict(), dict()) -> dict(). +-spec merge(Fun, Dict1, Dict2) -> Dict3 when + Fun :: fun((Key :: term(), Value1 :: term(), Value2 :: term()) -> Value :: term()), + Dict1 :: dict(), + Dict2 :: dict(), + Dict3 :: dict(). merge(F, D1, D2) when D1#dict.size < D2#dict.size -> fold_dict(fun (K, V1, D) -> diff --git a/lib/stdlib/src/digraph.erl b/lib/stdlib/src/digraph.erl index 9bdea671a9..e3f87d2c57 100644 --- a/lib/stdlib/src/digraph.erl +++ b/lib/stdlib/src/digraph.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1996-2011. 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(digraph). @@ -36,6 +36,8 @@ -export([get_short_path/3, get_short_cycle/2]). +-export_type([digraph/0, d_type/0, vertex/0]). + -record(digraph, {vtab = notable :: ets:tab(), etab = notable :: ets:tab(), ntab = notable :: ets:tab(), @@ -51,7 +53,8 @@ -type label() :: term(). -type vertex() :: term(). --type add_edge_err_rsn() :: {'bad_edge', [vertex()]} | {'bad_vertex', vertex()}. +-type add_edge_err_rsn() :: {'bad_edge', Path :: [vertex()]} + | {'bad_vertex', V :: vertex()}. %% %% Type is a list of @@ -68,7 +71,8 @@ new() -> new([]). --spec new([d_type()]) -> digraph(). +-spec new(Type) -> digraph() when + Type :: [d_type()]. new(Type) -> case check_type(Type, protected, []) of @@ -111,16 +115,20 @@ set_type([], G) -> G. %% Data access functions --spec delete(digraph()) -> 'true'. +-spec delete(G) -> 'true' when + G :: digraph(). delete(G) -> ets:delete(G#digraph.vtab), ets:delete(G#digraph.etab), ets:delete(G#digraph.ntab). --spec info(digraph()) -> [{'cyclicity', d_cyclicity()} | - {'memory', non_neg_integer()} | - {'protection', d_protection()}]. +-spec info(G) -> InfoList when + G :: digraph(), + InfoList :: [{'cyclicity', Cyclicity :: d_cyclicity()} | + {'memory', NoWords :: non_neg_integer()} | + {'protection', Protection :: d_protection()}]. + info(G) -> VT = G#digraph.vtab, ET = G#digraph.etab, @@ -133,32 +141,45 @@ info(G) -> Memory = ets:info(VT, memory) + ets:info(ET, memory) + ets:info(NT, memory), [{cyclicity, Cyclicity}, {memory, Memory}, {protection, Protection}]. --spec add_vertex(digraph()) -> vertex(). +-spec add_vertex(G) -> vertex() when + G :: digraph(). add_vertex(G) -> do_add_vertex({new_vertex_id(G), []}, G). --spec add_vertex(digraph(), vertex()) -> vertex(). +-spec add_vertex(G, V) -> vertex() when + G :: digraph(), + V :: vertex(). add_vertex(G, V) -> do_add_vertex({V, []}, G). --spec add_vertex(digraph(), vertex(), label()) -> vertex(). +-spec add_vertex(G, V, Label) -> vertex() when + G :: digraph(), + V :: vertex(), + Label :: label(). add_vertex(G, V, D) -> do_add_vertex({V, D}, G). --spec del_vertex(digraph(), vertex()) -> 'true'. +-spec del_vertex(G, V) -> 'true' when + G :: digraph(), + V :: vertex(). del_vertex(G, V) -> do_del_vertex(V, G). --spec del_vertices(digraph(), [vertex()]) -> 'true'. +-spec del_vertices(G, Vertices) -> 'true' when + G :: digraph(), + Vertices :: [vertex()]. del_vertices(G, Vs) -> do_del_vertices(Vs, G). --spec vertex(digraph(), vertex()) -> {vertex(), label()} | 'false'. +-spec vertex(G, V) -> {V, Label} | 'false' when + G :: digraph(), + V :: vertex(), + Label :: label(). vertex(G, V) -> case ets:lookup(G#digraph.vtab, V) of @@ -166,12 +187,15 @@ vertex(G, V) -> [Vertex] -> Vertex end. --spec no_vertices(digraph()) -> non_neg_integer(). +-spec no_vertices(G) -> non_neg_integer() when + G :: digraph(). no_vertices(G) -> ets:info(G#digraph.vtab, size). --spec vertices(digraph()) -> [vertex()]. +-spec vertices(G) -> Vertices when + G :: digraph(), + Vertices :: [vertex()]. vertices(G) -> ets:select(G#digraph.vtab, [{{'$1', '_'}, [], ['$1']}]). @@ -186,85 +210,125 @@ source_vertices(G) -> sink_vertices(G) -> collect_vertices(G, out). --spec in_degree(digraph(), vertex()) -> non_neg_integer(). +-spec in_degree(G, V) -> non_neg_integer() when + G :: digraph(), + V :: vertex(). in_degree(G, V) -> length(ets:lookup(G#digraph.ntab, {in, V})). --spec in_neighbours(digraph(), vertex()) -> [vertex()]. +-spec in_neighbours(G, V) -> Vertex when + G :: digraph(), + V :: vertex(), + Vertex :: [vertex()]. in_neighbours(G, V) -> ET = G#digraph.etab, NT = G#digraph.ntab, collect_elems(ets:lookup(NT, {in, V}), ET, 2). --spec in_edges(digraph(), vertex()) -> [edge()]. +-spec in_edges(G, V) -> Edges when + G :: digraph(), + V :: vertex(), + Edges :: [edge()]. in_edges(G, V) -> ets:select(G#digraph.ntab, [{{{in, V}, '$1'}, [], ['$1']}]). --spec out_degree(digraph(), vertex()) -> non_neg_integer(). +-spec out_degree(G, V) -> non_neg_integer() when + G :: digraph(), + V :: vertex(). out_degree(G, V) -> length(ets:lookup(G#digraph.ntab, {out, V})). --spec out_neighbours(digraph(), vertex()) -> [vertex()]. +-spec out_neighbours(G, V) -> Vertices when + G :: digraph(), + V :: vertex(), + Vertices :: [vertex()]. out_neighbours(G, V) -> ET = G#digraph.etab, NT = G#digraph.ntab, collect_elems(ets:lookup(NT, {out, V}), ET, 3). --spec out_edges(digraph(), vertex()) -> [edge()]. +-spec out_edges(G, V) -> Edges when + G :: digraph(), + V :: vertex(), + Edges :: [edge()]. out_edges(G, V) -> ets:select(G#digraph.ntab, [{{{out, V}, '$1'}, [], ['$1']}]). --spec add_edge(digraph(), vertex(), vertex()) -> - edge() | {'error', add_edge_err_rsn()}. +-spec add_edge(G, V1, V2) -> edge() | {'error', add_edge_err_rsn()} when + G :: digraph(), + V1 :: vertex(), + V2 :: vertex(). add_edge(G, V1, V2) -> do_add_edge({new_edge_id(G), V1, V2, []}, G). --spec add_edge(digraph(), vertex(), vertex(), label()) -> - edge() | {'error', add_edge_err_rsn()}. +-spec add_edge(G, V1, V2, Label) -> edge() | {'error', add_edge_err_rsn()} when + G :: digraph(), + V1 :: vertex(), + V2 :: vertex(), + Label :: label(). add_edge(G, V1, V2, D) -> do_add_edge({new_edge_id(G), V1, V2, D}, G). --spec add_edge(digraph(), edge(), vertex(), vertex(), label()) -> - edge() | {'error', add_edge_err_rsn()}. +-spec add_edge(G, E, V1, V2, Label) -> edge() | {'error', add_edge_err_rsn()} when + G :: digraph(), + E :: edge(), + V1 :: vertex(), + V2 :: vertex(), + Label :: label(). add_edge(G, E, V1, V2, D) -> do_add_edge({E, V1, V2, D}, G). --spec del_edge(digraph(), edge()) -> 'true'. +-spec del_edge(G, E) -> 'true' when + G :: digraph(), + E :: edge(). del_edge(G, E) -> do_del_edges([E], G). --spec del_edges(digraph(), [edge()]) -> 'true'. +-spec del_edges(G, Edges) -> 'true' when + G :: digraph(), + Edges :: [edge()]. del_edges(G, Es) -> do_del_edges(Es, G). --spec no_edges(digraph()) -> non_neg_integer(). +-spec no_edges(G) -> non_neg_integer() when + G :: digraph(). no_edges(G) -> ets:info(G#digraph.etab, size). --spec edges(digraph()) -> [edge()]. +-spec edges(G) -> Edges when + G :: digraph(), + Edges :: [edge()]. edges(G) -> ets:select(G#digraph.etab, [{{'$1', '_', '_', '_'}, [], ['$1']}]). --spec edges(digraph(), vertex()) -> [edge()]. +-spec edges(G, V) -> Edges when + G :: digraph(), + V :: vertex(), + Edges :: [edge()]. edges(G, V) -> ets:select(G#digraph.ntab, [{{{out, V},'$1'}, [], ['$1']}, {{{in, V}, '$1'}, [], ['$1']}]). --spec edge(digraph(), edge()) -> {edge(),vertex(),vertex(),label()} | 'false'. +-spec edge(G, E) -> {E, V1, V2, Label} | 'false' when + G :: digraph(), + E :: edge(), + V1 :: vertex(), + V2 :: vertex(), + Label :: label(). edge(G, E) -> case ets:lookup(G#digraph.etab,E) of @@ -275,7 +339,7 @@ edge(G, E) -> %% %% Generate a "unique" edge identifier (relative to this graph) %% --spec new_edge_id(digraph()) -> nonempty_improper_list('$e', non_neg_integer()). +-spec new_edge_id(digraph()) -> edge(). new_edge_id(G) -> NT = G#digraph.ntab, @@ -287,7 +351,7 @@ new_edge_id(G) -> %% %% Generate a "unique" vertex identifier (relative to this graph) %% --spec new_vertex_id(digraph()) -> nonempty_improper_list('$v', non_neg_integer()). +-spec new_vertex_id(digraph()) -> vertex(). new_vertex_id(G) -> NT = G#digraph.ntab, @@ -442,7 +506,10 @@ acyclic_add_edge(E, V1, V2, Label, G) -> %% Delete all paths from vertex V1 to vertex V2 %% --spec del_path(digraph(), vertex(), vertex()) -> 'true'. +-spec del_path(G, V1, V2) -> 'true' when + G :: digraph(), + V1 :: vertex(), + V2 :: vertex(). del_path(G, V1, V2) -> case get_path(G, V1, V2) of @@ -461,7 +528,10 @@ del_path(G, V1, V2) -> %% been searched. %% --spec get_cycle(digraph(), vertex()) -> [vertex(),...] | 'false'. +-spec get_cycle(G, V) -> Vertices | 'false' when + G :: digraph(), + V :: vertex(), + Vertices :: [vertex(),...]. get_cycle(G, V) -> case one_path(out_neighbours(G, V), V, [], [V], [V], 2, G, 1) of @@ -479,7 +549,11 @@ get_cycle(G, V) -> %% if no path exists false is returned %% --spec get_path(digraph(), vertex(), vertex()) -> [vertex(),...] | 'false'. +-spec get_path(G, V1, V2) -> Vertices | 'false' when + G :: digraph(), + V1 :: vertex(), + V2 :: vertex(), + Vertices :: [vertex(),...]. get_path(G, V1, V2) -> one_path(out_neighbours(G, V1), V2, [], [V1], [V1], 1, G, 1). @@ -514,7 +588,10 @@ one_path([], _, [], _, _, _, _, _Counter) -> false. %% Like get_cycle/2, but a cycle of length one is preferred. %% --spec get_short_cycle(digraph(), vertex()) -> [vertex(),...] | 'false'. +-spec get_short_cycle(G, V) -> Vertices | 'false' when + G :: digraph(), + V :: vertex(), + Vertices :: [vertex(),...]. get_short_cycle(G, V) -> get_short_path(G, V, V). @@ -524,7 +601,11 @@ get_short_cycle(G, V) -> %% to find a short path. %% --spec get_short_path(digraph(), vertex(), vertex()) -> [vertex(),...] | 'false'. +-spec get_short_path(G, V1, V2) -> Vertices | 'false' when + G :: digraph(), + V1 :: vertex(), + V2 :: vertex(), + Vertices :: [vertex(),...]. get_short_path(G, V1, V2) -> T = new(), diff --git a/lib/stdlib/src/digraph_utils.erl b/lib/stdlib/src/digraph_utils.erl index 080cae4742..e221be15a1 100644 --- a/lib/stdlib/src/digraph_utils.erl +++ b/lib/stdlib/src/digraph_utils.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% Copyright Ericsson AB 1999-2011. 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,48 +38,66 @@ %% A convenient type alias %% --type vertices() :: [digraph:vertex()]. - %% %% Exported functions %% --spec components(digraph()) -> vertices(). +-spec components(Digraph) -> [Component] when + Digraph :: digraph(), + Component :: [digraph:vertex()]. components(G) -> forest(G, fun inout/3). --spec strong_components(digraph()) -> vertices(). +-spec strong_components(Digraph) -> [StrongComponent] when + Digraph :: digraph(), + StrongComponent :: [digraph:vertex()]. strong_components(G) -> forest(G, fun in/3, revpostorder(G)). --spec cyclic_strong_components(digraph()) -> vertices(). +-spec cyclic_strong_components(Digraph) -> [StrongComponent] when + Digraph :: digraph(), + StrongComponent :: [digraph:vertex()]. cyclic_strong_components(G) -> remove_singletons(strong_components(G), G, []). --spec reachable(vertices(), digraph()) -> vertices(). +-spec reachable(Vertices, Digraph) -> Reachable when + Digraph :: digraph(), + Vertices :: [digraph:vertex()], + Reachable :: [digraph:vertex()]. reachable(Vs, G) when is_list(Vs) -> lists:append(forest(G, fun out/3, Vs, first)). --spec reachable_neighbours(vertices(), digraph()) -> vertices(). +-spec reachable_neighbours(Vertices, Digraph) -> Reachable when + Digraph :: digraph(), + Vertices :: [digraph:vertex()], + Reachable :: [digraph:vertex()]. reachable_neighbours(Vs, G) when is_list(Vs) -> lists:append(forest(G, fun out/3, Vs, not_first)). --spec reaching(vertices(), digraph()) -> vertices(). +-spec reaching(Vertices, Digraph) -> Reaching when + Digraph :: digraph(), + Vertices :: [digraph:vertex()], + Reaching :: [digraph:vertex()]. reaching(Vs, G) when is_list(Vs) -> lists:append(forest(G, fun in/3, Vs, first)). --spec reaching_neighbours(vertices(), digraph()) -> vertices(). +-spec reaching_neighbours(Vertices, Digraph) -> Reaching when + Digraph :: digraph(), + Vertices :: [digraph:vertex()], + Reaching :: [digraph:vertex()]. reaching_neighbours(Vs, G) when is_list(Vs) -> lists:append(forest(G, fun in/3, Vs, not_first)). --spec topsort(digraph()) -> vertices() | 'false'. +-spec topsort(Digraph) -> Vertices | 'false' when + Digraph :: digraph(), + Vertices :: [digraph:vertex()]. topsort(G) -> L = revpostorder(G), @@ -88,12 +106,15 @@ topsort(G) -> false -> false end. --spec is_acyclic(digraph()) -> boolean(). +-spec is_acyclic(Digraph) -> boolean() when + Digraph :: digraph(). is_acyclic(G) -> loop_vertices(G) =:= [] andalso topsort(G) =/= false. --spec arborescence_root(digraph()) -> 'no' | {'yes', digraph:vertex()}. +-spec arborescence_root(Digraph) -> 'no' | {'yes', Root} when + Digraph :: digraph(), + Root :: digraph:vertex(). arborescence_root(G) -> case digraph:no_edges(G) =:= digraph:no_vertices(G) - 1 of @@ -114,23 +135,30 @@ arborescence_root(G) -> no end. --spec is_arborescence(digraph()) -> boolean(). +-spec is_arborescence(Digraph) -> boolean() when + Digraph :: digraph(). is_arborescence(G) -> arborescence_root(G) =/= no. --spec is_tree(digraph()) -> boolean(). +-spec is_tree(Digraph) -> boolean() when + Digraph :: digraph(). is_tree(G) -> (digraph:no_edges(G) =:= digraph:no_vertices(G) - 1) andalso (length(components(G)) =:= 1). --spec loop_vertices(digraph()) -> vertices(). +-spec loop_vertices(Digraph) -> Vertices when + Digraph :: digraph(), + Vertices :: [digraph:vertex()]. loop_vertices(G) -> [V || V <- digraph:vertices(G), is_reflexive_vertex(V, G)]. --spec subgraph(digraph(), vertices()) -> digraph(). +-spec subgraph(Digraph, Vertices) -> SubGraph when + Digraph :: digraph(), + Vertices :: [digraph:vertex()], + SubGraph :: digraph(). subgraph(G, Vs) -> try @@ -140,10 +168,12 @@ subgraph(G, Vs) -> erlang:error(badarg) end. --type option() :: {'type', 'inherit' | [digraph:d_type()]} - | {'keep_labels', boolean()}. - --spec subgraph(digraph(), vertices(), [option()]) -> digraph(). +-spec subgraph(Digraph, Vertices, Options) -> SubGraph when + Digraph :: digraph(), + SubGraph :: digraph(), + Vertices :: [digraph:vertex()], + Options :: [{'type', SubgraphType} | {'keep_labels', boolean()}], + SubgraphType :: 'inherit' | [digraph:d_type()]. subgraph(G, Vs, Opts) -> try @@ -153,7 +183,9 @@ subgraph(G, Vs, Opts) -> erlang:error(badarg) end. --spec condensation(digraph()) -> digraph(). +-spec condensation(Digraph) -> CondensedDigraph when + Digraph :: digraph(), + CondensedDigraph :: digraph(). condensation(G) -> SCs = strong_components(G), @@ -176,12 +208,16 @@ condensation(G) -> ets:delete(I2C), SCG. --spec preorder(digraph()) -> vertices(). +-spec preorder(Digraph) -> Vertices when + Digraph :: digraph(), + Vertices :: [digraph:vertex()]. preorder(G) -> lists:reverse(revpreorder(G)). --spec postorder(digraph()) -> vertices(). +-spec postorder(Digraph) -> Vertices when + Digraph :: digraph(), + Vertices :: [digraph:vertex()]. postorder(G) -> lists:reverse(revpostorder(G)). diff --git a/lib/stdlib/src/edlin.erl b/lib/stdlib/src/edlin.erl index 6cb441dbed..026bd9038f 100644 --- a/lib/stdlib/src/edlin.erl +++ b/lib/stdlib/src/edlin.erl @@ -24,6 +24,7 @@ -export([init/0,start/1,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([expand/1]). -export([edit_line1/2]). @@ -421,6 +422,7 @@ over_paren_auto([], _, _, _) -> %% length_before(Line) %% length_after(Line) %% prompt(Line) +%% current_line(Line) %% Various functions for accessing bits of a line. erase_line({line,Pbs,{Bef,Aft},_}) -> @@ -447,6 +449,9 @@ length_after({line,_,{_Bef,Aft},_}) -> prompt({line,Pbs,_,_}) -> Pbs. +current_line({line,_,{Bef, Aft},_}) -> + reverse(Bef, Aft ++ "\n"). + %% %% 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 424aed3d2e..d804c1dee5 100644 --- a/lib/stdlib/src/epp.erl +++ b/lib/stdlib/src/epp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. 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,11 +29,14 @@ %%------------------------------------------------------------------------ -type macros() :: [{atom(), term()}]. +-type epp_handle() :: pid(). %% Epp state record. -record(epp, {file, %Current file location, %Current location + delta, %Offset from Location (-file) name="", %Current file name + name2="", %-"-, modified by -file istk=[], %Ifdef stack sstk=[], %State stack path=[], %Include-path @@ -59,14 +62,23 @@ %% parse_file(FileName, IncludePath, PreDefMacros) %% macro_defs(Epp) --spec open(file:name(), [file:name()]) -> - {'ok', pid()} | {'error', term()}. +-spec open(FileName, IncludePath) -> + {'ok', Epp} | {'error', ErrorDescriptor} when + FileName :: file:name(), + IncludePath :: [DirectoryName :: file:name()], + Epp :: epp_handle(), + ErrorDescriptor :: term(). open(Name, Path) -> open(Name, Path, []). --spec open(file:name(), [file:name()], macros()) -> - {'ok', pid()} | {'error', term()}. +-spec open(FileName, IncludePath, PredefMacros) -> + {'ok', Epp} | {'error', ErrorDescriptor} when + FileName :: file:name(), + IncludePath :: [DirectoryName :: file:name()], + PredefMacros :: macros(), + Epp :: epp_handle(), + ErrorDescriptor :: term(). open(Name, Path, Pdm) -> Self = self(), @@ -78,7 +90,8 @@ open(Name, File, StartLocation, Path, Pdm) -> Epp = spawn(fun() -> server(Self, Name, File, StartLocation,Path,Pdm) end), epp_request(Epp). --spec close(pid()) -> 'ok'. +-spec close(Epp) -> 'ok' when + Epp :: epp_handle(). close(Epp) -> %% Make sure that close is synchronous as a courtesy to test @@ -91,6 +104,13 @@ close(Epp) -> scan_erl_form(Epp) -> epp_request(Epp, scan_erl_form). +-spec parse_erl_form(Epp) -> + {'ok', AbsForm} | {'eof', Line} | {error, ErrorInfo} when + Epp :: epp_handle(), + AbsForm :: erl_parse:abstract_form(), + Line :: erl_scan:line(), + ErrorInfo :: erl_scan:error_info() | erl_parse:error_info(). + parse_erl_form(Epp) -> case epp_request(Epp, scan_erl_form) of {ok,Toks} -> @@ -105,10 +125,17 @@ macro_defs(Epp) -> %% format_error(ErrorDescriptor) -> String %% Return a string describing the error. +-spec format_error(ErrorDescriptor) -> io_lib:chars() when + ErrorDescriptor :: term(). + format_error(cannot_parse) -> io_lib:format("cannot parse file, giving up", []); format_error({bad,W}) -> io_lib:format("badly formed '~s'", [W]); +format_error(missing_parenthesis) -> + io_lib:format("badly formed define: missing closing right parenthesis",[]); +format_error(premature_end) -> + "premature end"; format_error({call,What}) -> io_lib:format("illegal macro call '~s'",[What]); format_error({undefined,M,none}) -> @@ -140,6 +167,16 @@ format_error(E) -> file:format_error(E). %% parse_file(FileName, IncludePath, [PreDefMacro]) -> %% {ok,[Form]} | {error,OpenError} +-spec parse_file(FileName, IncludePath, PredefMacros) -> + {'ok', [Form]} | {error, OpenError} when + FileName :: file:name(), + IncludePath :: [DirectoryName :: file:name()], + Form :: erl_parse:abstract_form() | {'error', ErrorInfo} | {'eof',Line}, + PredefMacros :: macros(), + Line :: erl_scan:line(), + ErrorInfo :: erl_scan:error_info() | erl_parse:error_info(), + OpenError :: file:posix() | badarg | system_limit. + parse_file(Ifile, Path, Predefs) -> case open(Ifile, Path, Predefs) of {ok,Epp} -> @@ -161,7 +198,7 @@ parse_file(Epp) -> case normalize_typed_record_fields(Fields) of {typed, NewFields} -> [{attribute, La, record, {Record, NewFields}}, - {attribute, La, type, + {attribute, La, type, {{record, Record}, Fields, []}} |parse_file(Epp)]; not_typed -> @@ -176,6 +213,8 @@ parse_file(Epp) -> [{eof,Location}] end. +normalize_typed_record_fields([]) -> + {typed, []}; normalize_typed_record_fields(Fields) -> normalize_typed_record_fields(Fields, [], false). @@ -184,7 +223,7 @@ normalize_typed_record_fields([], NewFields, Typed) -> true -> {typed, lists:reverse(NewFields)}; false -> not_typed end; -normalize_typed_record_fields([{typed_record_field,Field,_}|Rest], +normalize_typed_record_fields([{typed_record_field,Field,_}|Rest], NewFields, _Typed) -> normalize_typed_record_fields(Rest, [Field|NewFields], true); normalize_typed_record_fields([Field|Rest], NewFields, Typed) -> @@ -228,8 +267,8 @@ init_server(Pid, Name, File, AtLocation, Path, Pdm, Pre) -> case user_predef(Pdm, Ms0) of {ok,Ms1} -> epp_reply(Pid, {ok,self()}), - St = #epp{file=File, location=AtLocation, name=Name, - path=Path, macs=Ms1, pre_opened = Pre}, + St = #epp{file=File, location=AtLocation, delta=0, name=Name, + name2=Name, path=Path, macs=Ms1, pre_opened = Pre}, From = wait_request(St), enter_file_reply(From, Name, AtLocation, AtLocation), wait_req_scan(St); @@ -320,7 +359,7 @@ wait_req_scan(St) -> wait_req_skip(St, Sis) -> From = wait_request(St), skip_toks(From, St, Sis). - + %% enter_file(Path, FileName, IncludeToken, From, EppState) %% leave_file(From, EppState) %% Handle entering and leaving included files. Notify caller when the @@ -352,8 +391,8 @@ enter_file2(NewF, Pname, From, St, AtLocation, ExtraPath) -> enter_file_reply(From, Pname, Loc, AtLocation), Ms = dict:store({atom,'FILE'}, {none,[{string,Loc,Pname}]}, St#epp.macs), Path = St#epp.path ++ ExtraPath, - #epp{location=Loc,file=NewF, - name=Pname,sstk=[St|St#epp.sstk],path=Path,macs=Ms}. + #epp{file=NewF,location=Loc,name=Pname,delta=0, + sstk=[St|St#epp.sstk],path=Path,macs=Ms}. enter_file_reply(From, Name, Location, AtLocation) -> Attr = loc_attr(AtLocation), @@ -376,23 +415,32 @@ file_name(N) when is_atom(N) -> leave_file(From, St) -> case St#epp.istk of - [I|Cis] -> + [I|Cis] -> epp_reply(From, - {error,{St#epp.location,epp, + {error,{St#epp.location,epp, {illegal,"unterminated",I}}}), leave_file(wait_request(St),St#epp{istk=Cis}); [] -> case St#epp.sstk of [OldSt|Sts] -> close_file(St), - enter_file_reply(From, OldSt#epp.name, - OldSt#epp.location, OldSt#epp.location), + #epp{location=OldLoc, delta=Delta, name=OldName, + name2=OldName2} = OldSt, + CurrLoc = add_line(OldLoc, Delta), Ms = dict:store({atom,'FILE'}, - {none, - [{string,OldSt#epp.location, - OldSt#epp.name}]}, + {none,[{string,CurrLoc,OldName2}]}, St#epp.macs), - wait_req_scan(OldSt#epp{sstk=Sts,macs=Ms}); + NextSt = OldSt#epp{sstk=Sts,macs=Ms}, + enter_file_reply(From, OldName, CurrLoc, CurrLoc), + case OldName2 =:= OldName of + true -> + From; + false -> + NFrom = wait_request(NextSt), + enter_file_reply(NFrom, OldName2, OldLoc, + neg_line(CurrLoc)) + end, + wait_req_scan(NextSt); [] -> epp_reply(From, {eof,St#epp.location}), wait_req_scan(St) @@ -413,7 +461,7 @@ scan_toks(From, St) -> leave_file(From, St#epp{location=Cl}); {error,_E} -> epp_reply(From, {error,{St#epp.location,epp,cannot_parse}}), - leave_file(From, St) %This serious, just exit! + leave_file(wait_request(St), St) %This serious, just exit! end. scan_toks([{'-',_Lh},{atom,_Ld,define}=Define|Toks], From, St) -> @@ -487,28 +535,34 @@ scan_extends(_Ts, _As, Ms) -> Ms. %% scan_define(Tokens, DefineToken, From, EppState) -scan_define([{'(',_Lp},{Type,_Lm,M}=Mac,{',',_Lc}|Toks], _Def, From, St) +scan_define([{'(',_Lp},{Type,_Lm,M}=Mac,{',',Lc}|Toks], _Def, From, St) when Type =:= atom; Type =:= var -> - case dict:find({atom,M}, St#epp.macs) of - {ok, Defs} when is_list(Defs) -> - %% User defined macros: can be overloaded - case proplists:is_defined(none, Defs) of - true -> - epp_reply(From, {error,{loc(Mac),epp,{redefine,M}}}), + case catch macro_expansion(Toks, Lc) of + Expansion when is_list(Expansion) -> + case dict:find({atom,M}, St#epp.macs) of + {ok, Defs} when is_list(Defs) -> + %% User defined macros: can be overloaded + case proplists:is_defined(none, Defs) of + true -> + epp_reply(From, {error,{loc(Mac),epp,{redefine,M}}}), + wait_req_scan(St); + false -> + scan_define_cont(From, St, + {atom, M}, + {none, {none,Expansion}}) + end; + {ok, _PreDef} -> + %% Predefined macros: cannot be overloaded + epp_reply(From, {error,{loc(Mac),epp,{redefine_predef,M}}}), wait_req_scan(St); - false -> + error -> scan_define_cont(From, St, {atom, M}, - {none, {none,macro_expansion(Toks)}}) + {none, {none,Expansion}}) end; - {ok, _PreDef} -> - %% Predefined macros: cannot be overloaded - epp_reply(From, {error,{loc(Mac),epp,{redefine_predef,M}}}), - wait_req_scan(St); - error -> - scan_define_cont(From, St, - {atom, M}, - {none, {none,macro_expansion(Toks)}}) + {error,ErrL,What} -> + epp_reply(From, {error,{ErrL,epp,What}}), + wait_req_scan(St) end; scan_define([{'(',_Lp},{Type,_Lm,M}=Mac,{'(',_Lc}|Toks], Def, From, St) when Type =:= atom; Type =:= var -> @@ -534,6 +588,9 @@ scan_define([{'(',_Lp},{Type,_Lm,M}=Mac,{'(',_Lc}|Toks], Def, From, St) error -> scan_define_cont(From, St, {atom, M}, {Len, {As, Me}}) end; + {error,ErrL,What} -> + epp_reply(From, {error,{ErrL,epp,What}}), + wait_req_scan(St); _ -> epp_reply(From, {error,{loc(Def),epp,{bad,define}}}), wait_req_scan(St) @@ -595,7 +652,7 @@ scan_undef(_Toks, Undef, From, St) -> %% scan_include(Tokens, IncludeToken, From, St) -scan_include([{'(',_Llp},{string,_Lf,NewName0},{')',_Lrp},{dot,_Ld}], Inc, +scan_include([{'(',_Llp},{string,_Lf,NewName0},{')',_Lrp},{dot,_Ld}], Inc, From, St) -> NewName = expand_var(NewName0), enter_file(St#epp.path, NewName, Inc, From, St); @@ -631,7 +688,7 @@ scan_include_lib([{'(',_Llp},{string,_Lf,NewName0},{')',_Lrp},{dot,_Ld}], case file:open(LibName, [read]) of {ok,NewF} -> ExtraPath = [filename:dirname(LibName)], - wait_req_scan(enter_file2(NewF, LibName, From, + wait_req_scan(enter_file2(NewF, LibName, From, St, Loc, ExtraPath)); {error,_E2} -> epp_reply(From, @@ -753,14 +810,15 @@ scan_file([{'(',_Llp},{string,_Ls,Name},{',',_Lc},{integer,_Li,Ln},{')',_Lrp}, Ms = dict:store({atom,'FILE'}, {none,[{string,1,Name}]}, St#epp.macs), Locf = loc(Tf), NewLoc = new_location(Ln, St#epp.location, Locf), - scan_toks(From, St#epp{name=Name,location=NewLoc,macs=Ms}); + Delta = abs(get_line(element(2, Tf)))-Ln + St#epp.delta, + wait_req_scan(St#epp{name2=Name,location=NewLoc,delta=Delta,macs=Ms}); scan_file(_Toks, Tf, From, St) -> epp_reply(From, {error,{loc(Tf),epp,{bad,file}}}), wait_req_scan(St). new_location(Ln, Le, Lf) when is_integer(Lf) -> Ln+(Le-Lf); -new_location(Ln, {Le,_}, {Lf,_}) -> +new_location(Ln, {Le,_}, {Lf,_}) -> {Ln+(Le-Lf),1}. %% skip_toks(From, EppState, SkipIstack) @@ -787,7 +845,7 @@ skip_toks(From, St, [I|Sis]) -> leave_file(From, St#epp{location=Cl,istk=[I|Sis]}); {error,_E} -> epp_reply(From, {error,{St#epp.location,epp,cannot_parse}}), - leave_file(From, St) %This serious, just exit! + leave_file(wait_request(St), St) %This serious, just exit! end; skip_toks(From, St, []) -> scan_toks(From, St). @@ -801,22 +859,23 @@ skip_else(_Else, From, St, Sis) -> skip_toks(From, St, Sis). %% macro_pars(Tokens, ArgStack) -%% macro_expansion(Tokens) +%% macro_expansion(Tokens, Line) %% Extract the macro parameters and the expansion from a macro definition. -macro_pars([{')',_Lp}, {',',_Ld}|Ex], Args) -> - {ok, {lists:reverse(Args), macro_expansion(Ex)}}; -macro_pars([{var,_,Name}, {')',_Lp}, {',',_Ld}|Ex], Args) -> +macro_pars([{')',_Lp}, {',',Ld}|Ex], Args) -> + {ok, {lists:reverse(Args), macro_expansion(Ex, Ld)}}; +macro_pars([{var,_,Name}, {')',_Lp}, {',',Ld}|Ex], Args) -> false = lists:member(Name, Args), %Prolog is nice - {ok, {lists:reverse([Name|Args]), macro_expansion(Ex)}}; + {ok, {lists:reverse([Name|Args]), macro_expansion(Ex, Ld)}}; macro_pars([{var,_L,Name}, {',',_}|Ts], Args) -> - false = lists:member(Name, Args), + false = lists:member(Name, Args), macro_pars(Ts, [Name|Args]). -macro_expansion([{')',_Lp},{dot,_Ld}]) -> []; -macro_expansion([{dot,_Ld}]) -> []; %Be nice, allow no right paren! -macro_expansion([T|Ts]) -> - [T|macro_expansion(Ts)]. +macro_expansion([{')',_Lp},{dot,_Ld}], _L0) -> []; +macro_expansion([{dot,Ld}], _L0) -> throw({error,Ld,missing_parenthesis}); +macro_expansion([T|Ts], _L0) -> + [T|macro_expansion(Ts, element(2, T))]; +macro_expansion([], L0) -> throw({error,L0,premature_end}). %% expand_macros(Tokens, Macros) %% expand_macro(Tokens, MacroToken, RestTokens) @@ -1071,11 +1130,11 @@ epp_reply(From, Rep) -> wait_epp_reply(Epp, Mref) -> receive - {epp_reply,Epp,Rep} -> + {epp_reply,Epp,Rep} -> erlang:demonitor(Mref), receive {'DOWN',Mref,_,_,_} -> ok after 0 -> ok end, Rep; - {'DOWN',Mref,_,_,E} -> + {'DOWN',Mref,_,_,E} -> receive {epp_reply,Epp,Rep} -> Rep after 0 -> exit(E) end @@ -1116,6 +1175,9 @@ neg_line(L) -> abs_line(L) -> erl_scan:set_attribute(line, L, fun(Line) -> abs(Line) end). +add_line(L, Offset) -> + erl_scan:set_attribute(line, L, fun(Line) -> Line+Offset end). + start_loc(Line) when is_integer(Line) -> 1; start_loc({_Line, _Column}) -> @@ -1132,7 +1194,7 @@ get_line({Line,_Column}) -> %% mainly aimed at yecc, the parser generator, which uses the -file %% attribute to get correct lines in messages referring to code %% supplied by the user (actions etc in .yrl files). -%% +%% %% In a perfect world (read: perfectly implemented applications such %% as Xref, Cover, Debugger, etc.) it would not be necessary to %% distinguish -file attributes from epp and the input file. The @@ -1152,7 +1214,7 @@ get_line({Line,_Column}) -> %% have been output by epp (corresponding to -include and %% -include_lib) are kept, but the user's -file attributes are %% removed. This seems sufficient for now. -%% +%% %% It turns out to be difficult to distinguish -file attributes in the %% input file from the ones added by epp unless some action is taken. %% The (less than perfect) solution employed is to let epp assign @@ -1164,7 +1226,7 @@ get_line({Line,_Column}) -> interpret_file_attribute(Forms) -> interpret_file_attr(Forms, 0, []). -interpret_file_attr([{attribute,Loc,file,{File,Line}}=Form | Forms], +interpret_file_attr([{attribute,Loc,file,{File,Line}}=Form | Forms], Delta, Fs) -> {line, L} = erl_scan:attributes_info(Loc, line), if @@ -1175,10 +1237,10 @@ interpret_file_attr([{attribute,Loc,file,{File,Line}}=Form | Forms], %% -include or -include_lib % true = L =:= Line, case Fs of - [_, Delta1, File | Fs1] -> % end of included file - [Form | interpret_file_attr(Forms, Delta1, [File | Fs1])]; + [_, File | Fs1] -> % end of included file + [Form | interpret_file_attr(Forms, 0, [File | Fs1])]; _ -> % start of included file - [Form | interpret_file_attr(Forms, 0, [File, Delta | Fs])] + [Form | interpret_file_attr(Forms, 0, [File | Fs])] end end; interpret_file_attr([Form0 | Forms], Delta, Fs) -> diff --git a/lib/stdlib/src/erl_compile.erl b/lib/stdlib/src/erl_compile.erl index d9d15e05f8..abff37e4bc 100644 --- a/lib/stdlib/src/erl_compile.erl +++ b/lib/stdlib/src/erl_compile.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1997-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_compile). @@ -23,6 +23,8 @@ -export([compile_cmdline/1]). +-export_type([cmd_line_arg/0]). + %% Mapping from extension to {M,F} to run the correct compiler. compiler(".erl") -> {compile, compile}; diff --git a/lib/stdlib/src/erl_eval.erl b/lib/stdlib/src/erl_eval.erl index ea1b179ee5..515ea2ebb7 100644 --- a/lib/stdlib/src/erl_eval.erl +++ b/lib/stdlib/src/erl_eval.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. 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 @@ -34,6 +34,37 @@ -import(lists, [reverse/1,foldl/3,member/2]). +-export_type([binding_struct/0]). + +-type(expression() :: erl_parse:abstract_expr()). +-type(expressions() :: [erl_parse:abstract_expr()]). +-type(expression_list() :: [expression()]). +-type(clauses() :: [erl_parse:abstract_clause()]). +-type(name() :: term()). +-type(value() :: term()). +-type(bindings() :: [{name(), value()}]). +-type(binding_struct() :: orddict:orddict()). + +-type(lfun_value_handler() :: fun((Name :: atom(), + Arguments :: [term()]) -> + Value :: value())). +-type(lfun_eval_handler() :: fun((Name :: atom(), + Arguments :: expression_list(), + Bindings :: binding_struct()) -> + {value, + Value :: value(), + NewBindings :: binding_struct()})). +-type(local_function_handler() :: {value, lfun_value_handler()} + | {eval, lfun_eval_handler()} + | none). + +-type(func_spec() :: {Module :: module(), Function :: atom()} | function()). +-type(nlfun_handler() :: fun((FuncSpec :: func_spec(), + Arguments :: [term()]) -> + term())). +-type(non_local_function_handler() :: {value, nlfun_handler()} + | none). + %% exprs(ExpressionSeq, Bindings) %% exprs(ExpressionSeq, Bindings, LocalFuncHandler) %% exprs(ExpressionSeq, Bindings, LocalFuncHandler, ExternalFuncHandler) @@ -45,6 +76,11 @@ %% that there are valid constructs in Expression to be taken care of %% by a function handler but considerad errors by erl_lint. +-spec(exprs(Expressions, Bindings) -> {value, Value, NewBindings} when + Expressions :: expressions(), + Bindings :: binding_struct(), + Value :: value(), + NewBindings :: binding_struct()). exprs(Exprs, Bs) -> case check_command(Exprs, Bs) of ok -> @@ -53,9 +89,25 @@ exprs(Exprs, Bs) -> erlang:raise(error, Error, [{?MODULE,exprs,2}]) end. +-spec(exprs(Expressions, Bindings, LocalFunctionHandler) -> + {value, Value, NewBindings} when + Expressions :: expressions(), + Bindings :: binding_struct(), + LocalFunctionHandler :: local_function_handler(), + Value :: value(), + NewBindings :: binding_struct()). exprs(Exprs, Bs, Lf) -> exprs(Exprs, Bs, Lf, none, none). +-spec(exprs(Expressions, Bindings, LocalFunctionHandler, + NonLocalFunctionHandler) -> + {value, Value, NewBindings} when + Expressions :: expressions(), + Bindings :: binding_struct(), + LocalFunctionHandler :: local_function_handler(), + NonLocalFunctionHandler :: non_local_function_handler(), + Value :: value(), + NewBindings :: binding_struct()). exprs(Exprs, Bs, Lf, Ef) -> exprs(Exprs, Bs, Lf, Ef, none). @@ -75,6 +127,11 @@ exprs([E|Es], Bs0, Lf, Ef, RBs) -> %% %% Only expr/2 checks the command by calling erl_lint. See exprs/2. +-spec(expr(Expression, Bindings) -> {value, Value, NewBindings} when + Expression :: expression(), + Bindings :: binding_struct(), + Value :: value(), + NewBindings :: binding_struct()). expr(E, Bs) -> case check_command([E], Bs) of ok -> @@ -83,9 +140,25 @@ expr(E, Bs) -> erlang:raise(error, Error, [{?MODULE,expr,2}]) end. +-spec(expr(Expression, Bindings, LocalFunctionHandler) -> + {value, Value, NewBindings} when + Expression :: expression(), + Bindings :: binding_struct(), + LocalFunctionHandler :: local_function_handler(), + Value :: value(), + NewBindings :: binding_struct()). expr(E, Bs, Lf) -> expr(E, Bs, Lf, none, none). +-spec(expr(Expression, Bindings, LocalFunctionHandler, + NonLocalFunctionHandler) -> + {value, Value, NewBindings} when + Expression :: expression(), + Bindings :: binding_struct(), + LocalFunctionHandler :: local_function_handler(), + NonLocalFunctionHandler :: non_local_function_handler(), + Value :: value(), + NewBindings :: binding_struct()). expr(E, Bs, Lf, Ef) -> expr(E, Bs, Lf, Ef, none). @@ -114,6 +187,16 @@ fun_data(F) when is_function(F) -> fun_data(_T) -> false. +-spec(expr(Expression, Bindings, LocalFunctionHandler, + NonLocalFunctionHandler, ReturnFormat) -> + {value, Value, NewBindings} | Value when + Expression :: expression(), + Bindings :: binding_struct(), + LocalFunctionHandler :: local_function_handler(), + NonLocalFunctionHandler :: non_local_function_handler(), + ReturnFormat :: none | value, + Value :: value(), + NewBindings :: binding_struct()). expr({var,_,V}, Bs, _Lf, _Ef, RBs) -> case binding(V, Bs) of {value,Val} -> @@ -384,12 +467,9 @@ local_func(Func, As0, Bs0, {value,F}, value) -> local_func(Func, As0, Bs0, {value,F}, RBs) -> {As1,Bs1} = expr_list(As0, Bs0, {value,F}), ret_expr(F(Func, As1), Bs1, RBs); -local_func(Func, As0, Bs0, {value,F,Eas}, value) -> - {As1,_Bs1} = expr_list(As0, Bs0, {value,F,Eas}), - apply(F, [Func,As1|Eas]); local_func(Func, As0, Bs0, {value,F,Eas}, RBs) -> - {As1,Bs1} = expr_list(As0, Bs0, {value,F,Eas}), - ret_expr(apply(F, [Func,As1|Eas]), Bs1, RBs); + Fun = fun(Name, Args) -> apply(F, [Name,Args|Eas]) end, + local_func(Func, As0, Bs0, {value, Fun}, RBs); local_func(Func, As, Bs, {eval,F}, RBs) -> local_func2(F(Func, As, Bs), RBs); local_func(Func, As, Bs, {eval,F,Eas}, RBs) -> @@ -613,12 +693,33 @@ eval_fun([], As, _Bs, _Lf, _Ef, _RBs) -> %% expr_list(ExpressionList, Bindings, LocalFuncHandler, ExternalFuncHandler) %% Evaluate a list of expressions "in parallel" at the same level. +-spec(expr_list(ExpressionList, Bindings) -> {ValueList, NewBindings} when + ExpressionList :: expression_list(), + Bindings :: binding_struct(), + ValueList :: [value()], + NewBindings :: binding_struct()). expr_list(Es, Bs) -> expr_list(Es, Bs, none, none). +-spec(expr_list(ExpressionList, Bindings, LocalFunctionHandler) -> + {ValueList, NewBindings} when + ExpressionList :: expression_list(), + Bindings :: binding_struct(), + LocalFunctionHandler :: local_function_handler(), + ValueList :: [value()], + NewBindings :: binding_struct()). expr_list(Es, Bs, Lf) -> expr_list(Es, Bs, Lf, none). +-spec(expr_list(ExpressionList, Bindings, LocalFunctionHandler, + NonLocalFunctionHandler) -> + {ValueList, NewBindings} when + ExpressionList :: expression_list(), + Bindings :: binding_struct(), + LocalFunctionHandler :: local_function_handler(), + NonLocalFunctionHandler :: non_local_function_handler(), + ValueList :: [value()], + NewBindings :: binding_struct()). expr_list(Es, Bs, Lf, Ef) -> expr_list(Es, [], Bs, Bs, Lf, Ef). @@ -757,6 +858,15 @@ send_all([], _) -> true. %% match_clause -> {Body, Bindings} or nomatch +-spec(match_clause(Clauses, ValueList, Bindings, LocalFunctionHandler) -> + {Body, NewBindings} | nomatch when + Clauses :: clauses(), + ValueList :: [value()], + Bindings :: binding_struct(), + LocalFunctionHandler :: local_function_handler(), + Body :: expression_list(), + NewBindings :: binding_struct()). + match_clause(Cs, Vs, Bs, Lf) -> match_clause(Cs, Vs, Bs, Lf, none). @@ -973,18 +1083,30 @@ match_list(_, _, _Bs, _BBs) -> %% add_binding(Name, Value, Bindings) %% del_binding(Name, Bindings) +-spec(new_bindings() -> binding_struct()). new_bindings() -> orddict:new(). +-spec(bindings(BindingStruct :: binding_struct()) -> bindings()). bindings(Bs) -> orddict:to_list(Bs). +-spec(binding(Name, BindingStruct) -> {value, value()} | unbound when + Name :: name(), + BindingStruct :: binding_struct()). binding(Name, Bs) -> case orddict:find(Name, Bs) of {ok,Val} -> {value,Val}; error -> unbound end. +-spec(add_binding(Name, Value, BindingStruct) -> binding_struct() when + Name :: name(), + Value :: value(), + BindingStruct :: binding_struct()). add_binding(Name, Val, Bs) -> orddict:store(Name, Val, Bs). +-spec(del_binding(Name, BindingStruct) -> binding_struct() when + Name :: name(), + BindingStruct :: binding_struct()). del_binding(Name, Bs) -> orddict:erase(Name, Bs). add_bindings(Bs1, Bs2) -> diff --git a/lib/stdlib/src/erl_expand_records.erl b/lib/stdlib/src/erl_expand_records.erl index 6fa77f2c3b..eada563914 100644 --- a/lib/stdlib/src/erl_expand_records.erl +++ b/lib/stdlib/src/erl_expand_records.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2005-2011. 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% %% %% Purpose : Expand records into tuples. @@ -38,6 +38,10 @@ checked_ra=[] % succesfully accessed records }). +-spec(module(AbsForms, CompileOptions) -> AbsForms when + AbsForms :: [erl_parse:abstract_form()], + CompileOptions :: [compile:option()]). + %% Is is assumed that Fs is a valid list of forms. It should pass %% erl_lint without errors. module(Fs0, Opts0) -> @@ -95,8 +99,9 @@ forms([F | Fs0], St0) -> forms([], St) -> {[],St}. clauses([{clause,Line,H0,G0,B0} | Cs0], St0) -> - {H,St1} = head(H0, St0), - {G,St2} = guard(G0, St1), + {H1,St1} = head(H0, St0), + {G1,St2} = guard(G0, St1), + {H,G} = optimize_is_record(H1, G1, St2), {B,St3} = exprs(B0, St2), {Cs,St4} = clauses(Cs0, St3), {[{clause,Line,H,G,B} | Cs],St4}; @@ -191,7 +196,6 @@ guard_test1(Test, St) -> normalise_test(atom, 1) -> is_atom; normalise_test(binary, 1) -> is_binary; -normalise_test(constant, 1) -> is_constant; normalise_test(float, 1) -> is_float; normalise_test(function, 1) -> is_function; normalise_test(integer, 1) -> is_integer; @@ -346,9 +350,6 @@ expr({'fun',Line,{clauses,Cs0}}, St0) -> {{'fun',Line,{clauses,Cs}},St1}; expr({call,Line,{atom,_,is_record},[A,{atom,_,Name}]}, St) -> record_test(Line, A, Name, St); -expr({'cond',Line,Cs0}, St0) -> - {Cs,St1} = clauses(Cs0, St0), - {{'cond',Line,Cs},St1}; expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,is_record}}, [A,{atom,_,Name}]}, St) -> record_test(Line, A, Name, St); @@ -804,5 +805,137 @@ imported(F, A, St) -> error -> no end. +%%% +%%% Replace is_record/3 in guards with matching if possible. +%%% + +optimize_is_record(H0, G0, #exprec{compile=Opts}) -> + case opt_rec_vars(G0) of + [] -> + {H0,G0}; + Rs0 -> + case lists:member(no_is_record_optimization, Opts) of + true -> + {H0,G0}; + false -> + {H,Rs} = opt_pattern_list(H0, Rs0), + G = opt_remove(G0, Rs), + {H,G} + end + end. + + +%% opt_rec_vars(Guards) -> Vars. +%% Search through the guard expression, looking for +%% variables referenced in those is_record/3 calls that +%% will fail the entire guard if they evaluate to 'false' +%% +%% In the following code +%% +%% f(X, Y, Z) when is_record(X, r1) andalso +%% (is_record(Y, r2) orelse is_record(Z, r3)) +%% +%% the entire guard will be false if the record test for +%% X fails, and the clause can be rewritten to: +%% +%% f({r1,...}=X, Y, Z) when true andalso +%% (is_record(Y, r2) or is_record(Z, r3)) +%% +opt_rec_vars([G|Gs]) -> + Rs = opt_rec_vars_1(G, orddict:new()), + opt_rec_vars(Gs, Rs); +opt_rec_vars([]) -> orddict:new(). + +opt_rec_vars([G|Gs], Rs0) -> + Rs1 = opt_rec_vars_1(G, orddict:new()), + Rs = ordsets:intersection(Rs0, Rs1), + opt_rec_vars(Gs, Rs); +opt_rec_vars([], Rs) -> Rs. + +opt_rec_vars_1([T|Ts], Rs0) -> + Rs = opt_rec_vars_2(T, Rs0), + opt_rec_vars_1(Ts, Rs); +opt_rec_vars_1([], Rs) -> Rs. + +opt_rec_vars_2({op,_,'and',A1,A2}, Rs) -> + opt_rec_vars_1([A1,A2], Rs); +opt_rec_vars_2({op,_,'andalso',A1,A2}, Rs) -> + opt_rec_vars_1([A1,A2], Rs); +opt_rec_vars_2({op,_,'orelse',Arg,{atom,_,fail}}, Rs) -> + %% Since the second argument guarantees failure, + %% it is safe to inspect the first argument. + opt_rec_vars_2(Arg, Rs); +opt_rec_vars_2({call,_,{remote,_,{atom,_,erlang},{atom,_,is_record}}, + [{var,_,V},{atom,_,Tag},{integer,_,Sz}]}, Rs) -> + orddict:store(V, {Tag,Sz}, Rs); +opt_rec_vars_2({call,_,{atom,_,is_record}, + [{var,_,V},{atom,_,Tag},{integer,_,Sz}]}, Rs) -> + orddict:store(V, {Tag,Sz}, Rs); +opt_rec_vars_2(_, Rs) -> Rs. + +opt_pattern_list(Ps, Rs) -> + opt_pattern_list(Ps, Rs, []). + +opt_pattern_list([P0|Ps], Rs0, Acc) -> + {P,Rs} = opt_pattern(P0, Rs0), + opt_pattern_list(Ps, Rs, [P|Acc]); +opt_pattern_list([], Rs, Acc) -> + {reverse(Acc),Rs}. + +opt_pattern({var,_,V}=Var, Rs0) -> + case orddict:find(V, Rs0) of + {ok,{Tag,Sz}} -> + Rs = orddict:store(V, {remove,Tag,Sz}, Rs0), + {opt_var(Var, Tag, Sz),Rs}; + _ -> + {Var,Rs0} + end; +opt_pattern({cons,Line,H0,T0}, Rs0) -> + {H,Rs1} = opt_pattern(H0, Rs0), + {T,Rs} = opt_pattern(T0, Rs1), + {{cons,Line,H,T},Rs}; +opt_pattern({tuple,Line,Es0}, Rs0) -> + {Es,Rs} = opt_pattern_list(Es0, Rs0), + {{tuple,Line,Es},Rs}; +opt_pattern({match,Line,Pa0,Pb0}, Rs0) -> + {Pa,Rs1} = opt_pattern(Pa0, Rs0), + {Pb,Rs} = opt_pattern(Pb0, Rs1), + {{match,Line,Pa,Pb},Rs}; +opt_pattern(P, Rs) -> {P,Rs}. + +opt_var({var,Line,_}=Var, Tag, Sz) -> + Rp = record_pattern(2, -1, ignore, Sz, Line, [{atom,Line,Tag}]), + {match,Line,{tuple,Line,Rp},Var}. + +opt_remove(Gs, Rs) -> + [opt_remove_1(G, Rs) || G <- Gs]. + +opt_remove_1(Ts, Rs) -> + [opt_remove_2(T, Rs) || T <- Ts]. + +opt_remove_2({op,L,'and'=Op,A1,A2}, Rs) -> + {op,L,Op,opt_remove_2(A1, Rs),opt_remove_2(A2, Rs)}; +opt_remove_2({op,L,'andalso'=Op,A1,A2}, Rs) -> + {op,L,Op,opt_remove_2(A1, Rs),opt_remove_2(A2, Rs)}; +opt_remove_2({op,L,'orelse',A1,A2}, Rs) -> + {op,L,'orelse',opt_remove_2(A1, Rs),A2}; +opt_remove_2({call,Line,{remote,_,{atom,_,erlang},{atom,_,is_record}}, + [{var,_,V},{atom,_,Tag},{integer,_,Sz}]}=A, Rs) -> + case orddict:find(V, Rs) of + {ok,{remove,Tag,Sz}} -> + {atom,Line,true}; + _ -> + A + end; +opt_remove_2({call,Line,{atom,_,is_record}, + [{var,_,V},{atom,_,Tag},{integer,_,Sz}]}=A, Rs) -> + case orddict:find(V, Rs) of + {ok,{remove,Tag,Sz}} -> + {atom,Line,true}; + _ -> + A + end; +opt_remove_2(A, _) -> A. + neg_line(L) -> erl_parse:set_line(L, fun(Line) -> -abs(Line) end). diff --git a/lib/stdlib/src/erl_internal.erl b/lib/stdlib/src/erl_internal.erl index 16173d8210..478f05e792 100644 --- a/lib/stdlib/src/erl_internal.erl +++ b/lib/stdlib/src/erl_internal.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1998-2011. 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_internal). @@ -48,13 +48,15 @@ %% -export([bif/2,bif/3,guard_bif/2, - type_test/2,new_type_test/2,old_type_test/2]). + type_test/2,new_type_test/2,old_type_test/2,old_bif/2]). -export([arith_op/2,bool_op/2,comp_op/2,list_op/2,send_op/2,op_type/2]). %%--------------------------------------------------------------------------- %% Erlang builtin functions allowed in guards. --spec guard_bif(Name::atom(), Arity::arity()) -> boolean(). +-spec guard_bif(Name, Arity) -> boolean() when + Name :: atom(), + Arity :: arity(). guard_bif(abs, 1) -> true; guard_bif(float, 1) -> true; @@ -87,10 +89,14 @@ guard_bif(is_reference, 1) -> true; guard_bif(is_tuple, 1) -> true; guard_bif(is_record, 2) -> true; guard_bif(is_record, 3) -> true; +guard_bif(binary_part, 2) -> true; +guard_bif(binary_part, 3) -> true; guard_bif(Name, A) when is_atom(Name), is_integer(A) -> false. %% Erlang type tests. --spec type_test(Name::atom(), Arity::arity()) -> boolean(). +-spec type_test(Name, Arity) -> boolean() when + Name :: atom(), + Arity :: arity(). type_test(Name, Arity) -> new_type_test(Name, Arity) orelse old_type_test(Name, Arity). @@ -133,7 +139,9 @@ old_type_test(record, 2) -> true; old_type_test(function, 1) -> true; old_type_test(Name, A) when is_atom(Name), is_integer(A) -> false. --spec arith_op(Op::atom(), Arity::arity()) -> boolean(). +-spec arith_op(OpName, Arity) -> boolean() when + OpName :: atom(), + Arity :: arity(). arith_op('+', 1) -> true; arith_op('-', 1) -> true; @@ -151,7 +159,9 @@ arith_op('bsl', 2) -> true; arith_op('bsr', 2) -> true; arith_op(Op, A) when is_atom(Op), is_integer(A) -> false. --spec bool_op(Op::atom(), Arity::arity()) -> boolean(). +-spec bool_op(OpName, Arity) -> boolean() when + OpName :: atom(), + Arity :: arity(). bool_op('not', 1) -> true; bool_op('and', 2) -> true; @@ -159,7 +169,9 @@ bool_op('or', 2) -> true; bool_op('xor', 2) -> true; bool_op(Op, A) when is_atom(Op), is_integer(A) -> false. --spec comp_op(Op::atom(), Arity::arity()) -> boolean(). +-spec comp_op(OpName, Arity) -> boolean() when + OpName :: atom(), + Arity :: arity(). comp_op('==', 2) -> true; comp_op('/=', 2) -> true; @@ -171,18 +183,25 @@ comp_op('=:=', 2) -> true; comp_op('=/=', 2) -> true; comp_op(Op, A) when is_atom(Op), is_integer(A) -> false. --spec list_op(Op::atom(), Arity::arity()) -> boolean(). +-spec list_op(OpName, Arity) -> boolean() when + OpName :: atom(), + Arity :: arity(). list_op('++', 2) -> true; list_op('--', 2) -> true; list_op(Op, A) when is_atom(Op), is_integer(A) -> false. --spec send_op(Op::atom(), Arity::arity()) -> boolean(). +-spec send_op(OpName, Arity) -> boolean() when + OpName :: atom(), + Arity :: arity(). send_op('!', 2) -> true; send_op(Op, A) when is_atom(Op), is_integer(A) -> false. --spec op_type(atom(), arity()) -> 'arith' | 'bool' | 'comp' | 'list' | 'send'. +-spec op_type(OpName, Arity) -> Type when + OpName :: atom(), + Arity :: arity(), + Type :: 'arith' | 'bool' | 'comp' | 'list' | 'send'. op_type('+', 1) -> arith; op_type('-', 1) -> arith; @@ -219,7 +238,9 @@ op_type('!', 2) -> send. bif(erlang, Name, Arity) -> bif(Name, Arity); bif(M, F, A) when is_atom(M), is_atom(F), is_integer(A) -> false. --spec bif(Name::atom(), Arity::arity()) -> boolean(). +-spec bif(Name, Arity) -> boolean() when + Name :: atom(), + Arity::arity(). %% Returns true if erlang:Name/Arity is an auto-imported BIF, false otherwise. %% Use erlang:is_bultin(Mod, Name, Arity) to find whether a function is a BIF %% (meaning implemented in C) or not. @@ -229,11 +250,14 @@ bif(apply, 2) -> true; bif(apply, 3) -> true; bif(atom_to_binary, 2) -> true; bif(atom_to_list, 1) -> true; +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_list, 1) -> true; bif(binary_to_list, 3) -> true; bif(binary_to_term, 1) -> true; +bif(binary_to_term, 2) -> true; bif(bitsize, 1) -> true; bif(bit_size, 1) -> true; bif(bitstring_to_list, 1) -> true; @@ -242,10 +266,14 @@ bif(check_process_code, 2) -> true; bif(concat_binary, 1) -> true; bif(date, 0) -> true; bif(delete_module, 1) -> true; +bif(demonitor, 1) -> true; +bif(demonitor, 2) -> true; bif(disconnect_node, 1) -> true; bif(element, 2) -> true; bif(erase, 0) -> true; bif(erase, 1) -> true; +bif(error, 1) -> true; +bif(error, 2) -> true; bif(exit, 1) -> true; bif(exit, 2) -> true; bif(float, 1) -> true; @@ -261,6 +289,7 @@ bif(halt, 0) -> true; bif(halt, 1) -> true; bif(hd, 1) -> true; bif(integer_to_list, 1) -> true; +bif(integer_to_list, 2) -> true; bif(iolist_size, 1) -> true; bif(iolist_to_binary, 1) -> true; bif(is_alive, 0) -> true; @@ -290,11 +319,16 @@ bif(list_to_bitstring, 1) -> true; bif(list_to_existing_atom, 1) -> true; bif(list_to_float, 1) -> true; bif(list_to_integer, 1) -> true; +bif(list_to_integer, 2) -> true; bif(list_to_pid, 1) -> true; bif(list_to_tuple, 1) -> true; bif(load_module, 2) -> true; bif(make_ref, 0) -> true; +bif(max,2) -> true; +bif(min,2) -> true; bif(module_loaded, 1) -> true; +bif(monitor, 2) -> true; +bif(monitor, 3) -> true; bif(monitor_node, 2) -> true; bif(node, 0) -> true; bif(node, 1) -> true; @@ -305,6 +339,7 @@ bif(open_port, 2) -> true; bif(pid_to_list, 1) -> true; bif(port_close, 1) -> true; bif(port_command, 2) -> true; +bif(port_command, 3) -> true; bif(port_connect, 2) -> true; bif(port_control, 3) -> true; bif(pre_loaded, 0) -> true; @@ -349,3 +384,134 @@ bif(unlink, 1) -> true; bif(unregister, 1) -> true; bif(whereis, 1) -> true; bif(Name, A) when is_atom(Name), is_integer(A) -> false. + +-spec old_bif(Name::atom(), Arity::arity()) -> boolean(). +%% Returns true if erlang:Name/Arity is an old (pre R14) auto-imported BIF, false otherwise. +%% Use erlang:is_bultin(Mod, Name, Arity) to find whether a function is a BIF +%% (meaning implemented in C) or not. + +old_bif(abs, 1) -> true; +old_bif(apply, 2) -> true; +old_bif(apply, 3) -> true; +old_bif(atom_to_binary, 2) -> true; +old_bif(atom_to_list, 1) -> true; +old_bif(binary_to_atom, 2) -> true; +old_bif(binary_to_existing_atom, 2) -> true; +old_bif(binary_to_list, 1) -> true; +old_bif(binary_to_list, 3) -> true; +old_bif(binary_to_term, 1) -> true; +old_bif(bitsize, 1) -> true; +old_bif(bit_size, 1) -> true; +old_bif(bitstring_to_list, 1) -> true; +old_bif(byte_size, 1) -> true; +old_bif(check_process_code, 2) -> true; +old_bif(concat_binary, 1) -> true; +old_bif(date, 0) -> true; +old_bif(delete_module, 1) -> true; +old_bif(disconnect_node, 1) -> true; +old_bif(element, 2) -> true; +old_bif(erase, 0) -> true; +old_bif(erase, 1) -> true; +old_bif(exit, 1) -> true; +old_bif(exit, 2) -> true; +old_bif(float, 1) -> true; +old_bif(float_to_list, 1) -> true; +old_bif(garbage_collect, 0) -> true; +old_bif(garbage_collect, 1) -> true; +old_bif(get, 0) -> true; +old_bif(get, 1) -> true; +old_bif(get_keys, 1) -> true; +old_bif(group_leader, 0) -> true; +old_bif(group_leader, 2) -> true; +old_bif(halt, 0) -> true; +old_bif(halt, 1) -> true; +old_bif(hd, 1) -> true; +old_bif(integer_to_list, 1) -> true; +old_bif(iolist_size, 1) -> true; +old_bif(iolist_to_binary, 1) -> true; +old_bif(is_alive, 0) -> true; +old_bif(is_process_alive, 1) -> true; +old_bif(is_atom, 1) -> true; +old_bif(is_boolean, 1) -> true; +old_bif(is_binary, 1) -> true; +old_bif(is_bitstr, 1) -> true; +old_bif(is_bitstring, 1) -> true; +old_bif(is_float, 1) -> true; +old_bif(is_function, 1) -> true; +old_bif(is_function, 2) -> true; +old_bif(is_integer, 1) -> true; +old_bif(is_list, 1) -> true; +old_bif(is_number, 1) -> true; +old_bif(is_pid, 1) -> true; +old_bif(is_port, 1) -> true; +old_bif(is_reference, 1) -> true; +old_bif(is_tuple, 1) -> true; +old_bif(is_record, 2) -> true; +old_bif(is_record, 3) -> true; +old_bif(length, 1) -> true; +old_bif(link, 1) -> true; +old_bif(list_to_atom, 1) -> true; +old_bif(list_to_binary, 1) -> true; +old_bif(list_to_bitstring, 1) -> true; +old_bif(list_to_existing_atom, 1) -> true; +old_bif(list_to_float, 1) -> true; +old_bif(list_to_integer, 1) -> true; +old_bif(list_to_pid, 1) -> true; +old_bif(list_to_tuple, 1) -> true; +old_bif(load_module, 2) -> true; +old_bif(make_ref, 0) -> true; +old_bif(module_loaded, 1) -> true; +old_bif(monitor_node, 2) -> true; +old_bif(node, 0) -> true; +old_bif(node, 1) -> true; +old_bif(nodes, 0) -> true; +old_bif(nodes, 1) -> true; +old_bif(now, 0) -> true; +old_bif(open_port, 2) -> true; +old_bif(pid_to_list, 1) -> true; +old_bif(port_close, 1) -> true; +old_bif(port_command, 2) -> true; +old_bif(port_connect, 2) -> true; +old_bif(port_control, 3) -> true; +old_bif(pre_loaded, 0) -> true; +old_bif(process_flag, 2) -> true; +old_bif(process_flag, 3) -> true; +old_bif(process_info, 1) -> true; +old_bif(process_info, 2) -> true; +old_bif(processes, 0) -> true; +old_bif(purge_module, 1) -> true; +old_bif(put, 2) -> true; +old_bif(register, 2) -> true; +old_bif(registered, 0) -> true; +old_bif(round, 1) -> true; +old_bif(self, 0) -> true; +old_bif(setelement, 3) -> true; +old_bif(size, 1) -> true; +old_bif(spawn, 1) -> true; +old_bif(spawn, 2) -> true; +old_bif(spawn, 3) -> true; +old_bif(spawn, 4) -> true; +old_bif(spawn_link, 1) -> true; +old_bif(spawn_link, 2) -> true; +old_bif(spawn_link, 3) -> true; +old_bif(spawn_link, 4) -> true; +old_bif(spawn_monitor, 1) -> true; +old_bif(spawn_monitor, 3) -> true; +old_bif(spawn_opt, 2) -> true; +old_bif(spawn_opt, 3) -> true; +old_bif(spawn_opt, 4) -> true; +old_bif(spawn_opt, 5) -> true; +old_bif(split_binary, 2) -> true; +old_bif(statistics, 1) -> true; +old_bif(term_to_binary, 1) -> true; +old_bif(term_to_binary, 2) -> true; +old_bif(throw, 1) -> true; +old_bif(time, 0) -> true; +old_bif(tl, 1) -> true; +old_bif(trunc, 1) -> true; +old_bif(tuple_size, 1) -> true; +old_bif(tuple_to_list, 1) -> true; +old_bif(unlink, 1) -> true; +old_bif(unregister, 1) -> true; +old_bif(whereis, 1) -> true; +old_bif(Name, A) when is_atom(Name), is_integer(A) -> false. diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl index 91f7641af7..dd0b9bc2ab 100644 --- a/lib/stdlib/src/erl_lint.erl +++ b/lib/stdlib/src/erl_lint.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. 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 @@ %% Value. %% The option handling functions. --spec bool_option(atom(), atom(), boolean(), [_]) -> boolean(). +-spec bool_option(atom(), atom(), boolean(), [compile:option()]) -> boolean(). bool_option(On, Off, Default, Opts) -> foldl(fun (Opt, _Def) when Opt =:= On -> true; @@ -60,6 +60,10 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) -> (_Opt, Def) -> Def end, Default, Opts). +%% The maximum number of arguments allowed for a function. + +-define(MAX_ARGUMENTS, 255). + %% The error and warning info structures, {Line,Module,Descriptor}, %% are kept in their seperate fields in the lint state record together %% with the name of the file (when a new file is entered, marked by @@ -72,6 +76,10 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) -> %%-define(DEBUGF(X,Y), io:format(X, Y)). -define(DEBUGF(X,Y), void). +-type line() :: erl_scan:line(). % a convenient alias +-type fa() :: {atom(), arity()}. % function+arity +-type ta() :: {atom(), arity()}. % type+arity + %% Usage of records, functions, and imports. The variable table, which %% is passed on as an argument, holds the usage of variables. -record(usage, { @@ -94,9 +102,11 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) -> 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) + no_auto=gb_sets:empty() :: gb_set(), %Functions explicitly not autoimported defined=gb_sets:empty() :: gb_set(), %Defined fuctions - on_load=[] :: [{atom(),integer()}], %On-load function - on_load_line=0 :: integer(), %Line for on_load + on_load=[] :: [fa()], %On-load function + on_load_line=0 :: line(), %Line for on_load clashes=[], %Exported functions named as BIFs not_deprecated=[], %Not considered deprecated func=[], %Current function @@ -110,17 +120,23 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) -> %outside any fun or lc xqlc= false :: boolean(), %true if qlc.hrl included new = false :: boolean(), %Has user-defined 'new/N' - called= [], %Called functions + called= [] :: [{fa(),line()}], %Called functions usage = #usage{} :: #usage{}, specs = dict:new() :: dict(), %Type specifications - types = dict:new() :: dict() %Type definitions + types = dict:new() :: dict(), %Type definitions + exp_types=gb_sets:empty():: gb_set() %Exported types }). -type lint_state() :: #lint{}. +-type error_description() :: term(). +-type error_info() :: {erl_scan:line(), module(), error_description()}. %% format_error(Error) %% Return a string describing the error. +-spec format_error(ErrorDescriptor) -> io_lib:chars() when + ErrorDescriptor :: error_description(). + format_error(undefined_module) -> "no module definition"; format_error({bad_module_name, M}) -> @@ -161,6 +177,9 @@ format_error({bad_nowarn_unused_function,{F,A}}) -> io_lib:format("function ~w/~w undefined", [F,A]); format_error({bad_nowarn_bif_clash,{F,A}}) -> io_lib:format("function ~w/~w undefined", [F,A]); +format_error(disallowed_nowarn_bif_clash) -> + io_lib:format("compile directive nowarn_bif_clash is no longer allowed,~n" + " - use explicit module names or -compile({no_auto_import, [F/A]})", []); format_error({bad_nowarn_deprecated_function,{M,F,A}}) -> io_lib:format("~w:~w/~w is not a deprecated function", [M,F,A]); format_error({bad_on_load,Term}) -> @@ -186,13 +205,21 @@ format_error({define_import,{F,A}}) -> io_lib:format("defining imported function ~w/~w", [F,A]); format_error({unused_function,{F,A}}) -> io_lib:format("function ~w/~w is unused", [F,A]); -format_error({redefine_bif,{F,A}}) -> - io_lib:format("defining BIF ~w/~w", [F,A]); format_error({call_to_redefined_bif,{F,A}}) -> - io_lib:format("call to ~w/~w will call erlang:~w/~w; " - "not ~w/~w in this module \n" - " (add an explicit module name to the call to avoid this error)", - [F,A,F,A,F,A]); + io_lib:format("ambiguous call of overridden auto-imported BIF ~w/~w~n" + " - use erlang:~w/~w or \"-compile({no_auto_import,[~w/~w]}).\" " + "to resolve name clash", [F,A,F,A,F,A]); +format_error({call_to_redefined_old_bif,{F,A}}) -> + io_lib:format("ambiguous call of overridden pre R14 auto-imported BIF ~w/~w~n" + " - use erlang:~w/~w or \"-compile({no_auto_import,[~w/~w]}).\" " + "to resolve name clash", [F,A,F,A,F,A]); +format_error({redefine_old_bif_import,{F,A}}) -> + io_lib:format("import directive overrides pre R14 auto-imported BIF ~w/~w~n" + " - use \"-compile({no_auto_import,[~w/~w]}).\" " + "to resolve name clash", [F,A,F,A]); +format_error({redefine_bif_import,{F,A}}) -> + io_lib:format("import directive overrides auto-imported BIF ~w/~w~n" + " - use \"-compile({no_auto_import,[~w/~w]}).\" to resolve name clash", [F,A,F,A]); format_error({deprecated, MFA, ReplacementMFA, Rel}) -> io_lib:format("~s is deprecated and will be removed in ~s; use ~s", @@ -208,11 +235,17 @@ 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]); %% --- patterns and guards --- format_error(illegal_pattern) -> "illegal pattern"; format_error(illegal_bin_pattern) -> "binary patterns cannot be matched in parallel using '='"; format_error(illegal_expr) -> "illegal expression"; +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"; %% --- exports --- format_error({explicit_export,F,A}) -> @@ -242,10 +275,10 @@ format_error({untyped_record,T}) -> format_error({unbound_var,V}) -> io_lib:format("variable ~w is unbound", [V]); format_error({unsafe_var,V,{What,Where}}) -> - io_lib:format("variable ~w unsafe in ~w ~s", + io_lib:format("variable ~w unsafe in ~w ~s", [V,What,format_where(Where)]); format_error({exported_var,V,{What,Where}}) -> - io_lib:format("variable ~w exported from ~w ~s", + io_lib:format("variable ~w exported from ~w ~s", [V,What,format_where(Where)]); format_error({shadowed_var,V,In}) -> io_lib:format("variable ~w shadowed in ~w", [V,In]); @@ -290,22 +323,26 @@ format_error({ill_defined_behaviour_callbacks,Behaviour}) -> %% --- types and specs --- format_error({singleton_typevar, Name}) -> io_lib:format("type variable ~w is only used once (is unbound)", [Name]); +format_error({bad_export_type, _ETs}) -> + io_lib:format("bad export_type declaration", []); +format_error({duplicated_export_type, {T, A}}) -> + io_lib:format("type ~w/~w already exported", [T, A]); format_error({undefined_type, {TypeName, Arity}}) -> io_lib:format("type ~w~s undefined", [TypeName, gen_type_paren(Arity)]); format_error({unused_type, {TypeName, Arity}}) -> io_lib:format("type ~w~s is unused", [TypeName, gen_type_paren(Arity)]); format_error({new_builtin_type, {TypeName, Arity}}) -> io_lib:format("type ~w~s is a new builtin type; " - "its (re)definition is allowed only until the next release", + "its (re)definition is allowed only until the next release", [TypeName, gen_type_paren(Arity)]); format_error({builtin_type, {TypeName, Arity}}) -> - io_lib:format("type ~w~s is a builtin type; it cannot be redefined", + io_lib:format("type ~w~s is a builtin type; it cannot be redefined", [TypeName, gen_type_paren(Arity)]); format_error({renamed_type, OldName, NewName}) -> io_lib:format("type ~w() is now called ~w(); " "please use the new name instead", [OldName, NewName]); format_error({redefine_type, {TypeName, Arity}}) -> - io_lib:format("type ~w~s already defined", + io_lib:format("type ~w~s already defined", [TypeName, gen_type_paren(Arity)]); format_error({type_syntax, Constr}) -> io_lib:format("bad ~w type", [Constr]); @@ -354,7 +391,7 @@ pseudolocals() -> %% %% Used by erl_eval.erl to check commands. -%% +%% exprs(Exprs, BindingsList) -> exprs_opt(Exprs, BindingsList, []). @@ -362,7 +399,7 @@ exprs_opt(Exprs, BindingsList, Opts) -> {St0,Vs} = foldl(fun({{record,_SequenceNumber,_Name},Attr0}, {St1,Vs1}) -> Attr = zip_file_and_line(Attr0, "none"), {attribute_state(Attr, St1),Vs1}; - ({V,_}, {St1,Vs1}) -> + ({V,_}, {St1,Vs1}) -> {St1,[{V,{bound,unused,[]}} | Vs1]} end, {start("nofile",Opts),[]}, BindingsList), Vt = orddict:from_list(Vs), @@ -387,16 +424,39 @@ used_vars(Exprs, BindingsList) -> %% apply_lambda/2 has been called to shut lint up. N.B. these lists are %% really all ordsets! +-spec(module(AbsForms) -> {ok, Warnings} | {error, Errors, Warnings} when + AbsForms :: [erl_parse:abstract_form()], + Warnings :: [{file:filename(),[ErrorInfo]}], + Errors :: [{FileName2 :: file:filename(),[ErrorInfo]}], + ErrorInfo :: error_info()). + module(Forms) -> Opts = compiler_options(Forms), St = forms(Forms, start("nofile", Opts)), return_status(St). - + +-spec(module(AbsForms, FileName) -> + {ok, Warnings} | {error, Errors, Warnings} when + AbsForms :: [erl_parse:abstract_form()], + FileName :: atom() | string(), + Warnings :: [{file:filename(),[ErrorInfo]}], + Errors :: [{FileName2 :: file:filename(),[ErrorInfo]}], + ErrorInfo :: error_info()). + module(Forms, FileName) -> Opts = compiler_options(Forms), St = forms(Forms, start(FileName, Opts)), return_status(St). +-spec(module(AbsForms, FileName, CompileOptions) -> + {ok, Warnings} | {error, Errors, Warnings} when + AbsForms :: [erl_parse:abstract_form()], + FileName :: atom() | string(), + CompileOptions :: [compile:option()], + Warnings :: [{file:filename(),[ErrorInfo]}], + Errors :: [{FileName2 :: file:filename(),[ErrorInfo]}], + ErrorInfo :: error_info()). + module(Forms, FileName, Opts0) -> %% We want the options given on the command line to take %% precedence over options in the module. @@ -506,7 +566,7 @@ pack_errors(Es) -> %% Sort on line number. pack_warnings(Ws) -> - [{File,lists:sort([W || {F,W} <- Ws, F =:= File])} || + [{File,lists:sort([W || {F,W} <- Ws, F =:= File])} || File <- lists:usort([F || {F,_} <- Ws])]. %% add_error(ErrorDescriptor, State) -> State' @@ -516,13 +576,13 @@ pack_warnings(Ws) -> add_error(E, St) -> St#lint{errors=[{St#lint.file,E}|St#lint.errors]}. -add_error(FileLine, E, St) -> +add_error(FileLine, E, St) -> {File,Location} = loc(FileLine), add_error({Location,erl_lint,E}, St#lint{file = File}). add_warning(W, St) -> St#lint{warnings=[{St#lint.file,W}|St#lint.warnings]}. -add_warning(FileLine, W, St) -> +add_warning(FileLine, W, St) -> {File,Location} = loc(FileLine), add_warning({Location,erl_lint,W}, St#lint{file = File}). @@ -538,8 +598,12 @@ loc(L) -> forms(Forms0, St0) -> Forms = eval_file_attribute(Forms0, St0), + Locals = local_functions(Forms), + AutoImportSuppressed = auto_import_suppressed(St0#lint.compile), + StDeprecated = disallowed_compile_flags(Forms,St0), %% Line numbers are from now on pairs {File,Line}. - St1 = includes_qlc_hrl(Forms, St0), + St1 = includes_qlc_hrl(Forms, StDeprecated#lint{locals = Locals, + no_auto = AutoImportSuppressed}), St2 = bif_clashes(Forms, St1), St3 = not_deprecated(Forms, St2), St4 = foldl(fun form/2, pre_scan(Forms, St3), Forms), @@ -561,7 +625,7 @@ pre_scan([_ | Fs], St) -> pre_scan(Fs, St); pre_scan([], St) -> St. - + includes_qlc_hrl(Forms, St) -> %% QLC calls erl_lint several times, sometimes with the compile %% attribute removed. The file attribute, however, is left as is. @@ -667,6 +731,8 @@ 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) -> + export_type(L, Es, St); attribute_state({attribute,L,import,Is}, St) -> import(L, Is, St); attribute_state({attribute,L,record,{Name,Fields}}, St) -> @@ -724,27 +790,38 @@ bif_clashes(Forms, St) -> Clashes = ordsets:subtract(ordsets:from_list(Clashes0), Nowarn), St#lint{clashes=Clashes}. --spec is_bif_clash(atom(), byte(), lint_state()) -> boolean(). - -is_bif_clash(_Name, _Arity, #lint{clashes=[]}) -> - false; -is_bif_clash(Name, Arity, #lint{clashes=Clashes}) -> - ordsets:is_element({Name,Arity}, Clashes). - %% not_deprecated(Forms, State0) -> State not_deprecated(Forms, St0) -> %% There are no line numbers in St0#lint.compile. - MFAsL = [{MFA,L} || + MFAsL = [{MFA,L} || {attribute, L, compile, Args} <- Forms, {nowarn_deprecated_function, MFAs0} <- lists:flatten([Args]), MFA <- lists:flatten([MFAs0])], Nowarn = [MFA || {MFA,_L} <- MFAsL], - Bad = [MFAL || {{M,F,A},_L}=MFAL <- MFAsL, + Bad = [MFAL || {{M,F,A},_L}=MFAL <- MFAsL, otp_internal:obsolete(M, F, A) =:= no], St1 = func_line_warning(bad_nowarn_deprecated_function, Bad, St0), St1#lint{not_deprecated = ordsets:from_list(Nowarn)}. +%% The nowarn_bif_clash directive is not only deprecated, it's actually an error from R14A +disallowed_compile_flags(Forms, St0) -> + %% There are (still) no line numbers in St0#lint.compile. + Errors0 = [ {St0#lint.file,{L,erl_lint,disallowed_nowarn_bif_clash}} || + {attribute,[{line,{_,L}}],compile,nowarn_bif_clash} <- Forms ], + Errors1 = [ {St0#lint.file,{L,erl_lint,disallowed_nowarn_bif_clash}} || + {attribute,[{line,{_,L}}],compile,{nowarn_bif_clash, {_,_}}} <- Forms ], + Disabled = (not is_warn_enabled(bif_clash, St0)), + Errors = if + Disabled andalso Errors0 =:= [] -> + [{St0#lint.file,{erl_lint,disallowed_nowarn_bif_clash}} | St0#lint.errors]; + Disabled -> + Errors0 ++ Errors1 ++ St0#lint.errors; + true -> + Errors1 ++ St0#lint.errors + end, + St0#lint{errors=Errors}. + %% post_traversal_check(Forms, State0) -> State. %% Do some further checking after the forms have been traversed and %% data about calls etc. have been collected. @@ -862,7 +939,7 @@ check_deprecated(Forms, St0) -> Bad = [{E,L} || {attribute, L, deprecated, Depr} <- Forms, D <- lists:flatten([Depr]), E <- depr_cat(D, X, Mod)], - foldl(fun ({E,L}, St1) -> + foldl(fun ({E,L}, St1) -> add_error(L, E, St1) end, St0, Bad). @@ -912,7 +989,7 @@ 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} + Imports = [{{FA,list_to_atom(package_to_string(Mod))},L} || {attribute,L,import,{Mod,Fs}} <- Forms, FA <- lists:usort(Fs)], Bad = [{FM,L} || FM <- Unused, {FM2,L} <- Imports, FM =:= FM2], @@ -932,7 +1009,7 @@ check_unused_functions(Forms, St0) -> Opts = St1#lint.compile, case member(export_all, Opts) orelse not is_warn_enabled(unused_function, St1) of - true -> + true -> St1; false -> Nowarn = nowarn_function(nowarn_unused_function, Opts), @@ -1003,12 +1080,13 @@ check_option_functions(Forms, Tag0, Type, St0) -> {Tag, FAs0} <- lists:flatten([Args]), Tag0 =:= Tag, FA <- lists:flatten([FAs0])], - DefFunctions = gb_sets:to_list(St0#lint.defined) -- pseudolocals(), + DefFunctions = (gb_sets:to_list(St0#lint.defined) -- pseudolocals()) ++ + [{F,A} || {{F,A},_} <- orddict:to_list(St0#lint.imports)], Bad = [{FA,L} || {FA,L} <- FAsL, not member(FA, DefFunctions)], func_line_error(Type, Bad, St0). nowarn_function(Tag, Opts) -> - ordsets:from_list([FA || {Tag1,FAs} <- Opts, + ordsets:from_list([FA || {Tag1,FAs} <- Opts, Tag1 =:= Tag, FA <- lists:flatten([FAs])]). @@ -1021,11 +1099,8 @@ func_line_error(Type, Fs, St) -> check_untyped_records(Forms, St0) -> case is_warn_enabled(untyped_record, St0) of true -> - %% One possibility is to use the names of all records - %% RecNames = dict:fetch_keys(St0#lint.records), - %% but I think it's better to keep those that are used by the file - Usage = St0#lint.usage, - UsedRecNames = sets:to_list(Usage#usage.used_records), + %% Use the names of all records *defined* in the module (not used) + RecNames = dict:fetch_keys(St0#lint.records), %% these are the records with field(s) containing type info TRecNames = [Name || {attribute,_,type,{{record,Name},Fields,_}} <- Forms, @@ -1038,7 +1113,7 @@ check_untyped_records(Forms, St0) -> [] -> St; % exclude records with no fields [_|_] -> add_warning(L, {untyped_record, N}, St) end - end, St0, UsedRecNames -- TRecNames); + end, St0, RecNames -- TRecNames); false -> St0 end. @@ -1051,10 +1126,10 @@ check_unused_records(Forms, St0) -> %% functions count. Usage = St0#lint.usage, UsedRecords = sets:to_list(Usage#usage.used_records), - URecs = foldl(fun (Used, Recs) -> - dict:erase(Used, Recs) + URecs = foldl(fun (Used, Recs) -> + dict:erase(Used, Recs) end, St0#lint.records, UsedRecords), - Unused = [{Name,FileLine} || + Unused = [{Name,FileLine} || {Name,{FileLine,_Fields}} <- dict:to_list(URecs), element(1, loc(FileLine)) =:= FirstFile], foldl(fun ({N,L}, St) -> @@ -1064,18 +1139,19 @@ check_unused_records(Forms, St0) -> St0 end. -%% For storing the import list we use the orddict module. +%% For storing the import list we use the orddict module. %% We know an empty set is []. -%% export(Line, Exports, State) -> State. +-spec export(line(), [fa()], lint_state()) -> lint_state(). %% Mark functions as exported, also as called from the export line. export(Line, Es, #lint{exports = Es0, called = Called} = St0) -> - {Es1,C1,St1} = + {Es1,C1,St1} = foldl(fun (NA, {E,C,St2}) -> St = case gb_sets:is_element(NA, E) of true -> - add_warning(Line, {duplicated_export, NA}, St2); + Warn = {duplicated_export,NA}, + add_warning(Line, Warn, St2); false -> St2 end, @@ -1084,8 +1160,31 @@ export(Line, Es, #lint{exports = Es0, called = Called} = St0) -> {Es0,Called,St0}, Es), St1#lint{exports = Es1, called = C1}. -%% import(Line, Imports, State) -> State. -%% imported(Name, Arity, State) -> {yes,Module} | no. +-spec export_type(line(), [ta()], lint_state()) -> lint_state(). +%% Mark types as exported; also mark them as used from the export line. + +export_type(Line, ETs, #lint{usage = Usage, exp_types = ETs0} = St0) -> + UTs0 = Usage#usage.used_types, + try foldl(fun ({T,A}=TA, {E,U,St2}) when is_atom(T), is_integer(A) -> + St = case gb_sets:is_element(TA, E) of + true -> + Warn = {duplicated_export_type,TA}, + add_warning(Line, Warn, St2); + false -> + St2 + end, + {gb_sets:add_element(TA, E), dict:store(TA, Line, U), St} + end, + {ETs0,UTs0,St0}, ETs) of + {ETs1,UTs1,St1} -> + St1#lint{usage = Usage#usage{used_types = UTs1}, exp_types = ETs1} + catch + error:_ -> + add_error(Line, {bad_export_type, ETs}, St0) + end. + +-type import() :: {module(), [fa()]} | module(). +-spec import(line(), import(), lint_state()) -> lint_state(). import(Line, {Mod,Fs}, St) -> Mod1 = package_to_string(Mod), @@ -1097,11 +1196,41 @@ import(Line, {Mod,Fs}, St) -> St#lint{imports=add_imports(list_to_atom(Mod1), Mfs, St#lint.imports)}; Efs -> - foldl(fun (Ef, St0) -> - add_error(Line, {redefine_import,Ef}, - St0) + {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, - St, Efs) + {false,St}, Efs), + if + not Err -> + St1#lint{imports= + add_imports(list_to_atom(Mod1), Mfs, + St#lint.imports)}; + true -> + St1 + end end; false -> add_error(Line, {bad_module_name, Mod1}, St) @@ -1144,13 +1273,15 @@ check_imports(_Line, Fs, Is) -> add_imports(Mod, Fs, Is) -> foldl(fun (F, Is0) -> orddict:store(F, Mod, Is0) end, Is, Fs). +-spec imported(atom(), arity(), lint_state()) -> {'yes',module()} | 'no'. + imported(F, A, St) -> case orddict:find({F,A}, St#lint.imports) of {ok,Mod} -> {yes,Mod}; error -> no end. -%% on_load(Line, Val, State) -> State. +-spec on_load(line(), fa(), lint_state()) -> lint_state(). %% Check an on_load directive and remember it. on_load(Line, {Name,Arity}=Fa, #lint{on_load=OnLoad0}=St0) @@ -1182,7 +1313,7 @@ check_on_load(#lint{defined=Defined,on_load=[{_,0}=Fa], end; check_on_load(St) -> St. -%% call_function(Line, Name, Arity, State) -> State. +-spec call_function(line(), atom(), arity(), lint_state()) -> lint_state(). %% Add to both called and calls. call_function(Line, F, A, #lint{usage=Usage0,called=Cd,func=Func}=St) -> @@ -1194,12 +1325,6 @@ call_function(Line, F, A, #lint{usage=Usage0,called=Cd,func=Func}=St) -> end, St#lint{called=[{NA,Line}|Cd], usage=Usage}. -%% is_function_exported(Name, Arity, State) -> false|true. - -is_function_exported(Name, Arity, #lint{exports=Exports,compile=Compile}) -> - gb_sets:is_element({Name,Arity}, Exports) orelse - member(export_all, Compile). - %% function(Line, Name, Arity, Clauses, State) -> State. function(Line, instance, _Arity, _Cs, St) when St#lint.global_vt =/= [] -> @@ -1208,7 +1333,7 @@ function(Line, Name, Arity, Cs, St0) -> St1 = define_function(Line, Name, Arity, St0#lint{func={Name,Arity}}), clauses(Cs, St1#lint.global_vt, St1). -%% define_function(Line, Name, Arity, State) -> State. +-spec define_function(line(), atom(), arity(), lint_state()) -> lint_state(). define_function(Line, Name, Arity, St0) -> St1 = keyword_warning(Line, Name, St0), @@ -1217,18 +1342,18 @@ define_function(Line, Name, Arity, St0) -> true -> add_error(Line, {redefine_function,NA}, St1); false -> - St2 = St1#lint{defined=gb_sets:add_element(NA, St1#lint.defined)}, - St = case erl_internal:bif(Name, Arity) andalso - not is_function_exported(Name, Arity, St2) of - true -> add_warning(Line, {redefine_bif,NA}, St2); - false -> St2 - end, - case imported(Name, Arity, St) of - {yes,_M} -> add_error(Line, {define_import,NA}, St); - no -> St + St2 = function_check_max_args(Line, Arity, St1), + St3 = St2#lint{defined=gb_sets:add_element(NA, St2#lint.defined)}, + case imported(Name, Arity, St3) of + {yes,_M} -> add_error(Line, {define_import,NA}, St3); + no -> St3 end end. +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(Cs, Vt, St) -> @@ -1261,7 +1386,7 @@ head([P|Ps], Vt, Old, St0) -> {vtmerge_pat(Pvt, Psvt),vtmerge_pat(Bvt1,Bvt2),St2}; head([], _Vt, _Env, St) -> {[],[],St}. -%% pattern(Pattern, VarTable, Old, BinVarTable, State) -> +%% pattern(Pattern, VarTable, Old, BinVarTable, State) -> %% {UpdVarTable,BinVarTable,State}. %% Check pattern return variables. Old is the set of variables used for %% deciding whether an occurrence is a binding occurrence or a use, and @@ -1279,7 +1404,7 @@ pattern(P, Vt, St) -> pattern({var,_Line,'_'}, _Vt, _Old, _Bvt, St) -> {[],[],St}; %Ignore anonymous variable -pattern({var,Line,V}, _Vt, Old, Bvt, St) -> +pattern({var,Line,V}, _Vt, Old, Bvt, St) -> pat_var(V, Line, Old, Bvt, St); pattern({char,_Line,_C}, _Vt, _Old, _Bvt, St) -> {[],[],St}; pattern({integer,_Line,_I}, _Vt, _Old, _Bvt, St) -> {[],[],St}; @@ -1297,7 +1422,7 @@ pattern({tuple,_Line,Ps}, Vt, Old, Bvt, St) -> %%pattern({struct,_Line,_Tag,Ps}, Vt, Old, Bvt, St) -> %% pattern_list(Ps, Vt, Old, Bvt, St); pattern({record_index,Line,Name,Field}, _Vt, _Old, _Bvt, St) -> - {Vt1,St1} = + {Vt1,St1} = check_record(Line, Name, St, fun (Dfs, St1) -> pattern_field(Field, Name, Dfs, St1) @@ -1312,7 +1437,7 @@ pattern({record_field,Line,_,_}=M, _Vt, _Old, _Bvt, St0) -> end; pattern({record,Line,Name,Pfs}, Vt, Old, Bvt, St) -> case dict:find(Name, St#lint.records) of - {ok,{_Line,Fields}} -> + {ok,{_Line,Fields}} -> St1 = used_record(Name, St), pattern_fields(Pfs, Name, Fields, Vt, Old, Bvt, St1); error -> {[],[],add_error(Line, {undefined_record,Name}, St)} @@ -1372,7 +1497,7 @@ reject_bin_alias({cons,_,H1,T1}, {cons,_,H2,T2}, St0) -> reject_bin_alias(T1, T2, St); reject_bin_alias({tuple,_,Es1}, {tuple,_,Es2}, St) -> reject_bin_alias_list(Es1, Es2, St); -reject_bin_alias({record,_,Name1,Pfs1}, {record,_,Name2,Pfs2}, +reject_bin_alias({record,_,Name1,Pfs1}, {record,_,Name2,Pfs2}, #lint{records=Recs}=St) -> case {dict:find(Name1, Recs),dict:find(Name2, Recs)} of {{ok,{_Line1,Fields1}},{ok,{_Line2,Fields2}}} -> @@ -1454,7 +1579,7 @@ is_pattern_expr_1({op,_Line,Op,A1,A2}) -> erl_internal:arith_op(Op, 2) andalso all(fun is_pattern_expr/1, [A1,A2]); is_pattern_expr_1(_Other) -> false. -%% pattern_bin([Element], VarTable, Old, BinVarTable, State) -> +%% pattern_bin([Element], VarTable, Old, BinVarTable, State) -> %% {UpdVarTable,UpdBinVarTable,State}. %% Check a pattern group. BinVarTable are used binsize variables. @@ -1501,7 +1626,7 @@ good_string_size_type(default, Ts) -> end, Ts); good_string_size_type(_, _) -> false. -%% pat_bit_expr(Pattern, OldVarTable, BinVarTable,State) -> +%% pat_bit_expr(Pattern, OldVarTable, BinVarTable,State) -> %% {UpdVarTable,UpdBinVarTable,State}. %% Check pattern bit expression, only allow really valid patterns! @@ -1516,7 +1641,7 @@ pat_bit_expr(P, _Old, _Bvt, St) -> false -> {[],[],add_error(element(2, P), illegal_pattern, St)} end. -%% pat_bit_size(Size, VarTable, BinVarTable, State) -> +%% pat_bit_size(Size, VarTable, BinVarTable, State) -> %% {Value,UpdVarTable,UpdBinVarTable,State}. %% Check pattern size expression, only allow really valid sizes! @@ -1599,7 +1724,7 @@ bit_size_check(Line, Size, #bittype{type=Type,unit=Unit}, St) -> Sz = Unit * Size, %Total number of bits! St2 = elemtype_check(Line, Type, Sz, St), {Sz,St2}. - + elemtype_check(_Line, float, 32, St) -> St; elemtype_check(_Line, float, 64, St) -> St; elemtype_check(Line, float, _Size, St) -> @@ -1681,8 +1806,6 @@ gexpr({cons,_Line,H,T}, Vt, St) -> gexpr_list([H,T], Vt, St); gexpr({tuple,_Line,Es}, Vt, St) -> gexpr_list(Es, Vt, St); -%%gexpr({struct,_Line,_Tag,Es}, Vt, St) -> -%% gexpr_list(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 ); @@ -1713,7 +1836,7 @@ gexpr({call,_Line,{atom,_Lr,is_record},[E,{atom,Ln,Name}]}, Vt, St0) -> gexpr({call,Line,{atom,_Lr,is_record},[E,R]}, Vt, St0) -> {Asvt,St1} = gexpr_list([E,R], Vt, St0), {Asvt,add_error(Line, illegal_guard_expr, St1)}; -gexpr({call,Line,{remote,_Lr,{atom,_Lm,erlang},{atom,Lf,is_record}},[E,A]}, +gexpr({call,Line,{remote,_Lr,{atom,_Lm,erlang},{atom,Lf,is_record}},[E,A]}, Vt, St0) -> gexpr({call,Line,{atom,Lf,is_record},[E,A]}, Vt, St0); gexpr({call,_Line,{atom,_Lr,is_record},[E,{atom,_,_Name},{integer,_,_}]}, @@ -1728,14 +1851,22 @@ gexpr({call,Line,{remote,_,{atom,_,erlang},{atom,_,is_record}=Isr},[_,_,_]=Args} gexpr({call,Line,{atom,_La,F},As}, Vt, St0) -> {Asvt,St1} = gexpr_list(As, Vt, St0), A = length(As), - case erl_internal:guard_bif(F, A) of + %% 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; - false -> {Asvt,add_error(Line, illegal_guard_expr, St1)} + false -> + case is_local_function(St1#lint.locals,{F,A}) orelse + is_imported_function(St1#lint.imports,{F,A}) of + true -> + {Asvt,add_error(Line, {illegal_guard_local_call,{F,A}}, St1)}; + _ -> + {Asvt,add_error(Line, illegal_guard_expr, St1)} + end end; gexpr({call,Line,{remote,_Lr,{atom,_Lm,erlang},{atom,_Lf,F}},As}, Vt, St0) -> {Asvt,St1} = gexpr_list(As, Vt, St0), @@ -1774,13 +1905,16 @@ gexpr_list(Es, Vt, St) -> %% is_guard_test(Expression) -> boolean(). %% Test if a general expression is a guard test. +-spec is_guard_test(Expr) -> boolean() when + Expr :: erl_parse:abstract_expr(). + is_guard_test(E) -> is_guard_test2(E, dict:new()). %% is_guard_test(Expression, Forms) -> boolean(). is_guard_test(Expression, Forms) -> RecordAttributes = [A || A = {attribute, _, record, _D} <- Forms], - St0 = foldl(fun(Attr0, St1) -> + St0 = foldl(fun(Attr0, St1) -> Attr = zip_file_and_line(Attr0, "none"), attribute_state(Attr, St1) end, start(), RecordAttributes), @@ -1801,7 +1935,7 @@ is_guard_test2(G, RDs) -> %% is_guard_expr(Expression) -> boolean(). %% Test if an expression is a guard expression. -is_guard_expr(E) -> is_gexpr(E, []). +is_guard_expr(E) -> is_gexpr(E, []). is_gexpr({var,_L,_V}, _RDs) -> true; is_gexpr({char,_L,_C}, _RDs) -> true; @@ -1823,7 +1957,7 @@ is_gexpr({record_field,_L,Rec,_Name,Field}, RDs) -> is_gexpr({record,L,Name,Inits}, RDs) -> is_gexpr_fields(Inits, L, Name, RDs); is_gexpr({bin,_L,Fs}, RDs) -> - all(fun ({bin_element,_Line,E,Sz,_Ts}) -> + all(fun ({bin_element,_Line,E,Sz,_Ts}) -> is_gexpr(E, RDs) and (Sz =:= default orelse is_gexpr(Sz, RDs)) end, Fs); is_gexpr({call,_L,{atom,_Lf,F},As}, RDs) -> @@ -1898,15 +2032,13 @@ expr({bc,_Line,E,Qs}, Vt0, St0) -> {vtold(Vt,Vt0),St}; %Don't export local variables expr({tuple,_Line,Es}, Vt, St) -> expr_list(Es, Vt, St); -%%expr({struct,Line,Tag,Es}, Vt, St) -> -%% expr_list(Es, Vt, St); expr({record_index,Line,Name,Field}, _Vt, St) -> check_record(Line, Name, St, fun (Dfs, St1) -> record_field(Field, Name, Dfs, St1) end); expr({record,Line,Name,Inits}, Vt, St) -> check_record(Line, Name, St, - fun (Dfs, St1) -> - init_fields(Inits, Line, Name, Dfs, Vt, St1) + 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 @@ -1943,8 +2075,6 @@ expr({'case',Line,E,Cs}, Vt, St0) -> {Evt,St1} = expr(E, Vt, St0), {Cvt,St2} = icrt_clauses(Cs, {'case',Line}, vtupdate(Evt, Vt), St1), {vtmerge(Evt, Cvt),St2}; -expr({'cond',Line,Cs}, Vt, St) -> - cond_clauses(Cs,{'cond',Line}, Vt, St); expr({'receive',Line,Cs}, Vt, St) -> icrt_clauses(Cs, {'receive',Line}, Vt, St); expr({'receive',Line,Cs,To,ToEs}, Vt, St0) -> @@ -1963,8 +2093,11 @@ expr({'fun',Line,Body}, Vt, St) -> {Bvt, St1} = fun_clauses(Cs, Vt, St), {vtupdate(Bvt, Vt), St1}; {function,F,A} -> + %% BifClash - Fun expression %% N.B. Only allows BIFs here as well, NO IMPORTS!! - case erl_internal:bif(F, A) of + case ((not is_local_function(St#lint.locals,{F,A})) andalso + (erl_internal:bif(F, A) andalso + (not is_autoimport_suppressed(St#lint.no_auto,{F,A})))) of true -> {[],St}; false -> {[],call_function(Line, F, A, St)} end; @@ -1974,7 +2107,7 @@ expr({'fun',Line,Body}, Vt, St) -> expr({call,_Line,{atom,_Lr,is_record},[E,{atom,Ln,Name}]}, Vt, St0) -> {Rvt,St1} = expr(E, Vt, St0), {Rvt,exist_record(Ln, Name, St1)}; -expr({call,Line,{remote,_Lr,{atom,_Lm,erlang},{atom,Lf,is_record}},[E,A]}, +expr({call,Line,{remote,_Lr,{atom,_Lm,erlang},{atom,Lf,is_record}},[E,A]}, Vt, St0) -> 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) -> @@ -1997,29 +2130,54 @@ expr({call,Line,{atom,La,F},As}, Vt, St0) -> St1 = keyword_warning(La, F, St0), {Asvt,St2} = expr_list(As, Vt, St1), A = length(As), - case erl_internal:bif(F, A) of + IsLocal = is_local_function(St2#lint.locals,{F,A}), + IsAutoBif = erl_internal:bif(F, A), + AutoSuppressed = is_autoimport_suppressed(St2#lint.no_auto,{F,A}), + Warn = is_warn_enabled(bif_clash, St2) and (not bif_clash_specifically_disabled(St2,{F,A})), + Imported = imported(F, A, St2), + case ((not IsLocal) andalso (Imported =:= no) andalso + IsAutoBif andalso (not AutoSuppressed)) of true -> St3 = deprecated_function(Line, erlang, F, As, St2), - {Asvt,case is_warn_enabled(bif_clash, St3) andalso - is_bif_clash(F, A, St3) of - false -> - St3; - true -> - add_error(Line, {call_to_redefined_bif,{F,A}}, St3) - end}; + {Asvt,St3}; false -> - {Asvt,case imported(F, A, St2) of + {Asvt,case Imported of {yes,M} -> St3 = check_remote_function(Line, M, F, As, St2), U0 = St3#lint.usage, Imp = ordsets:add_element({{F,A},M},U0#usage.imported), St3#lint{usage=U0#usage{imported = Imp}}; no -> - case {F,A} of - {record_info,2} -> + case {F,A} of + {record_info,2} -> check_record_info_call(Line,La,As,St2); - N when N =:= St2#lint.func -> St2; - _ -> call_function(Line, F, A, St2) + N -> + %% BifClash - function call + %% Issue these warnings/errors even if it's a recursive call + St3 = if + (not AutoSuppressed) andalso IsAutoBif andalso Warn -> + case erl_internal:old_bif(F,A) of + true -> + add_error + (Line, + {call_to_redefined_old_bif, {F,A}}, + St2); + false -> + add_warning + (Line, + {call_to_redefined_bif, {F,A}}, + St2) + end; + true -> + St2 + end, + %% ...but don't lint recursive calls + if + N =:= St3#lint.func -> + St3; + true -> + call_function(Line, F, A, St3) + end end end} end; @@ -2160,7 +2318,7 @@ def_fields(Fs0, Name, St0) -> foldl(fun ({record_field,Lf,{atom,La,F},V}, {Fs,St}) -> case exist_field(F, Fs) of true -> {Fs,add_error(Lf, {redefine_field,Name,F}, St)}; - false -> + false -> St1 = St#lint{recdef_top = true}, {_,St2} = expr(V, [], St1), %% Warnings and errors found are kept, but @@ -2311,7 +2469,7 @@ init_fields(Ifs, Line, Name, Dfs, Vt0, St0) -> Defs = init_fields(Ifs, Line, Dfs), {_,St2} = check_fields(Defs, Name, Dfs, Vt1, St1, fun expr/3), {Vt1,St1#lint{usage = St2#lint.usage}}. - + ginit_fields(Ifs, Line, Name, Dfs, Vt0, St0) -> {Vt1,St1} = check_fields(Ifs, Name, Dfs, Vt0, St0, fun gexpr/3), Defs = init_fields(Ifs, Line, Dfs), @@ -2321,7 +2479,7 @@ ginit_fields(Ifs, Line, Name, Dfs, Vt0, St0) -> IllErrs = [E || {_File,{_Line,erl_lint,illegal_guard_expr}}=E <- Errors], St4 = St1#lint{usage = Usage, errors = IllErrs ++ St1#lint.errors}, {Vt1,St4}. - + %% Default initializations to be carried out init_fields(Ifs, Line, Dfs) -> [ {record_field,Lf,{atom,La,F},copy_expr(Di, Line)} || @@ -2399,7 +2557,7 @@ check_type({ann_type, _L, [_Var, Type]}, SeenVars, St) -> check_type(Type, SeenVars, St); check_type({paren_type, _L, [Type]}, SeenVars, St) -> check_type(Type, SeenVars, St); -check_type({remote_type, L, [{atom, _, Mod}, {atom, _, Name}, Args]}, +check_type({remote_type, L, [{atom, _, Mod}, {atom, _, Name}, Args]}, SeenVars, #lint{module=CurrentMod} = St) -> St1 = case (dict:is_key({Name, length(Args)}, default_types()) @@ -2437,7 +2595,7 @@ check_type({type, L, 'fun', [Dom, Range]}, SeenVars, St) -> check_type({type, -1, product, [Dom, Range]}, SeenVars, St1); check_type({type, L, range, [From, To]}, SeenVars, St) -> St1 = - case {From, To} of + case {erl_eval:partial_eval(From), erl_eval:partial_eval(To)} of {{integer, _, X}, {integer, _, Y}} when X < Y -> St; _ -> add_error(L, {type_syntax, range}, St) end, @@ -2446,8 +2604,8 @@ check_type({type, _L, tuple, any}, SeenVars, St) -> {SeenVars, St}; check_type({type, _L, any}, SeenVars, St) -> {SeenVars, St}; check_type({type, L, binary, [Base, Unit]}, SeenVars, St) -> St1 = - case {Base, Unit} of - {{integer, _, BaseVal}, + case {erl_eval:partial_eval(Base), erl_eval:partial_eval(Unit)} of + {{integer, _, BaseVal}, {integer, _, UnitVal}} when BaseVal >= 0, UnitVal >= 0 -> St; _ -> add_error(L, {type_syntax, binary}, St) end, @@ -2472,7 +2630,13 @@ check_type({type, La, TypeName, Args}, SeenVars, #lint{usage=Usage} = St) -> UsedTypes = dict:store({TypeName, Arity}, La, OldUsed), St#lint{usage=Usage#usage{used_types=UsedTypes}} end, - check_type({type, -1, product, Args}, SeenVars, St1). + check_type({type, -1, product, Args}, SeenVars, St1); +check_type(I, SeenVars, St) -> + case erl_eval:partial_eval(I) of + {integer,_ILn,_Integer} -> {SeenVars, St}; + _Other -> + {SeenVars, add_error(element(2, I), {type_syntax, integer}, St)} + end. check_record_types(Line, Name, Fields, SeenVars, St) -> case dict:find(Name, St#lint.records) of @@ -2480,12 +2644,12 @@ check_record_types(Line, Name, Fields, SeenVars, St) -> case lists:all(fun({type, _, field_type, _}) -> true; (_) -> false end, Fields) of - true -> + true -> check_record_types(Fields, Name, DefFields, SeenVars, St, []); false -> {SeenVars, add_error(Line, {type_syntax, record}, St)} end; - error -> + error -> {SeenVars, add_error(Line, {undefined_record, Name}, St)} end. @@ -2568,7 +2732,6 @@ default_types() -> {set, 0}, {string, 0}, {term, 0}, - {tid, 0}, {timeout, 0}, {var, 1}], dict:from_list([{T, -1} || T <- DefTypes]). @@ -2590,7 +2753,6 @@ is_newly_introduced_builtin_type({gb_tree, 0}) -> true; % opaque is_newly_introduced_builtin_type({iodata, 0}) -> true; is_newly_introduced_builtin_type({queue, 0}) -> true; % opaque is_newly_introduced_builtin_type({set, 0}) -> true; % opaque -is_newly_introduced_builtin_type({tid, 0}) -> true; % opaque %% R13B01 is_newly_introduced_builtin_type({boolean, 0}) -> true; is_newly_introduced_builtin_type({Name, _}) when is_atom(Name) -> false. @@ -2611,7 +2773,7 @@ spec_decl(Line, MFA0, TypeSpecs, St0 = #lint{specs = Specs, module = Mod}) -> check_specs([FunType|Left], Arity, St0) -> {FunType1, CTypes} = case FunType of - {type, _, bounded_fun, [FT = {type, _, 'fun', _}, Cs]} -> + {type, _, bounded_fun, [FT = {type, _, 'fun', _}, Cs]} -> Types0 = [T || {type, _, constraint, [_, T]} <- Cs], {FT, lists:append(Types0)}; {type, _, 'fun', _} = FT -> {FT, []} @@ -2671,10 +2833,12 @@ add_missing_spec_warnings(Forms, St0, Type) -> add_warning(L, {missing_spec,FA}, St) end, St0, Warns). -check_unused_types(Forms, St = #lint{usage=Usage, types=Types}) -> +check_unused_types(Forms, #lint{usage=Usage, types=Ts, exp_types=ExpTs}=St) -> case [File || {attribute,_L,file,{File,_Line}} <- Forms] of [FirstFile|_] -> - UsedTypes = Usage#usage.used_types, + D = Usage#usage.used_types, + L = gb_sets:to_list(ExpTs) ++ dict:fetch_keys(D), + UsedTypes = gb_sets:from_list(L), FoldFun = fun(_Type, -1, AccSt) -> %% Default type @@ -2682,19 +2846,18 @@ check_unused_types(Forms, St = #lint{usage=Usage, types=Types}) -> (Type, FileLine, AccSt) -> case loc(FileLine) of {FirstFile, _} -> - case dict:is_key(Type, UsedTypes) of + case gb_sets:is_member(Type, UsedTypes) of true -> AccSt; - false -> - add_warning(FileLine, - {unused_type, Type}, - AccSt) + false -> + Warn = {unused_type,Type}, + add_warning(FileLine, Warn, AccSt) end; _ -> - %% Don't warn about unused types in include file + %% No warns about unused types in include files AccSt end end, - dict:fold(FoldFun, St, Types); + dict:fold(FoldFun, St, Ts); [] -> St end. @@ -2720,45 +2883,6 @@ icrt_clause({clause,_Line,H,G,B}, Vt0, St0) -> {Bvt,St3} = exprs(B, Vt2, St2), {vtupdate(Bvt, Vt2),St3}. -%% The tests of 'cond' clauses are normal expressions - not guards. -%% Variables bound in a test is visible both in the corresponding body -%% and in the tests and bodies of subsequent clauses: a 'cond' is -%% *equivalent* to nested case-switches on boolean expressions. - -cond_clauses([C], In, Vt, St) -> - last_cond_clause(C, In, Vt, St); -cond_clauses([C | Cs], In, Vt, St) -> - cond_clause(C, Cs, In, Vt, St). - -%% see expr/3 for 'case' -cond_clause({clause,_L,[],[[E]],B}, Cs, In, Vt, St0) -> - {Evt,St1} = expr(E, Vt, St0), - {Cvt, St2} = cond_cases(B, Cs, In, vtupdate(Evt, Vt), St1), - Mvt = vtmerge(Evt, Cvt), - {Mvt,St2}. - -%% see icrt_clauses/4 -cond_cases(B, Cs, In, Vt, St0) -> - %% note that Vt is used for both cases - {Bvt,St1} = exprs(B, Vt, St0), % true case - Vt1 = vtupdate(Bvt, Vt), - {Cvt, St2} = cond_clauses(Cs, In, Vt, St1), % false case - Vt2 = vtupdate(Cvt, Vt), - %% and this also uses Vt - icrt_export([Vt1,Vt2], Vt, In, St2). - -%% last case must call icrt_export/4 with only one vartable -last_cond_clause({clause,_L,[],[[E]],B}, In, Vt, St0) -> - {Evt,St1} = expr(E, Vt, St0), - {Cvt, St2} = last_cond_case(B, In, vtupdate(Evt, Vt), St1), - Mvt = vtmerge(Evt, Cvt), - {Mvt,St2}. - -last_cond_case(B, In, Vt, St0) -> - {Bvt,St1} = exprs(B, Vt, St0), - Vt1 = vtupdate(Bvt, Vt), - icrt_export([Vt1], Vt, In, St1). - icrt_export(Csvt, Vt, In, St) -> Vt1 = vtmerge(Csvt), All = ordsets:subtract(vintersection(Csvt), vtnames(Vt)), @@ -2878,7 +3002,7 @@ fun_clause({clause,_Line,H,G,B}, Vt0, St0) -> %% %% used variable has been used %% unused variable has been bound but not used -%% +%% %% Lines is a list of line numbers where the variable was bound. %% %% Report variable errors/warnings as soon as possible and then change @@ -2908,9 +3032,9 @@ pat_var(V, Line, Vt, Bvt, St) -> case orddict:find(V, Bvt) of {ok, {bound,_Usage,Ls}} -> {[],[{V,{bound,used,Ls}}],St}; - error -> + error -> case orddict:find(V, Vt) of - {ok,{bound,_Usage,Ls}} -> + {ok,{bound,_Usage,Ls}} -> {[{V,{bound,used,Ls}}],[],St}; {ok,{{unsafe,In},_Usage,Ls}} -> {[{V,{bound,used,Ls}}],[], @@ -2963,7 +3087,7 @@ pat_binsize_var(V, Line, Vt, Bvt, St) -> expr_var(V, Line, Vt, St0) -> case orddict:find(V, Vt) of - {ok,{bound,_Usage,Ls}} -> + {ok,{bound,_Usage,Ls}} -> {[{V,{bound,used,Ls}}],St0}; {ok,{{unsafe,In},_Usage,Ls}} -> {[{V,{bound,used,Ls}}], @@ -3001,7 +3125,7 @@ check_old_unused_vars(Vt, Vt0, St0) -> warn_unused_vars(U, Vt, St0). unused_vars(Vt, Vt0, _St0) -> - U0 = orddict:filter(fun (V, {_State,unused,_Ls}) -> + U0 = orddict:filter(fun (V, {_State,unused,_Ls}) -> case atom_to_list(V) of "_"++_ -> false; _ -> true @@ -3017,7 +3141,7 @@ warn_unused_vars(U, Vt, St0) -> false -> St0; true -> foldl(fun ({V,{_,unused,Ls}}, St) -> - foldl(fun (L, St2) -> + foldl(fun (L, St2) -> add_warning(L, {unused_var,V}, St2) end, St, Ls) @@ -3117,7 +3241,7 @@ vt_no_unsafe(Vt) -> [V || {_,{S,_U,_L}}=V <- Vt, -ifdef(NOTUSED). vunion(Vs1, Vs2) -> ordsets:union(vtnames(Vs1), vtnames(Vs2)). -vunion(Vss) -> foldl(fun (Vs, Uvs) -> +vunion(Vss) -> foldl(fun (Vs, Uvs) -> ordsets:union(vtnames(Vs), Uvs) end, [], Vss). @@ -3147,7 +3271,7 @@ modify_line(T, F0) -> %% Forms. modify_line1({function,F,A}, _Mf) -> {function,F,A}; modify_line1({function,M,F,A}, _Mf) -> {function,M,F,A}; -modify_line1({attribute,L,record,{Name,Fields}}, Mf) -> +modify_line1({attribute,L,record,{Name,Fields}}, Mf) -> {attribute,Mf(L),record,{Name,modify_line1(Fields, Mf)}}; modify_line1({attribute,L,spec,{Fun,Types}}, Mf) -> {attribute,Mf(L),spec,{Fun,modify_line1(Types, Mf)}}; @@ -3162,7 +3286,7 @@ modify_line1({warning,W}, _Mf) -> {warning,W}; modify_line1({error,W}, _Mf) -> {error,W}; %% Expressions. modify_line1({clauses,Cs}, Mf) -> {clauses,modify_line1(Cs, Mf)}; -modify_line1({typed_record_field,Field,Type}, Mf) -> +modify_line1({typed_record_field,Field,Type}, Mf) -> {typed_record_field,modify_line1(Field, Mf),modify_line1(Type, Mf)}; modify_line1({Tag,L}, Mf) -> {Tag,Mf(L)}; modify_line1({Tag,L,E1}, Mf) -> @@ -3198,7 +3322,7 @@ check_record_info_call(Line,_La,_As,St) -> has_wildcard_field([{record_field,_Lf,{var,_La,'_'},_Val}|_Fs]) -> true; has_wildcard_field([_|Fs]) -> has_wildcard_field(Fs); has_wildcard_field([]) -> false. - + %% check_remote_function(Line, ModuleName, FuncName, [Arg], State) -> State. %% Perform checks on known remote calls. @@ -3214,7 +3338,7 @@ check_remote_function(Line, M, F, As, St0) -> check_qlc_hrl(Line, M, F, As, St) -> Arity = length(As), case As of - [{lc,_L,_E,_Qs}|_] when M =:= qlc, F =:= q, + [{lc,_L,_E,_Qs}|_] when M =:= qlc, F =:= q, Arity < 3, not St#lint.xqlc -> add_warning(Line, {missing_qlc_hrl, Arity}, St); _ -> @@ -3399,11 +3523,11 @@ extract_sequence(3, [$.,_|Fmt], Need) -> extract_sequence(4, Fmt, Need); extract_sequence(3, Fmt, Need) -> extract_sequence(4, Fmt, Need); -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, C | _Fmt], _Need) -> +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, C | _Fmt], _Need) -> {error,"invalid control ~t" ++ [C]}; extract_sequence(4, Fmt, Need) -> extract_sequence(5, Fmt, Need); @@ -3481,3 +3605,56 @@ expand_package(M, St0) -> {error, St1} end end. + + +%% Prebuild set of local functions (to override auto-import) +local_functions(Forms) -> + gb_sets:from_list([ {Func,Arity} || {function,_,Func,Arity,_} <- Forms ]). +%% Predicate to find out if the function is locally defined +is_local_function(LocalSet,{Func,Arity}) -> + gb_sets:is_element({Func,Arity},LocalSet). +%% Predicate to see if a function is explicitly imported +is_imported_function(ImportSet,{Func,Arity}) -> + case orddict:find({Func,Arity}, ImportSet) of + {ok,_Mod} -> true; + error -> false + end. +%% Predicate to see if a function is explicitly imported from the erlang module +is_imported_from_erlang(ImportSet,{Func,Arity}) -> + case orddict:find({Func,Arity}, ImportSet) of + {ok,erlang} -> true; + _ -> false + end. +%% Build set of functions where auto-import is explicitly supressed +auto_import_suppressed(CompileFlags) -> + L0 = [ X || {no_auto_import,X} <- CompileFlags ], + L1 = [ {Y,Z} || {Y,Z} <- lists:flatten(L0), is_atom(Y), is_integer(Z) ], + gb_sets:from_list(L1). +%% Predicate to find out if autoimport is explicitly supressed for a function +is_autoimport_suppressed(NoAutoSet,{Func,Arity}) -> + gb_sets:is_element({Func,Arity},NoAutoSet). +%% Predicate to find out if a function specific bif-clash supression (old deprecated) is present +bif_clash_specifically_disabled(St,{F,A}) -> + Nowarn = nowarn_function(nowarn_bif_clash, St#lint.compile), + lists:member({F,A},Nowarn). + +%% Predicate to find out if an autoimported guard_bif is not overriden in some way +%% Guard Bif without module name is disallowed if +%% * It is overridden by local function +%% * It is overridden by -import and that import is not of itself (i.e. from module erlang) +%% * The autoimport is suppressed or it's not reimported by -import directive +%% Otherwise it's OK (given that it's actually a guard bif and actually is autoimported) +no_guard_bif_clash(St,{F,A}) -> + ( + (not is_local_function(St#lint.locals,{F,A})) + andalso + ( + (not is_imported_function(St#lint.imports,{F,A})) orelse + is_imported_from_erlang(St#lint.imports,{F,A}) + ) + andalso + ( + (not is_autoimport_suppressed(St#lint.no_auto, {F,A})) orelse + is_imported_from_erlang(St#lint.imports,{F,A}) + ) + ). diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl index fd5d905797..bd5d65a1e1 100644 --- a/lib/stdlib/src/erl_parse.yrl +++ b/lib/stdlib/src/erl_parse.yrl @@ -1,20 +1,20 @@ %% -*- erlang -*- %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1996-2011. 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% %% @@ -30,14 +30,12 @@ expr_600 expr_700 expr_800 expr_900 expr_max list tail list_comprehension lc_expr lc_exprs -binary_comprehension +binary_comprehension tuple -atom1 %struct 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 -%% cond_expr cond_clause cond_clauses try_expr try_catch try_clause try_clauses query_expr function_call argument_list exprs guard @@ -49,22 +47,22 @@ opt_bit_size_expr bit_size_expr opt_bit_type_list bit_type_list bit_type top_type top_type_100 top_types type typed_expr typed_attr_val type_sig type_sigs type_guard type_guards fun_type fun_type_100 binary_type type_spec spec_fun typed_exprs typed_record_fields field_types field_type -bin_base_type bin_unit_type int_type. +bin_base_type bin_unit_type type_200 type_300 type_400 type_500. Terminals char integer float atom string var '(' ')' ',' '->' ':-' '{' '}' '[' ']' '|' '||' '<-' ';' ':' '#' '.' 'after' 'begin' 'case' 'try' 'catch' 'end' 'fun' 'if' 'of' 'receive' 'when' -'andalso' 'orelse' 'query' 'spec' -%% 'cond' +'andalso' 'orelse' 'query' 'bnot' 'not' '*' '/' 'div' 'rem' 'band' 'and' '+' '-' 'bor' 'bxor' 'bsl' 'bsr' 'or' 'xor' '++' '--' '==' '/=' '=<' '<' '>=' '>' '=:=' '=/=' '<=' '<<' '>>' -'!' '=' '::' +'!' '=' '::' '..' '...' +'spec' % helper dot. Expect 2. @@ -79,19 +77,16 @@ attribute -> '-' atom attr_val : build_attribute('$2', '$3'). attribute -> '-' atom typed_attr_val : build_typed_attribute('$2','$3'). attribute -> '-' atom '(' typed_attr_val ')' : build_typed_attribute('$2','$4'). attribute -> '-' 'spec' type_spec : build_type_spec('$2', '$3'). - -atom1 -> 'spec' : {atom, ?line('$1'), 'spec'}. -atom1 -> atom : '$1'. type_spec -> spec_fun type_sigs : {'$1', '$2'}. type_spec -> '(' spec_fun type_sigs ')' : {'$2', '$3'}. -spec_fun -> atom1 : '$1'. -spec_fun -> atom1 ':' atom1 : {'$1', '$3'}. +spec_fun -> atom : '$1'. +spec_fun -> atom ':' atom : {'$1', '$3'}. %% The following two are retained only for backwards compatibility; %% they are not part of the EEP syntax and should be removed. -spec_fun -> atom1 '/' integer '::' : {'$1', '$3'}. -spec_fun -> atom1 ':' atom1 '/' integer '::' : {'$1', '$3', '$5'}. +spec_fun -> atom '/' integer '::' : {'$1', '$3'}. +spec_fun -> atom ':' atom '/' integer '::' : {'$1', '$3', '$5'}. typed_attr_val -> expr ',' typed_record_fields : {typed_record, '$1', '$3'}. typed_attr_val -> expr '::' top_type : {type_def, '$1', '$3'}. @@ -109,14 +104,15 @@ type_sigs -> type_sig : ['$1']. type_sigs -> type_sig ';' type_sigs : ['$1'|'$3']. type_sig -> fun_type : '$1'. -type_sig -> fun_type 'when' type_guards : {type, ?line('$1'), bounded_fun, +type_sig -> fun_type 'when' type_guards : {type, ?line('$1'), bounded_fun, ['$1','$3']}. type_guards -> type_guard : ['$1']. type_guards -> type_guard ',' type_guards : ['$1'|'$3']. -type_guard -> atom1 '(' top_types ')' : {type, ?line('$1'), constraint, +type_guard -> atom '(' top_types ')' : {type, ?line('$1'), constraint, ['$1', '$3']}. +type_guard -> var '::' top_type : build_def('$1', '$3'). top_types -> top_type : ['$1']. top_types -> top_type ',' top_types : ['$1'|'$3']. @@ -124,58 +120,68 @@ top_types -> top_type ',' top_types : ['$1'|'$3']. top_type -> var '::' top_type_100 : {ann_type, ?line('$1'), ['$1','$3']}. top_type -> top_type_100 : '$1'. -top_type_100 -> type : '$1'. -top_type_100 -> type '|' top_type_100 : lift_unions('$1','$3'). +top_type_100 -> type_200 : '$1'. +top_type_100 -> type_200 '|' top_type_100 : lift_unions('$1','$3'). + +type_200 -> type_300 '..' type_300 : {type, ?line('$1'), range, + [skip_paren('$1'), + skip_paren('$3')]}. +type_200 -> type_300 : '$1'. + +type_300 -> type_300 add_op type_400 : ?mkop2(skip_paren('$1'), + '$2', skip_paren('$3')). +type_300 -> type_400 : '$1'. + +type_400 -> type_400 mult_op type_500 : ?mkop2(skip_paren('$1'), + '$2', skip_paren('$3')). +type_400 -> type_500 : '$1'. + +type_500 -> prefix_op type : ?mkop1('$1', skip_paren('$2')). +type_500 -> type : '$1'. type -> '(' top_type ')' : {paren_type, ?line('$2'), ['$2']}. type -> var : '$1'. -type -> atom1 : '$1'. -type -> atom1 '(' ')' : build_gen_type('$1'). -type -> atom1 '(' top_types ')' : {type, ?line('$1'), +type -> atom : '$1'. +type -> atom '(' ')' : build_gen_type('$1'). +type -> atom '(' top_types ')' : {type, ?line('$1'), normalise('$1'), '$3'}. -type -> atom1 ':' atom1 '(' ')' : {remote_type, ?line('$1'), +type -> atom ':' atom '(' ')' : {remote_type, ?line('$1'), ['$1', '$3', []]}. -type -> atom1 ':' atom1 '(' top_types ')' : {remote_type, ?line('$1'), +type -> atom ':' atom '(' top_types ')' : {remote_type, ?line('$1'), ['$1', '$3', '$5']}. type -> '[' ']' : {type, ?line('$1'), nil, []}. type -> '[' top_type ']' : {type, ?line('$1'), list, ['$2']}. -type -> '[' top_type ',' '.' '.' '.' ']' : {type, ?line('$1'), +type -> '[' top_type ',' '...' ']' : {type, ?line('$1'), nonempty_list, ['$2']}. type -> '{' '}' : {type, ?line('$1'), tuple, []}. type -> '{' top_types '}' : {type, ?line('$1'), tuple, '$2'}. -type -> '#' atom1 '{' '}' : {type, ?line('$1'), record, ['$2']}. -type -> '#' atom1 '{' field_types '}' : {type, ?line('$1'), +type -> '#' atom '{' '}' : {type, ?line('$1'), record, ['$2']}. +type -> '#' atom '{' field_types '}' : {type, ?line('$1'), record, ['$2'|'$4']}. type -> binary_type : '$1'. -type -> int_type : '$1'. -type -> int_type '.' '.' int_type : {type, ?line('$1'), range, - ['$1', '$4']}. +type -> integer : '$1'. type -> 'fun' '(' ')' : {type, ?line('$1'), 'fun', []}. type -> 'fun' '(' fun_type_100 ')' : '$3'. -int_type -> integer : '$1'. -int_type -> '-' integer : abstract(-normalise('$2'), - ?line('$2')). - -fun_type_100 -> '(' '.' '.' '.' ')' '->' top_type +fun_type_100 -> '(' '...' ')' '->' top_type : {type, ?line('$1'), 'fun', - [{type, ?line('$1'), any}, '$7']}. + [{type, ?line('$1'), any}, '$5']}. fun_type_100 -> fun_type : '$1'. fun_type -> '(' ')' '->' top_type : {type, ?line('$1'), 'fun', [{type, ?line('$1'), product, []}, '$4']}. -fun_type -> '(' top_types ')' '->' top_type +fun_type -> '(' top_types ')' '->' top_type : {type, ?line('$1'), 'fun', [{type, ?line('$1'), product, '$2'},'$5']}. field_types -> field_type : ['$1']. field_types -> field_type ',' field_types : ['$1'|'$3']. -field_type -> atom1 '::' top_type : {type, ?line('$1'), field_type, +field_type -> atom '::' top_type : {type, ?line('$1'), field_type, ['$1', '$3']}. -binary_type -> '<<' '>>' : {type, ?line('$1'),binary, - [abstract(0, ?line('$1')), +binary_type -> '<<' '>>' : {type, ?line('$1'),binary, + [abstract(0, ?line('$1')), abstract(0, ?line('$1'))]}. binary_type -> '<<' bin_base_type '>>' : {type, ?line('$1'),binary, ['$2', abstract(0, ?line('$1'))]}. @@ -184,9 +190,9 @@ binary_type -> '<<' bin_unit_type '>>' : {type, ?line('$1'),binary, binary_type -> '<<' bin_base_type ',' bin_unit_type '>>' : {type, ?line('$1'), binary, ['$2', '$4']}. -bin_base_type -> var ':' integer : build_bin_type(['$1'], '$3'). +bin_base_type -> var ':' type : build_bin_type(['$1'], '$3'). -bin_unit_type -> var ':' var '*' integer : build_bin_type(['$1', '$3'], '$5'). +bin_unit_type -> var ':' var '*' type : build_bin_type(['$1', '$3'], '$5'). attr_val -> expr : ['$1']. attr_val -> expr ',' exprs : ['$1' | '$3']. @@ -197,7 +203,7 @@ function -> function_clauses : build_function('$1'). function_clauses -> function_clause : ['$1']. function_clauses -> function_clause ';' function_clauses : ['$1'|'$3']. -function_clause -> atom1 clause_args clause_guard clause_body : +function_clause -> atom clause_args clause_guard clause_body : {clause,?line('$1'),element(3, '$1'),'$2','$3','$4'}. @@ -250,9 +256,9 @@ expr_800 -> expr_900 ':' expr_max : {remote,?line('$2'),'$1','$3'}. expr_800 -> expr_900 : '$1'. -expr_900 -> '.' atom1 : +expr_900 -> '.' atom : {record_field,?line('$1'),{atom,?line('$1'),''},'$2'}. -expr_900 -> expr_900 '.' atom1 : +expr_900 -> expr_900 '.' atom : {record_field,?line('$2'),'$1','$3'}. expr_900 -> expr_max : '$1'. @@ -270,7 +276,6 @@ expr_max -> if_expr : '$1'. expr_max -> case_expr : '$1'. expr_max -> receive_expr : '$1'. expr_max -> fun_expr : '$1'. -%%expr_max -> cond_expr : '$1'. expr_max -> try_expr : '$1'. expr_max -> query_expr : '$1'. @@ -304,8 +309,8 @@ opt_bit_type_list -> '$empty' : default. bit_type_list -> bit_type '-' bit_type_list : ['$1' | '$3']. bit_type_list -> bit_type : ['$1']. -bit_type -> atom1 : element(3,'$1'). -bit_type -> atom1 ':' integer : { element(3,'$1'), element(3,'$3') }. +bit_type -> atom : element(3,'$1'). +bit_type -> atom ':' integer : { element(3,'$1'), element(3,'$3') }. bit_size_expr -> expr_max : '$1'. @@ -325,7 +330,7 @@ tuple -> '{' '}' : {tuple,?line('$1'),[]}. tuple -> '{' exprs '}' : {tuple,?line('$1'),'$2'}. -%%struct -> atom1 tuple : +%%struct -> atom tuple : %% {struct,?line('$1'),element(3, '$1'),element(3, '$2')}. @@ -333,13 +338,17 @@ tuple -> '{' exprs '}' : {tuple,?line('$1'),'$2'}. %% N.B. Field names are returned as the complete object, even if they are %% always atoms for the moment, this might change in the future. -record_expr -> '#' atom1 '.' atom1 : +record_expr -> '#' atom '.' atom : {record_index,?line('$1'),element(3, '$2'),'$4'}. -record_expr -> '#' atom1 record_tuple : +record_expr -> '#' atom record_tuple : {record,?line('$1'),element(3, '$2'),'$3'}. -record_expr -> expr_max '#' atom1 '.' atom1 : +record_expr -> expr_max '#' atom '.' atom : + {record_field,?line('$2'),'$1',element(3, '$3'),'$5'}. +record_expr -> expr_max '#' atom record_tuple : + {record,?line('$2'),'$1',element(3, '$3'),'$4'}. +record_expr -> record_expr '#' atom '.' atom : {record_field,?line('$2'),'$1',element(3, '$3'),'$5'}. -record_expr -> expr_max '#' atom1 record_tuple : +record_expr -> record_expr '#' atom record_tuple : {record,?line('$2'),'$1',element(3, '$3'),'$4'}. record_tuple -> '{' '}' : []. @@ -349,7 +358,7 @@ record_fields -> record_field : ['$1']. record_fields -> record_field ',' record_fields : ['$1' | '$3']. record_field -> var '=' expr : {record_field,?line('$1'),'$1','$3'}. -record_field -> atom1 '=' expr : {record_field,?line('$1'),'$1','$3'}. +record_field -> atom '=' expr : {record_field,?line('$1'),'$1','$3'}. %% N.B. This is called from expr_700. @@ -383,9 +392,9 @@ receive_expr -> 'receive' cr_clauses 'after' expr clause_body 'end' : {'receive',?line('$1'),'$2','$4','$5'}. -fun_expr -> 'fun' atom1 '/' integer : +fun_expr -> 'fun' atom '/' integer : {'fun',?line('$1'),{function,element(3, '$2'),element(3, '$4')}}. -fun_expr -> 'fun' atom1 ':' atom1 '/' integer : +fun_expr -> 'fun' atom ':' atom '/' integer : {'fun',?line('$1'),{function,element(3, '$2'),element(3, '$4'),element(3,'$6')}}. fun_expr -> 'fun' fun_clauses 'end' : build_fun(?line('$1'), '$2'). @@ -415,21 +424,13 @@ try_clauses -> try_clause ';' try_clauses : ['$1' | '$3']. try_clause -> expr clause_guard clause_body : L = ?line('$1'), {clause,L,[{tuple,L,[{atom,L,throw},'$1',{var,L,'_'}]}],'$2','$3'}. -try_clause -> atom1 ':' expr clause_guard clause_body : +try_clause -> atom ':' expr clause_guard clause_body : L = ?line('$1'), {clause,L,[{tuple,L,['$1','$3',{var,L,'_'}]}],'$4','$5'}. try_clause -> var ':' expr clause_guard clause_body : L = ?line('$1'), {clause,L,[{tuple,L,['$1','$3',{var,L,'_'}]}],'$4','$5'}. -%%cond_expr -> 'cond' cond_clauses 'end' : {'cond',?line('$1'),'$2'}. - -%%cond_clauses -> cond_clause : ['$1']. -%%cond_clauses -> cond_clause ';' cond_clauses : ['$1' | '$3']. - -%%cond_clause -> expr clause_body : -%% {clause,?line('$1'),[],[['$1']],'$2'}. - query_expr -> 'query' list_comprehension 'end' : {'query',?line('$1'),'$2'}. @@ -447,7 +448,7 @@ guard -> exprs ';' guard : ['$1'|'$3']. atomic -> char : '$1'. atomic -> integer : '$1'. atomic -> float : '$1'. -atomic -> atom1 : '$1'. +atomic -> atom : '$1'. atomic -> strings : '$1'. strings -> string : '$1'. @@ -492,7 +493,7 @@ rule -> rule_clauses : build_rule('$1'). rule_clauses -> rule_clause : ['$1']. rule_clauses -> rule_clause ';' rule_clauses : ['$1'|'$3']. -rule_clause -> atom1 clause_args clause_guard rule_body : +rule_clause -> atom clause_args clause_guard rule_body : {clause,?line('$1'),element(3, '$1'),'$2','$3','$4'}. rule_body -> ':-' lc_exprs: '$2'. @@ -510,12 +511,21 @@ Erlang code. %% of the generated .erl file by the HiPE compiler. Please do not remove. -compile([{hipe,[{regalloc,linear_scan}]}]). +-export_type([abstract_clause/0, abstract_expr/0, abstract_form/0, + error_info/0]). + +-type abstract_clause() :: term(). +-type abstract_expr() :: term(). +-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()}. %% mkop(Op, Arg) -> {op,Line,Op,Arg}. %% mkop(Left, Op, Right) -> {op,Line,Op,Left,Right}. --define(mkop2(L, OpPos, R), - begin +-define(mkop2(L, OpPos, R), + begin {Op,Pos} = OpPos, {op,Pos,Op,L,R} end). @@ -533,9 +543,19 @@ Erlang code. %% These really suck and are only here until Calle gets multiple %% entry points working. +-spec parse_form(Tokens) -> {ok, AbsForm} | {error, ErrorInfo} when + Tokens :: [token()], + AbsForm :: abstract_form(), + ErrorInfo :: error_info(). +parse_form([{'-',L1},{atom,L2,spec}|Tokens]) -> + parse([{'-',L1},{'spec',L2}|Tokens]); parse_form(Tokens) -> parse(Tokens). +-spec parse_exprs(Tokens) -> {ok, ExprList} | {error, ErrorInfo} when + Tokens :: [token()], + ExprList :: [abstract_expr()], + ErrorInfo :: error_info(). parse_exprs(Tokens) -> case parse([{atom,0,f},{'(',0},{')',0},{'->',0}|Tokens]) of {ok,{function,_Lf,f,0,[{clause,_Lc,[],[],Exprs}]}} -> @@ -543,6 +563,10 @@ parse_exprs(Tokens) -> {error,_} = Err -> Err end. +-spec parse_term(Tokens) -> {ok, Term} | {error, ErrorInfo} when + Tokens :: [token()], + Term :: term(), + ErrorInfo :: error_info(). parse_term(Tokens) -> case parse([{atom,0,f},{'(',0},{')',0},{'->',0}|Tokens]) of {ok,{function,_Lf,f,0,[{clause,_Lc,[],[],[Expr]}]}} -> @@ -559,7 +583,7 @@ parse_term(Tokens) -> -type attributes() :: 'export' | 'file' | 'import' | 'module' | 'opaque' | 'record' | 'type'. -build_typed_attribute({atom,La,record}, +build_typed_attribute({atom,La,record}, {typed_record, {atom,_Ln,RecordName}, RecTuple}) -> {attribute,La,record,{RecordName,record_tuple(RecTuple)}}; build_typed_attribute({atom,La,Attr}, @@ -582,7 +606,7 @@ build_typed_attribute({atom,La,Attr},_) -> build_type_spec({spec,La}, {SpecFun, TypeSpecs}) -> NewSpecFun = case SpecFun of - {atom, _, Fun} -> + {atom, _, Fun} -> {Fun, find_arity_from_specs(TypeSpecs)}; {{atom,_, Mod}, {atom,_, Fun}} -> {Mod,Fun,find_arity_from_specs(TypeSpecs)}; @@ -605,11 +629,20 @@ find_arity_from_specs([Spec|_]) -> {type, _, 'fun', [{type, _, product, Args},_]} = Fun, length(Args). +build_def(LHS, Types) -> + IsSubType = {atom, ?line(LHS), is_subtype}, + {type, ?line(LHS), constraint, [IsSubType, [LHS, Types]]}. + lift_unions(T1, {type, _La, union, List}) -> {type, ?line(T1), union, [T1|List]}; lift_unions(T1, T2) -> {type, ?line(T1), union, [T1, T2]}. +skip_paren({paren_type,_L,[Type]}) -> + skip_paren(Type); +skip_paren(Type) -> + Type. + build_gen_type({atom, La, tuple}) -> {type, La, tuple, any}; build_gen_type({atom, La, Name}) -> @@ -618,7 +651,7 @@ build_gen_type({atom, La, Name}) -> build_bin_type([{var, _, '_'}|Left], Int) -> build_bin_type(Left, Int); build_bin_type([], Int) -> - Int; + skip_paren(Int); build_bin_type([{var, La, _}|_], _) -> ret_err(La, "Bad binary type"). @@ -716,7 +749,7 @@ attribute_farity(Other) -> Other. attribute_farity_list(Args) -> [attribute_farity(A) || A <- Args]. - + -spec error_bad_decl(integer(), attributes()) -> no_return(). error_bad_decl(L, S) -> @@ -739,17 +772,39 @@ record_fields([{match,_Lm,{atom,La,A},Expr}|Fields]) -> [{record_field,La,{atom,La,A},Expr}|record_fields(Fields)]; record_fields([{typed,Expr,TypeInfo}|Fields]) -> [Field] = record_fields([Expr]), - TypeInfo1 = + TypeInfo1 = case Expr of {match, _, _, _} -> TypeInfo; %% If we have an initializer. - {atom, La, _} -> - lift_unions(abstract(undefined, La), TypeInfo) - end, + {atom, La, _} -> + case has_undefined(TypeInfo) of + false -> + TypeInfo2 = maybe_add_paren(TypeInfo), + lift_unions(abstract(undefined, La), TypeInfo2); + true -> + TypeInfo + end + end, [{typed_record_field,Field,TypeInfo1}|record_fields(Fields)]; record_fields([Other|_Fields]) -> ret_err(?line(Other), "bad record field"); record_fields([]) -> []. +has_undefined({atom,_,undefined}) -> + true; +has_undefined({ann_type,_,[_,T]}) -> + has_undefined(T); +has_undefined({paren_type,_,[T]}) -> + has_undefined(T); +has_undefined({type,_,union,Ts}) -> + lists:any(fun has_undefined/1, Ts); +has_undefined(_) -> + false. + +maybe_add_paren({ann_type,L,T}) -> + {paren_type,L,[{ann_type,L,T}]}; +maybe_add_paren(T) -> + T. + term(Expr) -> try normalise(Expr) catch _:_R -> ret_err(?line(Expr), "bad attribute") @@ -796,6 +851,7 @@ check_clauses(Cs, Name, Arity) -> build_try(L,Es,Scs,{Ccs,As}) -> {'try',L,Es,Scs,Ccs,As}. +-spec ret_err(_, _) -> no_return(). ret_err(L, S) -> {location,Location} = get_attribute(L, location), return_error(Location, S). @@ -812,10 +868,11 @@ mapl(F, [H|T]) -> mapl(_, []) -> []. -%% normalise(AbsTerm) -%% abstract(Term) %% Convert between the abstract form of a term and a term. +-spec normalise(AbsTerm) -> Data when + AbsTerm :: abstract_expr(), + Data :: term(). normalise({char,_,C}) -> C; normalise({integer,_,I}) -> I; normalise({float,_,F}) -> F; @@ -853,6 +910,9 @@ normalise_list([H|T]) -> 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}; @@ -921,13 +981,18 @@ abstract_list([H|T], Line) -> abstract_list([], _Line) -> []. -%% tokens(AbsTerm) -> [Token] -%% tokens(AbsTerm, More) -> [Token] %% Generate a list of tokens representing the abstract term. +-spec tokens(AbsTerm) -> Tokens when + AbsTerm :: abstract_expr(), + Tokens :: [token()]. tokens(Abs) -> tokens(Abs, []). +-spec tokens(AbsTerm, MoreTokens) -> Tokens when + AbsTerm :: abstract_expr(), + MoreTokens :: [token()], + Tokens :: [token()]. tokens({char,L,C}, More) -> [{char,L,C}|More]; tokens({integer,L,N}, More) -> [{integer,L,N}|More]; tokens({float,L,F}, More) -> [{float,L,F}|More]; @@ -989,7 +1054,7 @@ inop_prec('#') -> {800,700,800}; inop_prec(':') -> {900,800,900}; inop_prec('.') -> {900,900,1000}. --type pre_op() :: 'catch' | '+' | '-' | 'bnot' | '#'. +-type pre_op() :: 'catch' | '+' | '-' | 'bnot' | 'not' | '#'. -spec preop_prec(pre_op()) -> {0 | 600 | 700, 100 | 700 | 800}. diff --git a/lib/stdlib/src/erl_posix_msg.erl b/lib/stdlib/src/erl_posix_msg.erl index fe981b23a7..909cc1d102 100644 --- a/lib/stdlib/src/erl_posix_msg.erl +++ b/lib/stdlib/src/erl_posix_msg.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-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 @@ -24,143 +24,146 @@ -spec message(atom()) -> string(). -message(e2big) -> "argument list too long"; -message(eacces) -> "permission denied"; -message(eaddrinuse) -> "address already in use"; -message(eaddrnotavail) -> "can't assign requested address"; -message(eadv) -> "advertise error"; -message(eafnosupport) -> "address family not supported by protocol family"; -message(eagain) -> "resource temporarily unavailable"; -message(ealign) -> "EALIGN"; -message(ealready) -> "operation already in progress"; -message(ebade) -> "bad exchange descriptor"; -message(ebadf) -> "bad file number"; -message(ebadfd) -> "file descriptor in bad state"; -message(ebadmsg) -> "not a data message"; -message(ebadr) -> "bad request descriptor"; -message(ebadrpc) -> "RPC structure is bad"; -message(ebadrqc) -> "bad request code"; -message(ebadslt) -> "invalid slot"; -message(ebfont) -> "bad font file format"; -message(ebusy) -> "file busy"; -message(echild) -> "no children"; -message(echrng) -> "channel number out of range"; -message(ecomm) -> "communication error on send"; -message(econnaborted) -> "software caused connection abort"; -message(econnrefused) -> "connection refused"; -message(econnreset) -> "connection reset by peer"; -message(edeadlk) -> "resource deadlock avoided"; -message(edeadlock) -> "resource deadlock avoided"; -message(edestaddrreq) -> "destination address required"; -message(edirty) -> "mounting a dirty fs w/o force"; -message(edom) -> "math argument out of range"; -message(edotdot) -> "cross mount point"; -message(edquot) -> "disk quota exceeded"; -message(eduppkg) -> "duplicate package name"; -message(eexist) -> "file already exists"; -message(efault) -> "bad address in system call argument"; -message(efbig) -> "file too large"; -message(ehostdown) -> "host is down"; -message(ehostunreach) -> "host is unreachable"; -message(eidrm) -> "identifier removed"; -message(einit) -> "initialization error"; -message(einprogress) -> "operation now in progress"; -message(eintr) -> "interrupted system call"; -message(einval) -> "invalid argument"; -message(eio) -> "I/O error"; -message(eisconn) -> "socket is already connected"; -message(eisdir) -> "illegal operation on a directory"; -message(eisnam) -> "is a name file"; -message(elbin) -> "ELBIN"; -message(el2hlt) -> "level 2 halted"; -message(el2nsync) -> "level 2 not synchronized"; -message(el3hlt) -> "level 3 halted"; -message(el3rst) -> "level 3 reset"; -message(elibacc) -> "can not access a needed shared library"; -message(elibbad) -> "accessing a corrupted shared library"; -message(elibexec) -> "can not exec a shared library directly"; -message(elibmax) -> - "attempting to link in more shared libraries than system limit"; -message(elibscn) -> ".lib section in a.out corrupted"; -message(elnrng) -> "link number out of range"; -message(eloop) -> "too many levels of symbolic links"; -message(emfile) -> "too many open files"; -message(emlink) -> "too many links"; -message(emsgsize) -> "message too long"; -message(emultihop) -> "multihop attempted"; -message(enametoolong) -> "file name too long"; -message(enavail) -> "not available"; -message(enet) -> "ENET"; -message(enetdown) -> "network is down"; -message(enetreset) -> "network dropped connection on reset"; -message(enetunreach) -> "network is unreachable"; -message(enfile) -> "file table overflow"; -message(enoano) -> "anode table overflow"; -message(enobufs) -> "no buffer space available"; -message(enocsi) -> "no CSI structure available"; -message(enodata) -> "no data available"; -message(enodev) -> "no such device"; -message(enoent) -> "no such file or directory"; -message(enoexec) -> "exec format error"; -message(enolck) -> "no locks available"; -message(enolink) -> "link has be severed"; -message(enomem) -> "not enough memory"; -message(enomsg) -> "no message of desired type"; -message(enonet) -> "machine is not on the network"; -message(enopkg) -> "package not installed"; -message(enoprotoopt) -> "bad proocol option"; -message(enospc) -> "no space left on device"; -message(enosr) -> "out of stream resources or not a stream device"; -message(enosym) -> "unresolved symbol name"; -message(enosys) -> "function not implemented"; -message(enotblk) -> "block device required"; -message(enotconn) -> "socket is not connected"; -message(enotdir) -> "not a directory"; -message(enotempty) -> "directory not empty"; -message(enotnam) -> "not a name file"; -message(enotsock) -> "socket operation on non-socket"; -message(enotsup) -> "operation not supported"; -message(enotty) -> "inappropriate device for ioctl"; -message(enotuniq) -> "name not unique on network"; -message(enxio) -> "no such device or address"; -message(eopnotsupp) -> "operation not supported on socket"; -message(eperm) -> "not owner"; -message(epfnosupport) -> "protocol family not supported"; -message(epipe) -> "broken pipe"; -message(eproclim) -> "too many processes"; -message(eprocunavail) -> "bad procedure for program"; -message(eprogmismatch) -> "program version wrong"; -message(eprogunavail) -> "RPC program not available"; -message(eproto) -> "protocol error"; -message(eprotonosupport) -> "protocol not suppored"; -message(eprototype) -> "protocol wrong type for socket"; -message(erange) -> "math result unrepresentable"; -message(erefused) -> "EREFUSED"; -message(eremchg) -> "remote address changed"; -message(eremdev) -> "remote device"; -message(eremote) -> "pathname hit remote file system"; -message(eremoteio) -> "remote i/o error"; -message(eremoterelease) -> "EREMOTERELEASE"; -message(erofs) -> "read-only file system"; -message(erpcmismatch) -> "RPC version is wrong"; -message(erremote) -> "object is remote"; -message(eshutdown) -> "can't send after socket shutdown"; -message(esocktnosupport) -> "socket type not supported"; -message(espipe) -> "invalid seek"; -message(esrch) -> "no such process"; -message(esrmnt) -> "srmount error"; -message(estale) -> "stale remote file handle"; -message(esuccess) -> "Error 0"; -message(etime) -> "timer expired"; -message(etimedout) -> "connection timed out"; -message(etoomanyrefs) -> "too many references: can't splice"; -message(etxtbsy) -> "text file or pseudo-device busy"; -message(euclean) -> "structure needs cleaning"; -message(eunatch) -> "protocol driver not attached"; -message(eusers) -> "too many users"; -message(eversion) -> "version mismatch"; -message(ewouldblock) -> "operation would block"; -message(exdev) -> "cross-domain link"; -message(exfull) -> "message tables full"; -message(nxdomain) -> "non-existing domain"; -message(_) -> "unknown POSIX error". +message(T) -> + binary_to_list(message_1(T)). + +message_1(e2big) -> <<"argument list too long">>; +message_1(eacces) -> <<"permission denied">>; +message_1(eaddrinuse) -> <<"address already in use">>; +message_1(eaddrnotavail) -> <<"can't assign requested address">>; +message_1(eadv) -> <<"advertise error">>; +message_1(eafnosupport) -> <<"address family not supported by protocol family">>; +message_1(eagain) -> <<"resource temporarily unavailable">>; +message_1(ealign) -> <<"EALIGN">>; +message_1(ealready) -> <<"operation already in progress">>; +message_1(ebade) -> <<"bad exchange descriptor">>; +message_1(ebadf) -> <<"bad file number">>; +message_1(ebadfd) -> <<"file descriptor in bad state">>; +message_1(ebadmsg) -> <<"not a data message">>; +message_1(ebadr) -> <<"bad request descriptor">>; +message_1(ebadrpc) -> <<"RPC structure is bad">>; +message_1(ebadrqc) -> <<"bad request code">>; +message_1(ebadslt) -> <<"invalid slot">>; +message_1(ebfont) -> <<"bad font file format">>; +message_1(ebusy) -> <<"file busy">>; +message_1(echild) -> <<"no children">>; +message_1(echrng) -> <<"channel number out of range">>; +message_1(ecomm) -> <<"communication error on send">>; +message_1(econnaborted) -> <<"software caused connection abort">>; +message_1(econnrefused) -> <<"connection refused">>; +message_1(econnreset) -> <<"connection reset by peer">>; +message_1(edeadlk) -> <<"resource deadlock avoided">>; +message_1(edeadlock) -> <<"resource deadlock avoided">>; +message_1(edestaddrreq) -> <<"destination address required">>; +message_1(edirty) -> <<"mounting a dirty fs w/o force">>; +message_1(edom) -> <<"math argument out of range">>; +message_1(edotdot) -> <<"cross mount point">>; +message_1(edquot) -> <<"disk quota exceeded">>; +message_1(eduppkg) -> <<"duplicate package name">>; +message_1(eexist) -> <<"file already exists">>; +message_1(efault) -> <<"bad address in system call argument">>; +message_1(efbig) -> <<"file too large">>; +message_1(ehostdown) -> <<"host is down">>; +message_1(ehostunreach) -> <<"host is unreachable">>; +message_1(eidrm) -> <<"identifier removed">>; +message_1(einit) -> <<"initialization error">>; +message_1(einprogress) -> <<"operation now in progress">>; +message_1(eintr) -> <<"interrupted system call">>; +message_1(einval) -> <<"invalid argument">>; +message_1(eio) -> <<"I/O error">>; +message_1(eisconn) -> <<"socket is already connected">>; +message_1(eisdir) -> <<"illegal operation on a directory">>; +message_1(eisnam) -> <<"is a name file">>; +message_1(elbin) -> <<"ELBIN">>; +message_1(el2hlt) -> <<"level 2 halted">>; +message_1(el2nsync) -> <<"level 2 not synchronized">>; +message_1(el3hlt) -> <<"level 3 halted">>; +message_1(el3rst) -> <<"level 3 reset">>; +message_1(elibacc) -> <<"can not access a needed shared library">>; +message_1(elibbad) -> <<"accessing a corrupted shared library">>; +message_1(elibexec) -> <<"can not exec a shared library directly">>; +message_1(elibmax) -> + <<"attempting to link in more shared libraries than system limit">>; +message_1(elibscn) -> <<".lib section in a.out corrupted">>; +message_1(elnrng) -> <<"link number out of range">>; +message_1(eloop) -> <<"too many levels of symbolic links">>; +message_1(emfile) -> <<"too many open files">>; +message_1(emlink) -> <<"too many links">>; +message_1(emsgsize) -> <<"message too long">>; +message_1(emultihop) -> <<"multihop attempted">>; +message_1(enametoolong) -> <<"file name too long">>; +message_1(enavail) -> <<"not available">>; +message_1(enet) -> <<"ENET">>; +message_1(enetdown) -> <<"network is down">>; +message_1(enetreset) -> <<"network dropped connection on reset">>; +message_1(enetunreach) -> <<"network is unreachable">>; +message_1(enfile) -> <<"file table overflow">>; +message_1(enoano) -> <<"anode table overflow">>; +message_1(enobufs) -> <<"no buffer space available">>; +message_1(enocsi) -> <<"no CSI structure available">>; +message_1(enodata) -> <<"no data available">>; +message_1(enodev) -> <<"no such device">>; +message_1(enoent) -> <<"no such file or directory">>; +message_1(enoexec) -> <<"exec format error">>; +message_1(enolck) -> <<"no locks available">>; +message_1(enolink) -> <<"link has be severed">>; +message_1(enomem) -> <<"not enough memory">>; +message_1(enomsg) -> <<"no message of desired type">>; +message_1(enonet) -> <<"machine is not on the network">>; +message_1(enopkg) -> <<"package not installed">>; +message_1(enoprotoopt) -> <<"bad proocol option">>; +message_1(enospc) -> <<"no space left on device">>; +message_1(enosr) -> <<"out of stream resources or not a stream device">>; +message_1(enosym) -> <<"unresolved symbol name">>; +message_1(enosys) -> <<"function not implemented">>; +message_1(enotblk) -> <<"block device required">>; +message_1(enotconn) -> <<"socket is not connected">>; +message_1(enotdir) -> <<"not a directory">>; +message_1(enotempty) -> <<"directory not empty">>; +message_1(enotnam) -> <<"not a name file">>; +message_1(enotsock) -> <<"socket operation on non-socket">>; +message_1(enotsup) -> <<"operation not supported">>; +message_1(enotty) -> <<"inappropriate device for ioctl">>; +message_1(enotuniq) -> <<"name not unique on network">>; +message_1(enxio) -> <<"no such device or address">>; +message_1(eopnotsupp) -> <<"operation not supported on socket">>; +message_1(eperm) -> <<"not owner">>; +message_1(epfnosupport) -> <<"protocol family not supported">>; +message_1(epipe) -> <<"broken pipe">>; +message_1(eproclim) -> <<"too many processes">>; +message_1(eprocunavail) -> <<"bad procedure for program">>; +message_1(eprogmismatch) -> <<"program version wrong">>; +message_1(eprogunavail) -> <<"RPC program not available">>; +message_1(eproto) -> <<"protocol error">>; +message_1(eprotonosupport) -> <<"protocol not suppored">>; +message_1(eprototype) -> <<"protocol wrong type for socket">>; +message_1(erange) -> <<"math result unrepresentable">>; +message_1(erefused) -> <<"EREFUSED">>; +message_1(eremchg) -> <<"remote address changed">>; +message_1(eremdev) -> <<"remote device">>; +message_1(eremote) -> <<"pathname hit remote file system">>; +message_1(eremoteio) -> <<"remote i/o error">>; +message_1(eremoterelease) -> <<"EREMOTERELEASE">>; +message_1(erofs) -> <<"read-only file system">>; +message_1(erpcmismatch) -> <<"RPC version is wrong">>; +message_1(erremote) -> <<"object is remote">>; +message_1(eshutdown) -> <<"can't send after socket shutdown">>; +message_1(esocktnosupport) -> <<"socket type not supported">>; +message_1(espipe) -> <<"invalid seek">>; +message_1(esrch) -> <<"no such process">>; +message_1(esrmnt) -> <<"srmount error">>; +message_1(estale) -> <<"stale remote file handle">>; +message_1(esuccess) -> <<"Error 0">>; +message_1(etime) -> <<"timer expired">>; +message_1(etimedout) -> <<"connection timed out">>; +message_1(etoomanyrefs) -> <<"too many references: can't splice">>; +message_1(etxtbsy) -> <<"text file or pseudo-device busy">>; +message_1(euclean) -> <<"structure needs cleaning">>; +message_1(eunatch) -> <<"protocol driver not attached">>; +message_1(eusers) -> <<"too many users">>; +message_1(eversion) -> <<"version mismatch">>; +message_1(ewouldblock) -> <<"operation would block">>; +message_1(exdev) -> <<"cross-domain link">>; +message_1(exfull) -> <<"message tables full">>; +message_1(nxdomain) -> <<"non-existing domain">>; +message_1(_) -> <<"unknown POSIX error">>. diff --git a/lib/stdlib/src/erl_pp.erl b/lib/stdlib/src/erl_pp.erl index b1b5bad294..7dc19f2e9b 100644 --- a/lib/stdlib/src/erl_pp.erl +++ b/lib/stdlib/src/erl_pp.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1996-2011. 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_pp). @@ -31,25 +31,53 @@ -define(MAXLINE, 72). +-type(hook_function() :: none + | fun((Expr :: erl_parse:abstract_expr(), + CurrentIndentation :: integer(), + CurrentPrecedence :: non_neg_integer(), + HookFunction :: hook_function()) -> + io_lib:chars())). + %%% %%% Exported functions %%% +-spec(form(Form) -> io_lib:chars() when + Form :: erl_parse:abstract_form()). + form(Thing) -> form(Thing, none). +-spec(form(Form, HookFunction) -> io_lib:chars() when + Form :: erl_parse:abstract_form(), + HookFunction :: hook_function()). + form(Thing, Hook) -> frmt(lform(Thing, Hook)). +-spec(attribute(Attribute) -> io_lib:chars() when + Attribute :: erl_parse:abstract_form()). + attribute(Thing) -> attribute(Thing, none). +-spec(attribute(Attribute, HookFunction) -> io_lib:chars() when + Attribute :: erl_parse:abstract_form(), + HookFunction :: hook_function()). + attribute(Thing, Hook) -> frmt(lattribute(Thing, Hook)). +-spec(function(Function) -> io_lib:chars() when + Function :: erl_parse:abstract_form()). + function(F) -> function(F, none). +-spec(function(Function, HookFunction) -> io_lib:chars() when + Function :: erl_parse:abstract_form(), + HookFunction :: hook_function()). + function(F, Hook) -> frmt(lfunction(F, Hook)). @@ -59,30 +87,67 @@ rule(R) -> rule(R, Hook) -> frmt(lrule(R, Hook)). +-spec(guard(Guard) -> io_lib:chars() when + Guard :: [erl_parse:abstract_expr()]). + guard(Gs) -> guard(Gs, none). +-spec(guard(Guard, HookFunction) -> io_lib:chars() when + Guard :: [erl_parse:abstract_expr()], + HookFunction :: hook_function()). + guard(Gs, Hook) -> frmt(lguard(Gs, Hook)). +-spec(exprs(Expressions) -> io_lib:chars() when + Expressions :: [erl_parse:abstract_expr()]). + exprs(Es) -> exprs(Es, 0, none). +-spec(exprs(Expressions, HookFunction) -> io_lib:chars() when + Expressions :: [erl_parse:abstract_expr()], + HookFunction :: hook_function()). + exprs(Es, Hook) -> exprs(Es, 0, Hook). +-spec(exprs(Expressions, Indent, HookFunction) -> io_lib:chars() when + Expressions :: [erl_parse:abstract_expr()], + Indent :: integer(), + HookFunction :: hook_function()). + exprs(Es, I, Hook) -> frmt({seq,[],[],[$,],lexprs(Es, Hook)}, I). +-spec(expr(Expression) -> io_lib:chars() when + Expression :: erl_parse:abstract_expr()). + expr(E) -> frmt(lexpr(E, 0, none)). +-spec(expr(Expression, HookFunction) -> io_lib:chars() when + Expression :: erl_parse:abstract_expr(), + HookFunction :: hook_function()). + expr(E, Hook) -> frmt(lexpr(E, 0, Hook)). +-spec(expr(Expression, Indent, HookFunction) -> io_lib:chars() when + Expression :: erl_parse:abstract_expr(), + Indent :: integer(), + HookFunction :: hook_function()). + expr(E, I, Hook) -> frmt(lexpr(E, 0, Hook), I). +-spec(expr(Expression, Indent, Precedence, HookFunction) -> io_lib:chars() when + Expression :: erl_parse:abstract_expr(), + Indent :: integer(), + Precedence :: non_neg_integer(), + HookFunction :: hook_function()). + expr(E, I, P, Hook) -> frmt(lexpr(E, P, Hook), I). @@ -115,7 +180,7 @@ lattribute({attribute,_Line,Name,Arg}, Hook) -> lattribute(module, {M,Vs}, _Hook) -> attr("module",[{var,0,pname(M)}, - foldr(fun(V, C) -> {cons,0,{var,0,V},C} + foldr(fun(V, C) -> {cons,0,{var,0,V},C} end, {nil,0}, Vs)]); lattribute(module, M, _Hook) -> attr("module", [{var,0,pname(M)}]); @@ -140,7 +205,7 @@ typeattr(Tag, {TypeName,Type,Args}, _Hook) -> ltype({ann_type,_Line,[V,T]}) -> typed(lexpr(V, none), T); ltype({paren_type,_Line,[T]}) -> - [$(,ltype(T),$)]; + [$(,ltype(T),$)]; ltype({type,_Line,union,Ts}) -> {seq,[],[],[' |'],ltypes(Ts)}; ltype({type,_Line,list,[T]}) -> @@ -153,7 +218,7 @@ ltype({type,Line,tuple,any}) -> simple_type({atom,Line,tuple}, []); ltype({type,_Line,tuple,Ts}) -> tuple_type(Ts, fun ltype/1); -ltype({type,_Line,record,[N|Fs]}) -> +ltype({type,_Line,record,[{atom,_,N}|Fs]}) -> record_type(N, Fs); ltype({type,_Line,range,[_I1,_I2]=Es}) -> expr_list(Es, '..', fun lexpr/2, none); @@ -161,24 +226,28 @@ ltype({type,_Line,binary,[I1,I2]}) -> binary_type(I1, I2); % except binary() ltype({type,_Line,'fun',[]}) -> leaf("fun()"); -ltype({type,_Line,'fun',_}=FunType) -> +ltype({type,_,'fun',[{type,_,any},_]}=FunType) -> + [fun_type(['fun',$(], FunType),$)]; +ltype({type,_Line,'fun',[{type,_,product,_},_]}=FunType) -> [fun_type(['fun',$(], FunType),$)]; ltype({type,Line,T,Ts}) -> simple_type({atom,Line,T}, Ts); ltype({remote_type,Line,[M,F,Ts]}) -> simple_type({remote,Line,M,F}, Ts); ltype({atom,_,T}) -> - %% Follow the convention to always quote atoms (in types): - leaf([$',atom_to_list(T),$']); + leaf(write(T)); ltype(E) -> lexpr(E, 0, none). -binary_type({integer,_,Int1}=I1, {integer,_,Int2}=I2) -> - E1 = [[leaf("_:"),lexpr(I1, 0, none)] || Int1 =/= 0], - E2 = [[leaf("_:_*"),lexpr(I2, 0, none)] || Int2 =/= 0], +binary_type(I1, I2) -> + B = [[] || {integer,_,0} <- [I1]] =:= [], + U = [[] || {integer,_,0} <- [I2]] =:= [], + P = max_prec(), + E1 = [[leaf("_:"),lexpr(I1, P, none)] || B], + E2 = [[leaf("_:_*"),lexpr(I2, P, none)] || U], {seq,'<<','>>',[$,],E1++E2}. -record_type({atom,_,Name}, Fields) -> +record_type(Name, Fields) -> {first,[record_name(Name)],field_types(Fields)}. field_types(Fs) -> @@ -442,7 +511,7 @@ lexpr({op,_,Op,Arg}, Prec, Hook) -> Ol = leaf(format("~s ", [Op])), El = [Ol,lexpr(Arg, R, Hook)], maybe_paren(P, Prec, El); -lexpr({op,_,Op,Larg,Rarg}, Prec, Hook) when Op =:= 'orelse'; +lexpr({op,_,Op,Larg,Rarg}, Prec, Hook) when Op =:= 'orelse'; Op =:= 'andalso' -> %% Breaks lines since R12B. {L,P,R} = inop_prec(Op), @@ -554,17 +623,11 @@ record_field({typed_record_field,{record_field,_,F,Val},Type}, Hook) -> Fl = lexpr(F, L, Hook), Vl = typed(lexpr(Val, R, Hook), Type), {list,[{cstep,[Fl,' ='],Vl}]}; -record_field({typed_record_field,Field,Type0}, Hook) -> - Type = remove_undefined(Type0), +record_field({typed_record_field,Field,Type}, Hook) -> typed(record_field(Field, Hook), Type); record_field({record_field,_,F}, Hook) -> lexpr(F, 0, Hook). -remove_undefined({type,L,union,[{atom,_,undefined}|T]}) -> - {type,L,union,T}; -remove_undefined(T) -> % cannot happen - T. - list({cons,_,H,T}, Es, Hook) -> list(T, [H|Es], Hook); list({nil,_}, Es, Hook) -> @@ -726,15 +789,15 @@ frmt(Item, I) -> %%% and indentation are inserted between IPs. %%% - {first,I,IP2}: IP2 follows after I, and is output with an indentation %%% updated with the width of I. -%%% - {seq,Before,After,Separator,IPs}: a sequence of Is separated by -%%% Separator. Before is output before IPs, and the indentation of IPs +%%% - {seq,Before,After,Separator,IPs}: a sequence of Is separated by +%%% Separator. Before is output before IPs, and the indentation of IPs %%% is updated with the width of Before. After follows after IPs. %%% - {force_nl,ExtraInfo,I}: fun-info (a comment) forces linebreak before I. %%% - {prefer_nl,Sep,IPs}: forces linebreak between Is unlesss negative %%% indentation. %%% - {string,S}: a string. %%% - {hook,...}, {ehook,...}: hook expressions. -%%% +%%% %%% list, first, seq, force_nl, and prefer_nl all accept IPs, where each %%% element is either an item or a tuple {step|cstep,I1,I2}. step means %%% that I2 is output after linebreak and an incremented indentation. @@ -760,7 +823,7 @@ f({seq,Before,After,Sep,LItems}, I0, ST, WT) -> {CharsL,SizeL} = unz(CharsSizeL), {BCharsL,BSizeL} = unz1([BCharsSize]), Sizes = BSizeL ++ SizeL, - NSepChars = if + NSepChars = if is_list(Sep), Sep =/= [] -> erlang:max(0, length(CharsL)-1); true -> @@ -875,7 +938,7 @@ nl_indent(I, T) when I > 0 -> [$\n|spaces(I, T)]. same_line(I0, SizeL, NSepChars) -> - try + try Size = lists:sum(SizeL) + NSepChars, true = incr(I0, Size) =< ?MAXLINE, {yes,Size} @@ -955,9 +1018,9 @@ write_a_string(S, N, Len) -> -define(N_SPACES, 30). spacetab() -> - {[_|L],_} = mapfoldl(fun(_, A) -> {A,[$\s|A]} + {[_|L],_} = mapfoldl(fun(_, A) -> {A,[$\s|A]} end, [], lists:seq(0, ?N_SPACES)), - list_to_tuple(L). + list_to_tuple(L). spaces(N, T) when N =< ?N_SPACES -> element(N, T); @@ -965,7 +1028,7 @@ spaces(N, T) -> [element(?N_SPACES, T)|spaces(N-?N_SPACES, T)]. wordtable() -> - L = [begin {leaf,Sz,S} = leaf(W), {S,Sz} end || + L = [begin {leaf,Sz,S} = leaf(W), {S,Sz} end || W <- [" ->"," =","<<",">>","[]","after","begin","case","catch", "end","fun","if","of","receive","try","when"," ::","..", " |"]], diff --git a/lib/stdlib/src/erl_scan.erl b/lib/stdlib/src/erl_scan.erl index 52ec81a78b..718ca2e91a 100644 --- a/lib/stdlib/src/erl_scan.erl +++ b/lib/stdlib/src/erl_scan.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1996-2011. 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% %% @@ -48,25 +48,20 @@ -module(erl_scan). -%%% External exports +%%% External exports -export([string/1,string/2,string/3,tokens/3,tokens/4, format_error/1,reserved_word/1, token_info/1,token_info/2, attributes_info/1,attributes_info/2,set_attribute/3]). -%%% Local record. --record(erl_scan, - {resword_fun=fun reserved_word/1, - ws=false, - comment=false, - text=false}). +-export_type([error_info/0, line/0, tokens_result/0]). %%% -%%% Exported functions +%%% Defines and type definitions %%% --define(COLUMN(C), is_integer(C), C >= 1). +-define(COLUMN(C), (is_integer(C) andalso C >= 1)). %% Line numbers less than zero have always been allowed: -define(ALINE(L), is_integer(L)). -define(STRING(S), is_list(S)). @@ -95,86 +90,126 @@ -type error_description() :: term(). -type error_info() :: {location(), module(), error_description()}. --spec format_error(Error :: term()) -> string(). +%%% Local record. +-record(erl_scan, + {resword_fun = fun reserved_word/1 :: resword_fun(), + ws = false :: boolean(), + comment = false :: boolean(), + text = false :: boolean()}). + +%%---------------------------------------------------------------------------- + +-spec format_error(ErrorDescriptor) -> string() when + ErrorDescriptor :: error_description(). format_error({string,Quote,Head}) -> lists:flatten(["unterminated " ++ string_thing(Quote) ++ - " starting with " ++ + " starting with " ++ io_lib:write_unicode_string(Head, Quote)]); -format_error({illegal,Type}) -> +format_error({illegal,Type}) -> lists:flatten(io_lib:fwrite("illegal ~w", [Type])); format_error(char) -> "unterminated character"; -format_error({base,Base}) -> +format_error({base,Base}) -> lists:flatten(io_lib:fwrite("illegal base '~w'", [Base])); -format_error(Other) -> +format_error(Other) -> lists:flatten(io_lib:write(Other)). --type string_return() :: {'ok', tokens(), location()} - | {'error', error_info(), location()}. - --spec string(String :: string()) -> string_return(). +-spec string(String) -> Return when + String :: string(), + Return :: {'ok', Tokens :: tokens(), EndLocation} + | {'error', ErrorInfo :: error_info(), ErrorLocation}, + EndLocation :: location(), + ErrorLocation :: location(). string(String) -> string(String, 1, []). --spec string(String :: string(), StartLocation :: location()) -> - string_return(). +-spec string(String, StartLocation) -> Return when + String :: string(), + Return :: {'ok', Tokens :: tokens(), EndLocation} + | {'error', ErrorInfo :: error_info(), ErrorLocation}, + StartLocation :: location(), + EndLocation :: location(), + ErrorLocation :: location(). string(String, StartLocation) -> string(String, StartLocation, []). --spec string(String :: string(), StartLocation :: location(), - Options :: options()) -> string_return(). +-spec string(String, StartLocation, Options) -> Return when + String :: string(), + Options :: options(), + Return :: {'ok', Tokens :: tokens(), EndLocation} + | {'error', ErrorInfo :: error_info(), ErrorLocation}, + StartLocation :: location(), + EndLocation :: location(), + ErrorLocation :: location(). string(String, Line, Options) when ?STRING(String), ?ALINE(Line) -> string1(String, options(Options), Line, no_col, []); string(String, {Line,Column}, Options) when ?STRING(String), - ?ALINE(Line), + ?ALINE(Line), ?COLUMN(Column) -> string1(String, options(Options), Line, Column, []). -type char_spec() :: string() | 'eof'. -type cont_fun() :: fun((char_spec(), #erl_scan{}, line(), column(), tokens(), any()) -> any()). --opaque return_cont() :: {string(), column(), tokens(), line(), - #erl_scan{}, cont_fun(), any()}. --type cont() :: return_cont() | []. --type tokens_result() :: {'ok', tokens(), location()} - | {'eof', location()} - | {'error', error_info(), location()}. --type tokens_return() :: {'done', tokens_result(), char_spec()} - | {'more', return_cont()}. - --spec tokens(Cont :: cont(), CharSpec :: char_spec(), - StartLocation :: location()) -> tokens_return(). +-opaque return_cont() :: {erl_scan_continuation, + string(), column(), tokens(), line(), + #erl_scan{}, any(), cont_fun()}. +-type tokens_result() :: {'ok', Tokens :: tokens(), EndLocation :: location()} + | {'eof', EndLocation :: location()} + | {'error', ErrorInfo :: error_info(), + EndLocation :: location()}. + +-spec tokens(Continuation, CharSpec, StartLocation) -> Return when + Continuation :: return_cont() | [], + CharSpec :: char_spec(), + StartLocation :: location(), + Return :: {'done',Result :: tokens_result(),LeftOverChars :: char_spec()} + | {'more', Continuation1 :: return_cont()}. tokens(Cont, CharSpec, StartLocation) -> tokens(Cont, CharSpec, StartLocation, []). --spec tokens(Cont :: cont(), CharSpec :: char_spec(), - StartLocation :: location(), Options :: options()) -> - tokens_return(). +-spec tokens(Continuation, CharSpec, StartLocation, Options) -> Return when + Continuation :: return_cont() | [], + CharSpec :: char_spec(), + StartLocation :: location(), + Options :: options(), + Return :: {'done',Result :: tokens_result(),LeftOverChars :: char_spec()} + | {'more', Continuation1 :: return_cont()}. tokens([], CharSpec, Line, Options) when ?ALINE(Line) -> tokens1(CharSpec, options(Options), Line, no_col, [], fun scan/6, []); tokens([], CharSpec, {Line,Column}, Options) when ?ALINE(Line), ?COLUMN(Column) -> tokens1(CharSpec, options(Options), Line, Column, [], fun scan/6, []); -tokens({Cs,Col,Toks,Line,St,Any,Fun}, CharSpec, _Loc, _Opts) -> +tokens({erl_scan_continuation,Cs,Col,Toks,Line,St,Any,Fun}, + CharSpec, _Loc, _Opts) -> tokens1(Cs++CharSpec, St, Line, Col, Toks, Fun, Any). --type attribute_item() :: 'column' | 'length' | 'line' +-type attribute_item() :: 'column' | 'length' | 'line' | 'location' | 'text'. -type info_location() :: location() | term(). --type attribute_info() :: {'column', column()}| {'length', pos_integer()} - | {'line', info_line()} +-type attribute_info() :: {'column', column()}| {'length', pos_integer()} + | {'line', info_line()} | {'location', info_location()} | {'text', string()}. -type token_item() :: 'category' | 'symbol' | attribute_item(). --type token_info() :: {'category', category()} | {'symbol', symbol()} +-type token_info() :: {'category', category()} | {'symbol', symbol()} | attribute_info(). --spec token_info(token()) -> [token_info()]. +-spec token_info(Token) -> TokenInfo when + Token :: token(), + TokenInfo :: [TokenInfoTuple :: token_info()]. token_info(Token) -> Items = [category,column,length,line,symbol,text], % undefined order token_info(Token, Items). --spec token_info(token(), token_item()) -> token_info() | 'undefined'; - (token(), [token_item()]) -> [token_info()]. +-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()]. token_info(_Token, []) -> []; token_info(Token, [Item|Items]) when is_atom(Item) -> @@ -197,14 +232,23 @@ token_info({_Category,Attrs}, Item) -> token_info({_Category,Attrs,_Symbol}, Item) -> attributes_info(Attrs, Item). --spec attributes_info(attributes()) -> [attribute_info()]. +-spec attributes_info(Attributes) -> AttributesInfo when + Attributes :: attributes(), + AttributesInfo :: [AttributeInfoTuple :: attribute_info()]. attributes_info(Attributes) -> Items = [column,length,line,text], % undefined order attributes_info(Attributes, Items). --spec attributes_info(attributes(), attribute_item()) -> - attribute_info() | 'undefined'; - (attributes(), [attribute_item()]) -> [attribute_info()]. +-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()]. attributes_info(_Attrs, []) -> []; attributes_info(Attrs, [A|As]) when is_atom(A) -> @@ -214,7 +258,7 @@ attributes_info(Attrs, [A|As]) when is_atom(A) -> AttributeInfo when is_tuple(AttributeInfo) -> [AttributeInfo|attributes_info(Attrs, As)] end; -attributes_info({Line,Column}, column=Item) when ?ALINE(Line), +attributes_info({Line,Column}, column=Item) when ?ALINE(Line), ?COLUMN(Column) -> {Item,Column}; attributes_info(Line, column) when ?ALINE(Line) -> @@ -230,12 +274,12 @@ attributes_info(Attrs, length=Item) -> end; attributes_info(Line, line=Item) when ?ALINE(Line) -> {Item,Line}; -attributes_info({Line,Column}, line=Item) when ?ALINE(Line), +attributes_info({Line,Column}, line=Item) when ?ALINE(Line), ?COLUMN(Column) -> {Item,Line}; attributes_info(Attrs, line=Item) -> attr_info(Attrs, Item); -attributes_info({Line,Column}=Location, location=Item) when ?ALINE(Line), +attributes_info({Line,Column}=Location, location=Item) when ?ALINE(Line), ?COLUMN(Column) -> {Item,Location}; attributes_info(Line, location=Item) when ?ALINE(Line) -> @@ -261,9 +305,10 @@ attributes_info(Attrs, text=Item) -> attributes_info(T1, T2) -> erlang:error(badarg, [T1,T2]). --type setlineattr_fun() :: fun((info_line()) -> info_line()). - --spec set_attribute('line', attributes(), setlineattr_fun()) -> attributes(). +-spec set_attribute(AttributeItem, Attributes, SetAttributeFun) -> Attributes when + AttributeItem :: 'line', + Attributes :: attributes(), + SetAttributeFun :: fun((info_line()) -> info_line()). set_attribute(Tag, Attributes, Fun) when ?SETATTRFUN(Fun) -> set_attr(Tag, Attributes, Fun). @@ -289,11 +334,11 @@ string_thing(_) -> "string". options(Opts0) when is_list(Opts0) -> Opts = lists:foldr(fun expand_opt/2, [], Opts0), - [RW_fun] = + [RW_fun] = case opts(Opts, [reserved_word_fun], []) of badarg -> erlang:error(badarg, [Opts0]); - R -> + R -> R end, Comment = proplists:get_bool(return_comments, Opts), @@ -307,10 +352,10 @@ options(Opt) -> options([Opt]). opts(Options, [Key|Keys], L) -> - V = case lists:keysearch(Key, 1, Options) of - {value,{reserved_word_fun,F}} when ?RESWORDFUN(F) -> + V = case lists:keyfind(Key, 1, Options) of + {reserved_word_fun,F} when ?RESWORDFUN(F) -> {ok,F}; - {value,{Key,_}} -> + {Key,_} -> badarg; false -> {ok,default_option(Key)} @@ -333,12 +378,13 @@ expand_opt(O, Os) -> [O|Os]. attr_info(Attrs, Item) -> - case catch lists:keysearch(Item, 1, Attrs) of - {value,{Item,Value}} -> - {Item,Value}; - false -> - undefined; - _ -> + try lists:keyfind(Item, 1, Attrs) of + {_Item, _Value} = T -> + T; + false -> + undefined + catch + _:_ -> erlang:error(badarg, [Attrs, Item]) end. @@ -369,7 +415,7 @@ set_attr(T1, T2, T3) -> tokens1(Cs, St, Line, Col, Toks, Fun, Any) when ?STRING(Cs); Cs =:= eof -> case Fun(Cs, St, Line, Col, Toks, Any) of {more,{Cs0,Ncol,Ntoks,Nline,Nany,Nfun}} -> - {more,{Cs0,Ncol,Ntoks,Nline,St,Nany,Nfun}}; + {more,{erl_scan_continuation,Cs0,Ncol,Ntoks,Nline,St,Nany,Nfun}}; {ok,Toks0,eof,Nline,Ncol} -> Res = case Toks0 of [] -> @@ -442,6 +488,14 @@ scan1([$\%=C|Cs], St, Line, Col, Toks) -> scan_comment(Cs, St, Line, Col, Toks, [C]); scan1([C|Cs], St, Line, Col, Toks) when ?DIGIT(C) -> scan_number(Cs, St, Line, Col, Toks, [C]); +scan1("..."++Cs, St, Line, Col, Toks) -> + tok2(Cs, St, Line, Col, Toks, "...", '...', 3); +scan1(".."=Cs, _St, Line, Col, Toks) -> + {more,{Cs,Col,Toks,Line,[],fun scan/6}}; +scan1(".."++Cs, St, Line, Col, Toks) -> + tok2(Cs, St, Line, Col, Toks, "..", '..', 2); +scan1("."=Cs, _St, Line, Col, Toks) -> + {more,{Cs,Col,Toks,Line,[],fun scan/6}}; scan1([$.=C|Cs], St, Line, Col, Toks) -> scan_dot(Cs, St, Line, Col, Toks, [C]); scan1([$"|Cs], St, Line, Col, Toks) -> %" Emacs @@ -591,12 +645,12 @@ scan_atom(Cs0, St, Line, Col, Toks, Ncs0) -> case catch list_to_atom(Wcs) of Name when is_atom(Name) -> case (St#erl_scan.resword_fun)(Name) of - true -> + true -> tok2(Cs, St, Line, Col, Toks, Wcs, Name); - false -> + false -> tok3(Cs, St, Line, Col, Toks, atom, Wcs, Name) end; - _Error -> + _Error -> Ncol = incr_column(Col, length(Wcs)), scan_error({illegal,atom}, Line, Col, Line, Ncol, Cs) end @@ -610,7 +664,7 @@ scan_variable(Cs0, St, Line, Col, Toks, Ncs0) -> case catch list_to_atom(Wcs) of Name when is_atom(Name) -> tok3(Cs, St, Line, Col, Toks, var, Wcs, Name); - _Error -> + _Error -> Ncol = incr_column(Col, length(Wcs)), scan_error({illegal,var}, Line, Col, Line, Ncol, Cs) end @@ -644,8 +698,6 @@ scan_dot([$\n=C|Cs], St, Line, Col, Toks, Ncs) -> scan_dot([C|Cs], St, Line, Col, Toks, Ncs) when ?WHITE_SPACE(C) -> Attrs = attributes(Line, Col, St, Ncs++[C]), {ok,[{dot,Attrs}|Toks],Cs,Line,incr_column(Col, 2)}; -scan_dot([]=Cs, _St, Line, Col, Toks, Ncs) -> - {more,{Cs,Col,Toks,Line,Ncs,fun scan_dot/6}}; scan_dot(eof=Cs, St, Line, Col, Toks, Ncs) -> Attrs = attributes(Line, Col, St, Ncs), {ok,[{dot,Attrs}|Toks],Cs,Line,incr_column(Col, 1)}; @@ -690,7 +742,7 @@ scan_nl_spcs([]=Cs, _St, Line, Col, Toks, N) -> {more,{Cs,Col,Toks,Line,N,fun scan_nl_spcs/6}}; scan_nl_spcs(Cs, St, Line, Col, Toks, N) -> newline_end(Cs, St, Line, Col, Toks, N, nl_spcs(N)). - + scan_nl_tabs([$\t|Cs], St, Line, Col, Toks, N) when N < 11 -> scan_nl_tabs(Cs, St, Line, Col, Toks, N+1); scan_nl_tabs([]=Cs, _St, Line, Col, Toks, N) -> @@ -701,7 +753,7 @@ scan_nl_tabs(Cs, St, Line, Col, Toks, N) -> %% Note: returning {more,Cont} is meaningless here; one could just as %% well return several tokens. But since tokens() scans up to a full %% stop anyway, nothing is gained by not collecting all white spaces. -scan_nl_white_space([$\n|Cs], #erl_scan{text = false}=St, Line, no_col=Col, +scan_nl_white_space([$\n|Cs], #erl_scan{text = false}=St, Line, no_col=Col, Toks0, Ncs) -> Toks = [{white_space,Line,lists:reverse(Ncs)}|Toks0], scan_newline(Cs, St, Line+1, Col, Toks); @@ -714,7 +766,7 @@ scan_nl_white_space([C|Cs], St, Line, Col, Toks, Ncs) when ?WHITE_SPACE(C) -> scan_nl_white_space(Cs, St, Line, Col, Toks, [C|Ncs]); scan_nl_white_space([]=Cs, _St, Line, Col, Toks, Ncs) -> {more,{Cs,Col,Toks,Line,Ncs,fun scan_nl_white_space/6}}; -scan_nl_white_space(Cs, #erl_scan{text = false}=St, Line, no_col=Col, +scan_nl_white_space(Cs, #erl_scan{text = false}=St, Line, no_col=Col, Toks, Ncs) -> scan1(Cs, St, Line+1, Col, [{white_space,Line,lists:reverse(Ncs)}|Toks]); scan_nl_white_space(Cs, St, Line, Col, Toks, Ncs0) -> @@ -723,7 +775,7 @@ scan_nl_white_space(Cs, St, Line, Col, Toks, Ncs0) -> Token = {white_space,Attrs,Ncs}, scan1(Cs, St, Line+1, new_column(Col, length(Ncs)), [Token|Toks]). -newline_end(Cs, #erl_scan{text = false}=St, Line, no_col=Col, +newline_end(Cs, #erl_scan{text = false}=St, Line, no_col=Col, Toks, _N, Ncs) -> scan1(Cs, St, Line+1, Col, [{white_space,Line,Ncs}|Toks]); newline_end(Cs, St, Line, Col, Toks, N, Ncs) -> @@ -789,7 +841,7 @@ scan_char([$\\|Cs]=Cs0, St, Line, Col, Toks) -> Ntoks = [{char,Attrs,Val}|Toks], scan1(Ncs, St, Line, Ncol, Ntoks) end; -scan_char([$\n=C|Cs], St, Line, Col, Toks) -> +scan_char([$\n=C|Cs], St, Line, Col, Toks) -> Attrs = attributes(Line, Col, 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) -> @@ -896,7 +948,7 @@ 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 =/= $\\, +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) -> @@ -909,7 +961,7 @@ scan_string_col([Q|Cs], Line, Col, Q, Wcs0, Uni) -> {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 =/= $\\, +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) -> @@ -970,8 +1022,8 @@ scan_string1(eof, Line, Col, _Q, _Str, Wcs, _Uni) -> {error,Line,Col,lists:reverse(Wcs),eof}. -define(OCT(C), C >= $0, C =< $7). --define(HEX(C), C >= $0 andalso C =< $9 orelse - C >= $A andalso C =< $F orelse +-define(HEX(C), C >= $0 andalso C =< $9 orelse + C >= $A andalso C =< $F orelse C >= $a andalso C =< $f). %% \<1-3> octal digits @@ -1086,7 +1138,7 @@ scan_number(Cs, St, Line, Col, Toks, Ncs0) -> Ncol = incr_column(Col, length(Ncs)), scan_error({illegal,integer}, Line, Col, Line, Ncol, Cs) end. - + scan_based_int([C|Cs], St, Line, Col, Toks, {B,Ncs,Bcs}) when ?DIGIT(C), C < $0+B -> scan_based_int(Cs, St, Line, Col, Toks, {B,[C|Ncs],Bcs}); @@ -1262,7 +1314,7 @@ nl_tabs(8) -> "\n\t\t\t\t\t\t\t"; nl_tabs(9) -> "\n\t\t\t\t\t\t\t\t"; nl_tabs(10) -> "\n\t\t\t\t\t\t\t\t\t"; nl_tabs(11) -> "\n\t\t\t\t\t\t\t\t\t\t". - + tabs(1) -> "\t"; tabs(2) -> "\t\t"; tabs(3) -> "\t\t\t"; @@ -1274,7 +1326,7 @@ tabs(8) -> "\t\t\t\t\t\t\t\t"; tabs(9) -> "\t\t\t\t\t\t\t\t\t"; tabs(10) -> "\t\t\t\t\t\t\t\t\t\t". --spec reserved_word(atom()) -> boolean(). +-spec reserved_word(Atom :: atom()) -> boolean(). reserved_word('after') -> true; reserved_word('begin') -> true; reserved_word('case') -> true; @@ -1303,5 +1355,4 @@ reserved_word('bsl') -> true; reserved_word('bsr') -> true; reserved_word('or') -> true; reserved_word('xor') -> true; -reserved_word('spec') -> true; reserved_word(_) -> false. diff --git a/lib/stdlib/src/escript.erl b/lib/stdlib/src/escript.erl index 5958a58d7c..d67617260e 100644 --- a/lib/stdlib/src/escript.erl +++ b/lib/stdlib/src/escript.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2010. All Rights Reserved. +%% Copyright Ericsson AB 2007-2011. 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,102 +19,254 @@ -module(escript). %% Useful functions that can be called from scripts. --export([script_name/0, foldl/3]). +-export([script_name/0, create/2, extract/2]). %% Internal API. -export([start/0, start/1]). --record(state, {file, - module, +%%----------------------------------------------------------------------- + +-define(SHEBANG, "/usr/bin/env escript"). +-define(COMMENT, "This is an -*- erlang -*- file"). + +%%----------------------------------------------------------------------- + +-type mode() :: 'native' | 'compile' | 'debug' | 'interpret' | 'run'. +-type source() :: 'archive' | 'beam' | 'text'. + +-record(state, {file :: file:filename(), + module :: module(), forms_or_bin, - source, - n_errors, - mode, - exports_main, - has_records}). - + source :: source(), + n_errors :: non_neg_integer(), + mode :: mode(), + exports_main :: boolean(), + has_records :: boolean()}). + +-type shebang() :: string(). +-type comment() :: string(). +-type emu_args() :: string(). + +-record(sections, {type, + shebang :: shebang(), + comment :: comment(), + emu_args :: emu_args(), + body}). + +-record(extract_options, {compile_source}). + +-type zip_file() :: + file:filename() + | {file:filename(), binary()} + | {file:filename(), binary(), file:file_info()}. +-type zip_create_option() :: term(). +-type section() :: + shebang + | {shebang, shebang()} + | comment + | {comment, comment()} + | {emu_args, emu_args()} + | {source, file:filename() | binary()} + | {beam, file:filename() | binary()} + | {archive, file:filename() | binary()} + | {archive, [zip_file()], [zip_create_option()]}. + +%%----------------------------------------------------------------------- + +%% Create a complete escript file with both header and body +-spec create(file:filename() | binary, [section()]) -> + ok | {ok, binary()} | {error, term()}. + +create(File, Options) when is_list(Options) -> + try + S = prepare(Options, #sections{}), + BinList = + [Section || Section <- [S#sections.shebang, + S#sections.comment, + S#sections.emu_args, + S#sections.body], + Section =/= undefined], + case File of + binary -> + {ok, list_to_binary(BinList)}; + _ -> + case file:write_file(File, BinList) of + ok -> + ok; + {error, Reason} -> + {error, {Reason, File}} + end + end + catch + throw:PrepareReason -> + {error, PrepareReason} + end. + +prepare([H | T], S) -> + case H of + {shebang, undefined} -> + prepare(T, S); + shebang -> + prepare(T, S#sections{shebang = "#!" ++ ?SHEBANG ++ "\n"}); + {shebang, default} -> + prepare(T, S#sections{shebang = "#!" ++ ?SHEBANG ++ "\n"}); + {shebang, Shebang} when is_list(Shebang) -> + prepare(T, S#sections{shebang = "#!" ++ Shebang ++ "\n"}); + {comment, undefined} -> + prepare(T, S); + comment -> + prepare(T, S#sections{comment = "%% " ++ ?COMMENT ++ "\n"}); + {comment, default} -> + prepare(T, S#sections{comment = "%% " ++ ?COMMENT ++ "\n"}); + {comment, Comment} when is_list(Comment) -> + prepare(T, S#sections{comment = "%% " ++ Comment ++ "\n"}); + {emu_args, undefined} -> + prepare(T, S); + {emu_args, Args} when is_list(Args) -> + prepare(T, S#sections{emu_args = "%%!" ++ Args ++ "\n"}); + {Type, File} when is_list(File) -> + case file:read_file(File) of + {ok, Bin} -> + prepare(T, S#sections{type = Type, body = Bin}); + {error, Reason} -> + throw({Reason, H}) + end; + {Type, Bin} when is_binary(Bin) -> + prepare(T, S#sections{type = Type, body = Bin}); + {archive = Type, ZipFiles, ZipOptions} + when is_list(ZipFiles), is_list(ZipOptions) -> + File = "dummy.zip", + case zip:create(File, ZipFiles, ZipOptions ++ [memory]) of + {ok, {File, ZipBin}} -> + prepare(T, S#sections{type = Type, body = ZipBin}); + {error, Reason} -> + throw({Reason, H}) + end; + _ -> + throw({badarg, H}) + end; +prepare([], #sections{body = undefined}) -> + throw(missing_body); +prepare([], #sections{type = Type} = S) + when Type =:= source; Type =:= beam; Type =:= archive -> + S; +prepare([], #sections{type = Type}) -> + throw({illegal_type, Type}); +prepare(BadOptions, _) -> + throw({badarg, BadOptions}). + +-type section_name() :: shebang | comment | emu_args | body . +-type extract_option() :: compile_source | {section, [section_name()]}. +-spec extract(file:filename(), [extract_option()]) -> + {ok, [section()]} | {error, term()}. + +extract(File, Options) when is_list(File), is_list(Options) -> + try + EO = parse_extract_options(Options, + #extract_options{compile_source = false}), + {HeaderSz, NextLineNo, Fd, Sections} = + parse_header(File, not EO#extract_options.compile_source), + Type = Sections#sections.type, + case {Type, EO#extract_options.compile_source} of + {source, true} -> + Bin = compile_source(Type, File, Fd, NextLineNo, HeaderSz); + {_, _} -> + ok = file:close(Fd), + case file:read_file(File) of + {ok, <<_Header:HeaderSz/binary, Bin/binary>>} -> + ok; + {error, ReadReason} -> + Bin = get_rid_of_compiler_warning, + throw(ReadReason) + end + end, + return_sections(Sections, Bin) + catch + throw:Reason -> + {error, Reason} + end. + +parse_extract_options([H | T], EO) -> + case H of + compile_source -> + EO2 = EO#extract_options{compile_source = true}, + parse_extract_options(T, EO2); + _ -> + throw({badarg, H}) + end; +parse_extract_options([], EO) -> + EO. + +compile_source(Type, File, Fd, NextLineNo, HeaderSz) -> + {text, _Module, Forms, _HasRecs, _Mode} = + do_parse_file(Type, File, Fd, NextLineNo, HeaderSz, false), + ok = file:close(Fd), + case compile:forms(Forms, [return_errors, debug_info]) of + {ok, _, BeamBin} -> + BeamBin; + {error, Errors, Warnings} -> + throw({compile, [{errors, format_errors(Errors)}, + {warnings, format_errors(Warnings)}]}) + end. + +format_errors(CompileErrors) -> + [lists:flatten([File, ":", integer_to_list(LineNo), ": ", + Mod:format_error(Error)]) || + {File, FileErrors} <- CompileErrors, + {LineNo, Mod, Error} <- FileErrors]. + +return_sections(S, Bin) -> + {ok, [normalize_section(shebang, S#sections.shebang), + normalize_section(comment, S#sections.comment), + normalize_section(emu_args, S#sections.emu_args), + normalize_section(S#sections.type, Bin)]}. + +normalize_section(Name, undefined) -> + {Name, undefined}; +normalize_section(shebang, "#!" ++ Chars) -> + Chopped = string:strip(Chars, right, $\n), + Stripped = string:strip(Chopped, both), + if + Stripped =:= ?SHEBANG -> + {shebang, default}; + true -> + {shebang, Stripped} + end; +normalize_section(comment, Chars) -> + Chopped = string:strip(Chars, right, $\n), + Stripped = string:strip(string:strip(Chopped, left, $%), both), + if + Stripped =:= ?COMMENT -> + {comment, default}; + true -> + {comment, Stripped} + end; +normalize_section(emu_args, "%%!" ++ Chars) -> + Chopped = string:strip(Chars, right, $\n), + Stripped = string:strip(Chopped, both), + {emu_args, Stripped}; +normalize_section(Name, Chars) -> + {Name, Chars}. + +-spec script_name() -> string(). + script_name() -> [ScriptName|_] = init:get_plain_arguments(), ScriptName. -%% Apply Fun(Name, GetInfo, GetBin, Acc) for each file in the escript. -%% -%% Fun/2 must return a new accumulator which is passed to the next call. -%% The function returns the final value of the accumulator. Acc0 is -%% returned if the escript contain an empty archive. -%% -%% GetInfo/0 is a fun that returns a #file_info{} record for the file. -%% GetBin/0 is a fun that returns a the contents of the file as a binary. -%% -%% An escript may contain erlang code, beam code or an archive: -%% -%% archive - the Fun/2 will be applied for each file in the archive -%% beam - the Fun/2 will be applied once and GetInfo/0 returns the file -%% info for the (entire) escript file -%% erl - the Fun/2 will be applied once, GetInfo/0 returns the file -%% info for the (entire) escript file and the GetBin returns -%% the compiled beam code - -%%-spec foldl(fun((string(), -%% fun(() -> #file_info()), -%% fun(() -> binary() -> term()), -%% term()) -> term()), -%% term(), -%% string()). -foldl(Fun, Acc0, File) when is_function(Fun, 4) -> - case parse_file(File, false) of - {text, _, Forms, _HasRecs, _Mode} when is_list(Forms) -> - GetInfo = fun() -> file:read_file_info(File) end, - GetBin = - fun() -> - case compile:forms(Forms, [return_errors, debug_info]) of - {ok, _, BeamBin} -> - BeamBin; - {error, _Errors, _Warnings} -> - fatal("There were compilation errors.") - end - end, - try - {ok, Fun(".", GetInfo, GetBin, Acc0)} - catch - throw:Reason -> - {error, Reason} - end; - {beam, _, BeamBin, _HasRecs, _Mode} when is_binary(BeamBin) -> - GetInfo = fun() -> file:read_file_info(File) end, - GetBin = fun() -> BeamBin end, - try - {ok, Fun(".", GetInfo, GetBin, Acc0)} - catch - throw:Reason -> - {error, Reason} - end; - {archive, _, ArchiveBin, _HasRecs, _Mode} when is_binary(ArchiveBin) -> - ZipFun = - fun({Name, GetInfo, GetBin}, A) -> - A2 = Fun(Name, GetInfo, GetBin, A), - {true, false, A2} - end, - case prim_zip:open(ZipFun, Acc0, {File, ArchiveBin}) of - {ok, PrimZip, Res} -> - ok = prim_zip:close(PrimZip), - {ok, Res}; - {error, bad_eocd} -> - {error, "Not an archive file"}; - {error, Reason} -> - {error, Reason} - end - end. - %% %% Internal API. %% +-spec start() -> no_return(). + start() -> start([]). +-spec start([string()]) -> no_return(). + start(EscriptOptions) -> - try + try %% Commands run using -run or -s are run in a process %% trap_exit set to false. Because this behaviour is %% surprising for users of escript, make sure to reset @@ -143,16 +295,20 @@ parse_and_run(File, Args, Options) -> parse_file(File, CheckOnly), Mode2 = case lists:member("d", Options) of - true -> + true -> debug; false -> case lists:member("c", Options) of - true -> + true -> compile; false -> case lists:member("i", Options) of true -> interpret; - false -> Mode + false -> + case lists:member("n", Options) of + true -> native; + false -> Mode + end end end end, @@ -169,6 +325,14 @@ parse_and_run(File, Args, Options) -> _Other -> fatal("There were compilation errors.") end; + native -> + case compile:forms(FormsOrBin, [report,native]) of + {ok, Module, BeamBin} -> + {module, Module} = code:load_binary(Module, File, BeamBin), + run(Module, Args); + _Other -> + fatal("There were compilation errors.") + end; debug -> case compile:forms(FormsOrBin, [report, debug_info]) of {ok,Module,BeamBin} -> @@ -177,7 +341,7 @@ parse_and_run(File, Args, Options) -> _Other -> fatal("There were compilation errors.") end - end; + end; is_binary(FormsOrBin) -> case Source of archive -> @@ -190,11 +354,13 @@ parse_and_run(File, Args, Options) -> true -> my_halt(0); false -> - Text = lists:concat(["Function ", Module, ":main/1 is not exported"]), + Text = lists:concat(["Function ", Module, + ":main/1 is not exported"]), fatal(Text) end; _ -> - Text = lists:concat(["Cannot load module ", Module, " from archive"]), + Text = lists:concat(["Cannot load module ", Module, + " from archive"]), fatal(Text) end; ok -> @@ -212,7 +378,7 @@ parse_and_run(File, Args, Options) -> run -> {module, Module} = code:load_binary(Module, File, FormsOrBin), run(Module, Args); - debug -> + debug -> [Base | Rest] = lists:reverse(filename:split(File)), Base2 = filename:basename(Base, code:objfile_extension()), Rest2 = @@ -222,8 +388,8 @@ parse_and_run(File, Args, Options) -> end, SrcFile = filename:join(lists:reverse([Base2 ++ ".erl" | Rest2])), debug(Module, {Module, SrcFile, File, FormsOrBin}, Args) - end - end + end + end end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -231,25 +397,19 @@ parse_and_run(File, Args, Options) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% parse_file(File, CheckOnly) -> - S = #state{file = File, - n_errors = 0, - mode = interpret, - exports_main = false, - has_records = false}, - {ok, Fd} = - case file:open(File, [read]) of - {ok, Fd0} -> - {ok, Fd0}; - {error, R} -> - fatal(lists:concat([file:format_error(R), ": '", File, "'"])) - end, - {HeaderSz, StartLine, ScriptType} = skip_header(Fd, 1), + {HeaderSz, NextLineNo, Fd, Sections} = + parse_header(File, false), + do_parse_file(Sections#sections.type, + File, Fd, NextLineNo, HeaderSz, CheckOnly). + +do_parse_file(Type, File, Fd, NextLineNo, HeaderSz, CheckOnly) -> + S = initial_state(File), #state{mode = Mode, source = Source, module = Module, forms_or_bin = FormsOrBin, has_records = HasRecs} = - case ScriptType of + case Type of archive -> %% Archive file ok = file:close(Fd), @@ -260,63 +420,101 @@ parse_file(File, CheckOnly) -> parse_beam(S, File, HeaderSz, CheckOnly); source -> %% Source code - parse_source(S, File, Fd, StartLine, HeaderSz, CheckOnly) + parse_source(S, File, Fd, NextLineNo, HeaderSz, CheckOnly) end, {Source, Module, FormsOrBin, HasRecs, Mode}. +initial_state(File) -> + #state{file = File, + n_errors = 0, + mode = interpret, + exports_main = false, + has_records = false}. + %% Skip header and make a heuristic guess about the script type -skip_header(P, LineNo) -> +parse_header(File, KeepFirst) -> + LineNo = 1, + {ok, Fd} = + case file:open(File, [read]) of + {ok, Fd0} -> + {ok, Fd0}; + {error, R} -> + fatal(lists:concat([file:format_error(R), ": '", File, "'"])) + end, + %% Skip shebang on first line - {ok, HeaderSz0} = file:position(P, cur), - Line1 = get_line(P), + {ok, HeaderSz0} = file:position(Fd, cur), + Line1 = get_line(Fd), case classify_line(Line1) of shebang -> - find_first_body_line(P, LineNo); + find_first_body_line(Fd, HeaderSz0, LineNo, KeepFirst, + #sections{shebang = Line1}); archive -> - {HeaderSz0, LineNo, archive}; + {HeaderSz0, LineNo, Fd, + #sections{type = archive}}; beam -> - {HeaderSz0, LineNo, beam}; + {HeaderSz0, LineNo, Fd, + #sections{type = beam}}; _ -> - find_first_body_line(P, LineNo) + find_first_body_line(Fd, HeaderSz0, LineNo, KeepFirst, + #sections{}) end. -find_first_body_line(P, LineNo) -> - {ok, HeaderSz1} = file:position(P, cur), +find_first_body_line(Fd, HeaderSz0, LineNo, KeepFirst, Sections) -> + {ok, HeaderSz1} = file:position(Fd, cur), %% Look for special comment on second line - Line2 = get_line(P), - {ok, HeaderSz2} = file:position(P, cur), + Line2 = get_line(Fd), + {ok, HeaderSz2} = file:position(Fd, cur), case classify_line(Line2) of emu_args -> %% Skip special comment on second line - Line3 = get_line(P), - {HeaderSz2, LineNo + 2, guess_type(Line3)}; - _ -> + Line3 = get_line(Fd), + {HeaderSz2, LineNo + 2, Fd, + Sections#sections{type = guess_type(Line3), + comment = undefined, + emu_args = Line2}}; + Line2Type -> %% Look for special comment on third line - Line3 = get_line(P), - {ok, HeaderSz3} = file:position(P, cur), - case classify_line(Line3) of - emu_args -> + Line3 = get_line(Fd), + {ok, HeaderSz3} = file:position(Fd, cur), + Line3Type = classify_line(Line3), + if + Line3Type =:= emu_args -> %% Skip special comment on third line - Line4 = get_line(P), - {HeaderSz3, LineNo + 3, guess_type(Line4)}; - _ -> + Line4 = get_line(Fd), + {HeaderSz3, LineNo + 3, Fd, + Sections#sections{type = guess_type(Line4), + comment = Line2, + emu_args = Line3}}; + Sections#sections.shebang =:= undefined, + KeepFirst =:= true -> + %% No shebang. Use the entire file + {HeaderSz0, LineNo, Fd, + Sections#sections{type = guess_type(Line2)}}; + Sections#sections.shebang =:= undefined -> + %% No shebang. Skip the first line + {HeaderSz1, LineNo, Fd, + Sections#sections{type = guess_type(Line2)}}; + Line2Type =:= comment -> + %% Skip shebang on first line and comment on second + {HeaderSz2, LineNo + 2, Fd, + Sections#sections{type = guess_type(Line3), + comment = Line2}}; + true -> %% Just skip shebang on first line - {HeaderSz1, LineNo + 1, guess_type(Line2)} + {HeaderSz1, LineNo + 1, Fd, + Sections#sections{type = guess_type(Line2)}} end end. - + classify_line(Line) -> case Line of - [$\#, $\! | _] -> - shebang; - [$P, $K | _] -> - archive; - [$F, $O, $R, $1 | _] -> - beam; - [$\%, $\%, $\! | _] -> - emu_args; - _ -> - undefined + "#!" ++ _ -> shebang; + "PK" ++ _ -> archive; + "FOR1" ++ _ -> beam; + "%%!" ++ _ -> emu_args; + "%" ++ _ -> comment; + _ -> undefined end. guess_type(Line) -> @@ -336,8 +534,8 @@ get_line(P) -> parse_archive(S, File, HeaderSz) -> case file:read_file(File) of - {ok, <<_FirstLine:HeaderSz/binary, Bin/binary>>} -> - Mod = + {ok, <<_Header:HeaderSz/binary, Bin/binary>>} -> + Mod = case init:get_argument(escript) of {ok, [["main", M]]} -> %% Use explicit module name @@ -345,14 +543,13 @@ parse_archive(S, File, HeaderSz) -> _ -> %% Use escript name without extension as module name RevBase = lists:reverse(filename:basename(File)), - RevBase2 = + RevBase2 = case lists:dropwhile(fun(X) -> X =/= $. end, RevBase) of [$. | Rest] -> Rest; [] -> RevBase end, list_to_atom(lists:reverse(RevBase2)) end, - S#state{source = archive, mode = run, module = Mod, @@ -365,7 +562,7 @@ parse_archive(S, File, HeaderSz) -> parse_beam(S, File, HeaderSz, CheckOnly) -> - {ok, <<_FirstLine:HeaderSz/binary, Bin/binary>>} = + {ok, <<_Header:HeaderSz/binary, Bin/binary>>} = file:read_file(File), case beam_lib:chunks(Bin, [exports]) of {ok, {Module, [{exports, Exports}]}} -> @@ -385,9 +582,7 @@ parse_beam(S, File, HeaderSz, CheckOnly) -> forms_or_bin = Bin} end; {error, beam_lib, Reason} when is_tuple(Reason) -> - fatal(element(1, Reason)); - {error, beam_lib, Reason} -> - fatal(Reason) + fatal(element(1, Reason)) end. parse_source(S, File, Fd, StartLine, HeaderSz, CheckOnly) -> @@ -399,7 +594,7 @@ parse_source(S, File, Fd, StartLine, HeaderSz, CheckOnly) -> {ok, FileForm} = epp:parse_erl_form(Epp), OptModRes = epp:parse_erl_form(Epp), S2 = S#state{source = text, module = Module}, - S3 = + S3 = case OptModRes of {ok, {attribute,_, module, M} = Form} -> epp_parse_file(Epp, S2#state{module = M}, [Form, FileForm]); @@ -408,8 +603,8 @@ parse_source(S, File, Fd, StartLine, HeaderSz, CheckOnly) -> epp_parse_file2(Epp, S2, [ModForm, FileForm], OptModRes); {error, _} -> epp_parse_file2(Epp, S2, [FileForm], OptModRes); - {eof,LastLine} -> - S#state{forms_or_bin = [FileForm, {eof,LastLine}]} + {eof, _LastLine} = Eof -> + S#state{forms_or_bin = [FileForm, Eof]} end, ok = epp:close(Epp), ok = file:close(Fd), @@ -448,12 +643,12 @@ check_source(S, CheckOnly) -> pre_def_macros(File) -> {MegaSecs, Secs, MicroSecs} = erlang:now(), - Replace = fun(Char) -> + Replace = fun(Char) -> case Char of $\. -> $\_; _ -> Char end - end, + end, CleanBase = lists:map(Replace, filename:basename(File)), ModuleStr = CleanBase ++ "__" ++ @@ -481,7 +676,7 @@ epp_parse_file2(Epp, S, Forms, Parsed) -> {attribute,Ln,mode,NewMode} -> S2 = S#state{mode = NewMode}, if - NewMode =:= compile; NewMode =:= interpret; NewMode =:= debug -> + NewMode =:= compile; NewMode =:= interpret; NewMode =:= debug; NewMode =:= native -> epp_parse_file(Epp, S2, [Form | Forms]); true -> Args = lists:flatten(io_lib:format("illegal mode attribute: ~p", [NewMode])), @@ -504,8 +699,8 @@ epp_parse_file2(Epp, S, Forms, Parsed) -> io:format("~s:~w: ~s\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} -> - S#state{forms_or_bin = lists:reverse([{eof, LastLine} | Forms])} + {eof, _LastLine} = Eof -> + S#state{forms_or_bin = lists:reverse([Eof | Forms])} end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -642,8 +837,8 @@ eval_exprs([E|Es], Bs0, Lf, Ef, RBs) -> eval_exprs(Es, Bs, Lf, Ef, RBs). format_exception(Class, Reason) -> - PF = fun(Term, I) -> - io_lib:format("~." ++ integer_to_list(I) ++ "P", [Term, 50]) + PF = fun(Term, I) -> + 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, @@ -651,7 +846,7 @@ format_exception(Class, Reason) -> fatal(Str) -> throw(Str). - + my_halt(Reason) -> case process_info(group_leader(), status) of {_,waiting} -> @@ -675,7 +870,7 @@ hidden_apply(App, M, F, Args) -> Arity = length(Args), Text = io_lib:format("Call to ~w:~w/~w in application ~w failed.\n", [M, F, Arity, App]), - fatal(Text); + fatal(Text); Stk -> erlang:raise(error, undef, Stk) end diff --git a/lib/stdlib/src/ets.erl b/lib/stdlib/src/ets.erl index 9f84e3639f..afa914a456 100644 --- a/lib/stdlib/src/ets.erl +++ b/lib/stdlib/src/ets.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1996-2011. 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(ets). @@ -42,28 +42,20 @@ -export([i/0, i/1, i/2, i/3]). -%%------------------------------------------------------------------------------ +-export_type([tab/0, tid/0, match_spec/0]). --type tab() :: atom() | tid(). +%%----------------------------------------------------------------------------- --type ext_info() :: 'md5sum' | 'object_count'. --type protection() :: 'private' | 'protected' | 'public'. --type type() :: 'bag' | 'duplicate_bag' | 'ordered_set' | 'set'. +-type tab() :: atom() | tid(). --type table_info() :: {'name', atom()} - | {'type', type()} - | {'protection', protection()} - | {'named_table', boolean()} - | {'keypos', non_neg_integer()} - | {'size', non_neg_integer()} - | {'extended_info', [ext_info()]} - | {'version', {non_neg_integer(), non_neg_integer()}}. +%% a similar definition is also in erl_types +-opaque tid() :: integer(). %% these ones are also defined in erl_bif_types -type match_pattern() :: atom() | tuple(). --type match_specs() :: [{match_pattern(), [_], [_]}]. +-type match_spec() :: [{match_pattern(), [_], [_]}]. -%%------------------------------------------------------------------------------ +%%----------------------------------------------------------------------------- %% The following functions used to be found in this module, but %% are now BIFs (i.e. implemented in C). @@ -81,6 +73,7 @@ %% insert/2 %% is_compiled_ms/1 %% last/1 +%% member/2 %% next/2 %% prev/2 %% rename/2 @@ -96,11 +89,14 @@ %% 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 %% -opaque comp_match_spec() :: any(). %% this one is REALLY opaque @@ -114,7 +110,9 @@ match_spec_run(List, CompiledMS) -> | {tab(),integer(),integer(),binary(),list(),integer()} | {tab(),_,_,integer(),binary(),list(),integer(),integer()}. --spec repair_continuation(continuation(), match_specs()) -> continuation(). +-spec repair_continuation(Continuation, MatchSpec) -> Continuation when + Continuation :: continuation(), + MatchSpec :: match_spec(). %% $end_of_table is an allowed continuation in ets... repair_continuation('$end_of_table', _) -> @@ -148,7 +146,9 @@ repair_continuation(Untouched = {Table,N1,N2,Bin,L,N3}, MS) {Table,N1,N2,ets:match_spec_compile(MS),L,N3} end. --spec fun2ms(function()) -> match_specs(). +-spec fun2ms(LiteralFun) -> MatchSpec when + LiteralFun :: function(), + MatchSpec :: match_spec(). fun2ms(ShellFun) when is_function(ShellFun) -> %% Check that this is really a shell fun... @@ -172,7 +172,13 @@ fun2ms(ShellFun) when is_function(ShellFun) -> shell]}}) end. --spec foldl(fun((_, term()) -> term()), term(), tab()) -> term(). +-spec foldl(Function, Acc0, Tab) -> Acc1 when + Function :: fun((Element :: term(), AccIn) -> AccOut), + Tab :: tab(), + Acc0 :: term(), + Acc1 :: term(), + AccIn :: term(), + AccOut :: term(). foldl(F, Accu, T) -> ets:safe_fixtable(T, true), @@ -193,7 +199,13 @@ do_foldl(F, Accu0, Key, T) -> ets:next(T, Key), T) end. --spec foldr(fun((_, term()) -> term()), term(), tab()) -> term(). +-spec foldr(Function, Acc0, Tab) -> Acc1 when + Function :: fun((Element :: term(), AccIn) -> AccOut), + Tab :: tab(), + Acc0 :: term(), + Acc1 :: term(), + AccIn :: term(), + AccOut :: term(). foldr(F, Accu, T) -> ets:safe_fixtable(T, true), @@ -214,7 +226,9 @@ do_foldr(F, Accu0, Key, T) -> ets:prev(T, Key), T) end. --spec from_dets(tab(), dets:tab_name()) -> 'true'. +-spec from_dets(Tab, DetsTab) -> 'true' when + Tab :: tab(), + DetsTab :: dets:tab_name(). from_dets(EtsTable, DetsTable) -> case (catch dets:to_ets(DetsTable, EtsTable)) of @@ -230,7 +244,9 @@ from_dets(EtsTable, DetsTable) -> erlang:error(Unexpected,[EtsTable,DetsTable]) end. --spec to_dets(tab(), dets:tab_name()) -> tab(). +-spec to_dets(Tab, DetsTab) -> DetsTab when + Tab :: tab(), + DetsTab :: dets:tab_name(). to_dets(EtsTable, DetsTable) -> case (catch dets:from_ets(DetsTable, EtsTable)) of @@ -246,8 +262,11 @@ to_dets(EtsTable, DetsTable) -> erlang:error(Unexpected,[EtsTable,DetsTable]) end. --spec test_ms(tuple(), match_specs()) -> - {'ok', term()} | {'error', [{'warning'|'error', string()}]}. +-spec test_ms(Tuple, MatchSpec) -> {'ok', Result} | {'error', Errors} when + Tuple :: tuple(), + MatchSpec :: match_spec(), + Result :: term(), + Errors :: [{'warning'|'error', string()}]. test_ms(Term, MS) -> case erlang:match_spec_test(Term, MS, table) of @@ -257,7 +276,11 @@ test_ms(Term, MS) -> Error end. --spec init_table(tab(), fun(('read' | 'close') -> term())) -> 'true'. +-spec init_table(Tab, InitFun) -> 'true' when + Tab :: tab(), + InitFun :: fun((Arg) -> Res), + Arg :: 'read' | 'close', + Res :: 'end_of_input' | {Objects :: [term()], InitFun} | term(). init_table(Table, Fun) -> ets:delete_all_objects(Table), @@ -282,7 +305,9 @@ init_table_sub(Table, [H|T]) -> ets:insert(Table, H), init_table_sub(Table, T). --spec match_delete(tab(), match_pattern()) -> 'true'. +-spec match_delete(Tab, Pattern) -> 'true' when + Tab :: tab(), + Pattern :: match_pattern(). match_delete(Table, Pattern) -> ets:select_delete(Table, [{Pattern,[],[true]}]), @@ -290,7 +315,9 @@ match_delete(Table, Pattern) -> %% Produce a list of tuples from a table --spec tab2list(tab()) -> [tuple()]. +-spec tab2list(Tab) -> [Object] when + Tab :: tab(), + Object :: tuple(). tab2list(T) -> ets:match_object(T, '_'). @@ -329,15 +356,21 @@ do_filter(Tab, Key, F, A, Ack) -> md5sum = false :: boolean() }). --type fname() :: string() | atom(). --type t2f_option() :: {'extended_info', [ext_info()]}. - --spec tab2file(tab(), fname()) -> 'ok' | {'error', term()}. +-spec tab2file(Tab, Filename) -> 'ok' | {'error', Reason} when + Tab :: tab(), + Filename :: file:name(), + Reason :: term(). tab2file(Tab, File) -> tab2file(Tab, File, []). --spec tab2file(tab(), fname(), [t2f_option()]) -> 'ok' | {'error', term()}. +-spec tab2file(Tab, Filename, Options) -> 'ok' | {'error', Reason} when + Tab :: tab(), + Filename :: file:name(), + Options :: [Option], + Option :: {'extended_info', [ExtInfo]}, + ExtInfo :: 'md5sum' | 'object_count', + Reason :: term(). tab2file(Tab, File, Options) -> try @@ -496,18 +529,24 @@ parse_ft_info_options(_,Malformed) -> %% Opt := {verify,boolean()} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --type f2t_option() :: {'verify', boolean()}. - --spec file2tab(fname()) -> {'ok', tab()} | {'error', term()}. +-spec file2tab(Filename) -> {'ok', Tab} | {'error', Reason} when + Filename :: file:name(), + Tab :: tab(), + Reason :: term(). file2tab(File) -> file2tab(File, []). --spec file2tab(fname(), [f2t_option()]) -> {'ok', tab()} | {'error', term()}. +-spec file2tab(Filename, Options) -> {'ok', Tab} | {'error', Reason} when + Filename :: file:name(), + Tab :: tab(), + Options :: [Option], + Option :: {'verify', boolean()}, + Reason :: term(). file2tab(File, Opts) -> try - {ok,Verify} = parse_f2t_opts(Opts,false), + {ok,Verify,TabArg} = parse_f2t_opts(Opts,false,[]), Name = make_ref(), {ok, Major, Minor, FtOptions, MD5State, FullHeader, DLContext} = case disk_log:open([{name, Name}, @@ -535,7 +574,7 @@ file2tab(File, Opts) -> true -> ok end, - {ok, Tab, HeadCount} = create_tab(FullHeader), + {ok, Tab, HeadCount} = create_tab(FullHeader, TabArg), StrippedOptions = case Verify of true -> @@ -622,14 +661,14 @@ do_read_and_verify(ReadFun,InitState,Tab,FtOptions,HeadCount,Verify) -> end, {ok,Tab}; {ok,{FinalMD5State,FinalCount,['$end_of_table',LastInfo],_}} -> - ECount = case lists:keysearch(count,1,LastInfo) of - {value,{count,N}} -> + ECount = case lists:keyfind(count,1,LastInfo) of + {count,N} -> N; _ -> false end, - EMD5 = case lists:keysearch(md5,1,LastInfo) of - {value,{md5,M}} -> + EMD5 = case lists:keyfind(md5,1,LastInfo) of + {md5,M} -> M; _ -> false @@ -671,15 +710,17 @@ do_read_and_verify(ReadFun,InitState,Tab,FtOptions,HeadCount,Verify) -> {ok,Tab} end. -parse_f2t_opts([],Verify) -> - {ok,Verify}; -parse_f2t_opts([{verify, true}|T],_OV) -> - parse_f2t_opts(T,true); -parse_f2t_opts([{verify,false}|T],OV) -> - parse_f2t_opts(T,OV); -parse_f2t_opts([Unexpected|_],_) -> +parse_f2t_opts([],Verify,Tab) -> + {ok,Verify,Tab}; +parse_f2t_opts([{verify, true}|T],_OV,Tab) -> + parse_f2t_opts(T,true,Tab); +parse_f2t_opts([{verify,false}|T],OV,Tab) -> + parse_f2t_opts(T,OV,Tab); +parse_f2t_opts([{table,Tab}|T],OV,[]) -> + parse_f2t_opts(T,OV,Tab); +parse_f2t_opts([Unexpected|_],_,_) -> throw({unknown_option,Unexpected}); -parse_f2t_opts(Malformed,_) -> +parse_f2t_opts(Malformed,_,_) -> throw({malformed_option,Malformed}). count_mandatory([]) -> @@ -742,22 +783,21 @@ get_header_data(Name,true) -> false -> throw(badfile); true -> - Major = case lists:keysearch(major,1,L) of - {value,{major,Maj}} -> + Major = case lists:keyfind(major,1,L) of + {major,Maj} -> Maj; _ -> 0 end, - Minor = case lists:keysearch(minor,1,L) of - {value,{minor,Min}} -> + Minor = case lists:keyfind(minor,1,L) of + {minor,Min} -> Min; _ -> 0 end, FtOptions = - case lists:keysearch(extended_info,1,L) of - {value,{extended_info,I}} - when is_list(I) -> + case lists:keyfind(extended_info,1,L) of + {extended_info,I} when is_list(I) -> #filetab_options { object_count = @@ -786,29 +826,28 @@ get_header_data(Name,true) -> end; get_header_data(Name, false) -> - case wrap_chunk(Name,start,1,false) of + case wrap_chunk(Name, start, 1, false) of {C,[Tup]} when is_tuple(Tup) -> L = tuple_to_list(Tup), case verify_header_mandatory(L) of false -> throw(badfile); true -> - Major = case lists:keysearch(major_version,1,L) of - {value,{major_version,Maj}} -> + Major = case lists:keyfind(major_version, 1, L) of + {major_version, Maj} -> Maj; _ -> 0 end, - Minor = case lists:keysearch(minor_version,1,L) of - {value,{minor_version,Min}} -> + Minor = case lists:keyfind(minor_version, 1, L) of + {minor_version, Min} -> Min; _ -> 0 end, FtOptions = - case lists:keysearch(extended_info,1,L) of - {value,{extended_info,I}} - when is_list(I) -> + case lists:keyfind(extended_info, 1, L) of + {extended_info, I} when is_list(I) -> #filetab_options { object_count = @@ -825,25 +864,26 @@ get_header_data(Name, false) -> throw(badfile) end. -md5_and_convert([],MD5State,Count) -> +md5_and_convert([], MD5State, Count) -> {[],MD5State,Count,[]}; -md5_and_convert([H|T],MD5State,Count) when is_binary(H) -> +md5_and_convert([H|T], MD5State, Count) when is_binary(H) -> case (catch binary_to_term(H)) of {'EXIT', _} -> md5_and_convert(T,MD5State,Count); - ['$end_of_table',Dat] -> - {[],MD5State,Count,['$end_of_table',Dat]}; + ['$end_of_table',_Dat] = L -> + {[],MD5State,Count,L}; Term -> - X = erlang:md5_update(MD5State,H), - {Rest,NewMD5,NewCount,NewLast} = md5_and_convert(T,X,Count+1), + X = erlang:md5_update(MD5State, H), + {Rest,NewMD5,NewCount,NewLast} = md5_and_convert(T, X, Count+1), {[Term | Rest],NewMD5,NewCount,NewLast} end. -scan_for_endinfo([],Count) -> + +scan_for_endinfo([], Count) -> {[],Count,[]}; -scan_for_endinfo([['$end_of_table',Dat]],Count) -> +scan_for_endinfo([['$end_of_table',Dat]], Count) -> {['$end_of_table',Dat],Count,[]}; -scan_for_endinfo([Term|T],Count) -> - {NewLast,NCount,Rest} = scan_for_endinfo(T,Count+1), +scan_for_endinfo([Term|T], Count) -> + {NewLast,NCount,Rest} = scan_for_endinfo(T, Count+1), {NewLast,NCount,[Term | Rest]}. load_table(ReadFun, State, Tab) -> @@ -852,23 +892,32 @@ load_table(ReadFun, State, Tab) -> [] -> {ok,NewState}; List -> - ets:insert(Tab,List), - load_table(ReadFun,NewState,Tab) + ets:insert(Tab, List), + load_table(ReadFun, NewState, Tab) end. -create_tab(I) -> - {value, {name, Name}} = lists:keysearch(name, 1, I), - {value, {type, Type}} = lists:keysearch(type, 1, I), - {value, {protection, P}} = lists:keysearch(protection, 1, I), - {value, {named_table, Val}} = lists:keysearch(named_table, 1, I), - {value, {keypos, Kp}} = lists:keysearch(keypos, 1, I), - {value, {size, Sz}} = lists:keysearch(size, 1, I), - try - Tab = ets:new(Name, [Type, P, {keypos, Kp} | named_table(Val)]), - {ok, Tab, Sz} - catch - _:_ -> - throw(cannot_create_table) +create_tab(I, TabArg) -> + {name, Name} = lists:keyfind(name, 1, I), + {type, Type} = lists:keyfind(type, 1, I), + {protection, P} = lists:keyfind(protection, 1, I), + {named_table, Val} = lists:keyfind(named_table, 1, I), + {keypos, _Kp} = Keypos = lists:keyfind(keypos, 1, I), + {size, Sz} = lists:keyfind(size, 1, I), + Comp = case lists:keyfind(compressed, 1, I) of + {compressed, true} -> [compressed]; + {compressed, false} -> []; + false -> [] + end, + case TabArg of + [] -> + try + Tab = ets:new(Name, [Type, P, Keypos] ++ named_table(Val) ++ Comp), + {ok, Tab, Sz} + catch _:_ -> + throw(cannot_create_table) + end; + _ -> + {ok, TabArg, Sz} end. named_table(true) -> [named_table]; @@ -880,7 +929,22 @@ named_table(false) -> []. %% information %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec tabfile_info(fname()) -> {'ok', [table_info()]} | {'error', term()}. +-spec tabfile_info(Filename) -> {'ok', TableInfo} | {'error', Reason} when + Filename :: file:name(), + TableInfo :: [InfoItem], + InfoItem :: {'name', atom()} + | {'type', Type} + | {'protection', Protection} + | {'named_table', boolean()} + | {'keypos', non_neg_integer()} + | {'size', non_neg_integer()} + | {'extended_info', [ExtInfo]} + | {'version', {Major :: non_neg_integer(), + Minor :: non_neg_integer()}}, + ExtInfo :: 'md5sum' | 'object_count', + Type :: 'bag' | 'duplicate_bag' | 'ordered_set' | 'set', + Protection :: 'private' | 'protected' | 'public', + Reason :: term(). tabfile_info(File) when is_list(File) ; is_atom(File) -> try @@ -905,9 +969,9 @@ tabfile_info(File) when is_list(File) ; is_atom(File) -> {value, Val} = lists:keysearch(named_table, 1, FullHeader), {value, Kp} = lists:keysearch(keypos, 1, FullHeader), {value, Sz} = lists:keysearch(size, 1, FullHeader), - Ei = case lists:keysearch(extended_info, 1, FullHeader) of - {value, Ei0} -> Ei0; - _ -> {extended_info, []} + Ei = case lists:keyfind(extended_info, 1, FullHeader) of + false -> {extended_info, []}; + Ei0 -> Ei0 end, {ok, [N,Type,P,Val,Kp,Sz,Ei,{version,{Major,Minor}}]} catch @@ -917,20 +981,22 @@ tabfile_info(File) when is_list(File) ; is_atom(File) -> {error,ExReason} end. --type qlc__query_handle() :: term(). %% XXX: belongs in 'qlc' - --type num_objects() :: 'default' | pos_integer(). --type trav_method() :: 'first_next' | 'last_prev' - | 'select' | {'select', match_specs()}. --type table_option() :: {'n_objects', num_objects()} - | {'traverse', trav_method()}. - --spec table(tab()) -> qlc__query_handle(). +-spec table(Tab) -> QueryHandle when + Tab :: tab(), + QueryHandle :: qlc:query_handle(). table(Tab) -> table(Tab, []). --spec table(tab(), table_option() | [table_option()]) -> qlc__query_handle(). +-spec table(Tab, Options) -> QueryHandle when + Tab :: tab(), + QueryHandle :: qlc:query_handle(), + Options :: [Option] | Option, + Option :: {'n_objects', NObjects} + | {'traverse', TraverseMethod}, + NObjects :: 'default' | pos_integer(), + TraverseMethod :: 'first_next' | 'last_prev' + | 'select' | {'select', MatchSpec :: match_spec()}. table(Tab, Opts) -> case options(Opts, [traverse, n_objects]) of @@ -1021,21 +1087,20 @@ options(Option, Keys) -> options([Option], Keys, []). options(Options, [Key | Keys], L) when is_list(Options) -> - V = case lists:keysearch(Key, 1, Options) of - {value, {n_objects, default}} -> + V = case lists:keyfind(Key, 1, Options) of + {n_objects, default} -> {ok, default_option(Key)}; - {value, {n_objects, NObjs}} when is_integer(NObjs), - NObjs >= 1 -> + {n_objects, NObjs} when is_integer(NObjs), NObjs >= 1 -> {ok, NObjs}; - {value, {traverse, select}} -> + {traverse, select} -> {ok, select}; - {value, {traverse, {select, MS}}} -> - {ok, {select, MS}}; - {value, {traverse, first_next}} -> + {traverse, {select, _MS} = Select} -> + {ok, Select}; + {traverse, first_next} -> {ok, first_next}; - {value, {traverse, last_prev}} -> + {traverse, last_prev} -> {ok, last_prev}; - {value, {Key, _}} -> + {Key, _} -> badarg; false -> Default = default_option(Key), @@ -1121,7 +1186,8 @@ to_string(X) -> lists:flatten(io_lib:format("~p", [X])). %% view a specific table --spec i(tab()) -> 'ok'. +-spec i(Tab) -> 'ok' when + Tab :: tab(). i(Tab) -> i(Tab, 40). diff --git a/lib/stdlib/src/eval_bits.erl b/lib/stdlib/src/eval_bits.erl index 3671aecdcb..2cbd6cdae7 100644 --- a/lib/stdlib/src/eval_bits.erl +++ b/lib/stdlib/src/eval_bits.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% Copyright Ericsson AB 1999-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 @@ -19,6 +19,8 @@ %% -module(eval_bits). +%% Avoid warning for local function error/1 clashing with autoimported BIF. +-compile({no_auto_import,[error/1]}). -export([expr_grp/3,expr_grp/5,match_bits/6, match_bits/7,bin_gen/6]). diff --git a/lib/stdlib/src/file_sorter.erl b/lib/stdlib/src/file_sorter.erl index e21a0c88f3..3f31852afc 100644 --- a/lib/stdlib/src/file_sorter.erl +++ b/lib/stdlib/src/file_sorter.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2010. All Rights Reserved. +%% Copyright Ericsson AB 2001-2011. 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,6 +18,8 @@ %% -module(file_sorter). +%% Avoid warning for local function error/2 clashing with autoimported BIF. +-compile({no_auto_import,[error/2]}). -export([sort/1, sort/2, sort/3, keysort/2, keysort/3, keysort/4, merge/2, merge/3, @@ -48,12 +50,68 @@ %%% Exported functions %%% +-export_type([reason/0]). + +-type(file_name() :: file:name()). +-type(file_names() :: [file:name()]). +-type(i_command() :: read | close). +-type(i_reply() :: end_of_input | {end_of_input, value()} + | {[object()], infun()} | input_reply()). +-type(infun() :: fun((i_command()) -> i_reply())). +-type(input() :: file_names() | infun()). +-type(input_reply() :: term()). +-type(o_command() :: {value, value()} | [object()] | close). +-type(o_reply() :: outfun() | output_reply()). +-type(object() :: term() | binary()). +-type(outfun() :: fun((o_command()) -> o_reply())). +-type(output() :: file_name() | outfun()). +-type(output_reply() :: term()). +-type(value() :: term()). + +-type(options() :: [option()] | option()). +-type(option() :: {compressed, boolean()} + | {header, header_length()} + | {format, format()} + | {no_files, no_files()} + | {order, order()} + | {size, size()} + | {tmpdir, tmp_directory()} + | {unique, boolean()}). +-type(format() :: binary_term | term | binary | format_fun()). +-type(format_fun() :: fun((binary()) -> term())). +-type(header_length() :: pos_integer()). +-type(key_pos() :: pos_integer() | [pos_integer()]). +-type(no_files() :: pos_integer()). % > 1 +-type(order() :: ascending | descending | order_fun()). +-type(order_fun() :: fun((term(), term()) -> boolean())). +-type(size() :: non_neg_integer()). +-type(tmp_directory() :: [] | file:name()). + +-type(reason() :: bad_object + | {bad_object, file_name()} + | {bad_term, file_name()} + | {file_error, file_name(), + file:posix() | badarg | system_limit} + | {premature_eof, file_name()}). + +-spec(sort(FileName) -> Reply when + FileName :: file_name(), + Reply :: ok | {error, reason()} | input_reply() | output_reply()). sort(FileName) -> sort([FileName], FileName). +-spec(sort(Input, Output) -> Reply when + Input :: input(), + Output :: output(), + Reply :: ok | {error, reason()} | input_reply() | output_reply()). sort(Input, Output) -> sort(Input, Output, []). +-spec(sort(Input, Output, Options) -> Reply when + Input :: input(), + Output :: output(), + Options :: options(), + Reply :: ok | {error, reason()} | input_reply() | output_reply()). sort(Input0, Output0, Options) -> case {is_input(Input0), maybe_output(Output0), options(Options)} of {{true,Input}, {true,Output}, #opts{}=Opts} -> @@ -62,12 +120,27 @@ sort(Input0, Output0, Options) -> badarg(culprit(tuple_to_list(T)), [Input0, Output0, Options]) end. +-spec(keysort(KeyPos, FileName) -> Reply when + KeyPos :: key_pos(), + FileName :: file_name(), + Reply :: ok | {error, reason()} | input_reply() | output_reply()). keysort(KeyPos, FileName) -> keysort(KeyPos, [FileName], FileName). +-spec(keysort(KeyPos, Input, Output) -> Reply when + KeyPos :: key_pos(), + Input :: input(), + Output :: output(), + Reply :: ok | {error, reason()} | input_reply() | output_reply()). keysort(KeyPos, Input, Output) -> keysort(KeyPos, Input, Output, []). +-spec(keysort(KeyPos, Input, Output, Options) -> Reply when + KeyPos :: key_pos(), + Input :: input(), + Output :: output(), + Options :: options(), + Reply :: ok | {error, reason()} | input_reply() | output_reply()). keysort(KeyPos, Input0, Output0, Options) -> R = case {is_keypos(KeyPos), is_input(Input0), maybe_output(Output0), options(Options)} of @@ -87,9 +160,18 @@ keysort(KeyPos, Input0, Output0, Options) -> badarg(culprit(O), [KeyPos, Input0, Output0, Options]) end. +-spec(merge(FileNames, Output) -> Reply when + FileNames :: file_names(), + Output :: output(), + Reply :: ok | {error, reason()} | output_reply()). merge(Files, Output) -> merge(Files, Output, []). +-spec(merge(FileNames, Output, Options) -> Reply when + FileNames :: file_names(), + Output :: output(), + Options :: options(), + Reply :: ok | {error, reason()} | output_reply()). merge(Files0, Output0, Options) -> case {is_files(Files0), maybe_output(Output0), options(Options)} of %% size not used @@ -99,9 +181,20 @@ merge(Files0, Output0, Options) -> badarg(culprit(tuple_to_list(T)), [Files0, Output0, Options]) end. +-spec(keymerge(KeyPos, FileNames, Output) -> Reply when + KeyPos :: key_pos(), + FileNames :: file_names(), + Output :: output(), + Reply :: ok | {error, reason()} | output_reply()). keymerge(KeyPos, Files, Output) -> keymerge(KeyPos, Files, Output, []). +-spec(keymerge(KeyPos, FileNames, Output, Options) -> Reply when + KeyPos :: key_pos(), + FileNames :: file_names(), + Output :: output(), + Options :: options(), + Reply :: ok | {error, reason()} | output_reply()). keymerge(KeyPos, Files0, Output0, Options) -> R = case {is_keypos(KeyPos), is_files(Files0), maybe_output(Output0), options(Options)} of @@ -121,9 +214,21 @@ keymerge(KeyPos, Files0, Output0, Options) -> badarg(culprit(O), [KeyPos, Files0, Output0, Options]) end. +-spec(check(FileName) -> Reply when + FileName :: file_name(), + Reply :: {ok, [Result]} | {error, reason()}, + Result :: {FileName, TermPosition, term()}, + TermPosition :: pos_integer()). check(FileName) -> check([FileName], []). +-spec(check(FileNames, Options) -> Reply when + FileNames :: file_names(), + Options :: options(), + Reply :: {ok, [Result]} | {error, reason()}, + Result :: {FileName, TermPosition, term()}, + FileName :: file_name(), + TermPosition :: pos_integer()). check(Files0, Options) -> case {is_files(Files0), options(Options)} of {{true,Files}, #opts{}=Opts} -> @@ -132,9 +237,23 @@ check(Files0, Options) -> badarg(culprit(tuple_to_list(T)), [Files0, Options]) end. +-spec(keycheck(KeyPos, FileName) -> Reply when + KeyPos :: key_pos(), + FileName :: file_name(), + Reply :: {ok, [Result]} | {error, reason()}, + Result :: {FileName, TermPosition, term()}, + TermPosition :: pos_integer()). keycheck(KeyPos, FileName) -> keycheck(KeyPos, [FileName], []). +-spec(keycheck(KeyPos, FileNames, Options) -> Reply when + KeyPos :: key_pos(), + FileNames :: file_names(), + Options :: options(), + Reply :: {ok, [Result]} | {error, reason()}, + Result :: {FileName, TermPosition, term()}, + FileName :: file_name(), + TermPosition :: pos_integer()). keycheck(KeyPos, Files0, Options) -> R = case {is_keypos(KeyPos), is_files(Files0), options(Options)} of {_, _, #opts{format = binary}} -> @@ -191,7 +310,7 @@ options([{format, Format} | L], Opts) when Format =:= binary; options([{format, binary_term} | L], Opts) -> options(L, Opts#opts{format = binary_term_fun()}); options([{size, Size} | L], Opts) when is_integer(Size), Size >= 0 -> - options(L, Opts#opts{size = max(Size, 1)}); + options(L, Opts#opts{size = erlang:max(Size, 1)}); options([{no_files, NoFiles} | L], Opts) when is_integer(NoFiles), NoFiles > 1 -> options(L, Opts#opts{no_files = NoFiles}); @@ -997,10 +1116,10 @@ close_read_fun(Fd, FileName, fsort) -> file:delete(FileName). read_objs(Fd, FileName, I, L, Bin0, Size0, LSz, W) -> - Max = max(Size0, ?CHUNKSIZE), + Max = erlang:max(Size0, ?CHUNKSIZE), BSz0 = byte_size(Bin0), Min = Size0 - BSz0 + W#w.hdlen, % Min > 0 - NoBytes = max(Min, Max), + NoBytes = erlang:max(Min, Max), case read(Fd, FileName, NoBytes, W) of {ok, Bin} -> BSz = byte_size(Bin), @@ -1180,9 +1299,6 @@ make_key2([Kp], T) -> make_key2([Kp | Kps], T) -> [element(Kp, T) | make_key2(Kps, T)]. -max(A, B) when A < B -> B; -max(A, _) -> A. - infun(W) -> W1 = W#w{in = undefined}, try (W#w.in)(read) of diff --git a/lib/stdlib/src/filelib.erl b/lib/stdlib/src/filelib.erl index 74c5172137..d532cea187 100644 --- a/lib/stdlib/src/filelib.erl +++ b/lib/stdlib/src/filelib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2010. All Rights Reserved. +%% Copyright Ericsson AB 1997-2011. 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,8 @@ %% 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([fold_files/5, last_modified/1, file_size/1, ensure_dir/1]). @@ -38,68 +40,85 @@ erlang:error(UnUsUalVaRiAbLeNaMe) end). +-type filename() :: file:name(). +-type dirname() :: filename(). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec wildcard(name()) -> [file:filename()]. +-spec wildcard(Wildcard) -> [file:filename()] when + Wildcard :: filename() | dirname(). wildcard(Pattern) when is_list(Pattern) -> ?HANDLE_ERROR(do_wildcard(Pattern, file)). --spec wildcard(name(), name() | atom()) -> [file:filename()]. -wildcard(Pattern, Cwd) when is_list(Pattern), is_list(Cwd) -> +-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)) -> ?HANDLE_ERROR(do_wildcard(Pattern, Cwd, file)); wildcard(Pattern, Mod) when is_list(Pattern), is_atom(Mod) -> ?HANDLE_ERROR(do_wildcard(Pattern, Mod)). --spec wildcard(name(), name(), atom()) -> [file:filename()]. +-spec wildcard(file:name(), file:name(), atom()) -> [file:filename()]. wildcard(Pattern, Cwd, Mod) - when is_list(Pattern), is_list(Cwd), is_atom(Mod) -> + when is_list(Pattern), (is_list(Cwd) or is_binary(Cwd)), is_atom(Mod) -> ?HANDLE_ERROR(do_wildcard(Pattern, Cwd, Mod)). --spec is_dir(name()) -> boolean(). +-spec is_dir(Name) -> boolean() when + Name :: filename() | dirname(). is_dir(Dir) -> do_is_dir(Dir, file). --spec is_dir(name(), atom()) -> boolean(). +-spec is_dir(file:name(), atom()) -> boolean(). is_dir(Dir, Mod) when is_atom(Mod) -> do_is_dir(Dir, Mod). --spec is_file(name()) -> boolean(). +-spec is_file(Name) -> boolean() when + Name :: filename() | dirname(). is_file(File) -> do_is_file(File, file). --spec is_file(name(), atom()) -> boolean(). +-spec is_file(file:name(), atom()) -> boolean(). is_file(File, Mod) when is_atom(Mod) -> do_is_file(File, Mod). --spec is_regular(name()) -> boolean(). +-spec is_regular(Name) -> boolean() when + Name :: filename(). is_regular(File) -> do_is_regular(File, file). --spec is_regular(name(), atom()) -> boolean(). +-spec is_regular(file:name(), atom()) -> boolean(). is_regular(File, Mod) when is_atom(Mod) -> do_is_regular(File, Mod). --spec fold_files(name(), string(), boolean(), fun((_,_) -> _), _) -> _. +-spec fold_files(Dir, RegExp, Recursive, Fun, AccIn) -> AccOut when + Dir :: dirname(), + RegExp :: string(), + Recursive :: boolean(), + Fun :: fun((F :: file:filename(), AccIn) -> AccOut), + AccIn :: term(), + AccOut :: term(). fold_files(Dir, RegExp, Recursive, Fun, Acc) -> do_fold_files(Dir, RegExp, Recursive, Fun, Acc, file). --spec fold_files(name(), string(), boolean(), fun((_,_) -> _), _, atom()) -> _. +-spec fold_files(file:name(), string(), boolean(), fun((_,_) -> _), _, atom()) -> _. fold_files(Dir, RegExp, Recursive, Fun, Acc, Mod) when is_atom(Mod) -> do_fold_files(Dir, RegExp, Recursive, Fun, Acc, Mod). --spec last_modified(name()) -> date_time() | 0. +-spec last_modified(Name) -> file:date_time() | 0 when + Name :: filename() | dirname(). last_modified(File) -> do_last_modified(File, file). --spec last_modified(name(), atom()) -> date_time() | 0. +-spec last_modified(file:name(), atom()) -> file:date_time() | 0. last_modified(File, Mod) when is_atom(Mod) -> do_last_modified(File, Mod). --spec file_size(name()) -> non_neg_integer(). +-spec file_size(Filename) -> non_neg_integer() when + Filename :: filename(). file_size(File) -> do_file_size(File, file). --spec file_size(name(), atom()) -> non_neg_integer(). +-spec file_size(file:name(), atom()) -> non_neg_integer(). file_size(File, Mod) when is_atom(Mod) -> do_file_size(File, Mod). @@ -116,7 +135,7 @@ do_wildcard_comp({compiled_wildcard,{exists,File}}, Mod) -> 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) -> +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) -> @@ -125,9 +144,18 @@ do_wildcard_comp({compiled_wildcard,{exists,File}}, Cwd, Mod) -> _ -> [] end; do_wildcard_comp({compiled_wildcard,[current|Rest]}, Cwd0, Mod) -> - Cwd = filename:join([Cwd0]), %Slash away redundant slashes. - PrefixLen = length(Cwd)+1, - [lists:nthtail(PrefixLen, N) || N <- do_wildcard_1([Cwd], Rest, 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). @@ -164,36 +192,44 @@ do_is_regular(File, Mod) -> %% If <Recursive> is true all sub-directories to <Dir> are processed do_fold_files(Dir, RegExp, Recursive, Fun, Acc, Mod) -> - {ok, Re1} = re:compile(RegExp), - do_fold_files1(Dir, Re1, Recursive, Fun, Acc, Mod). + {ok, Re1} = re:compile(RegExp,[unicode]), + do_fold_files1(Dir, Re1, RegExp, Recursive, Fun, Acc, Mod). -do_fold_files1(Dir, RegExp, Recursive, Fun, Acc, Mod) -> +do_fold_files1(Dir, RegExp, OrigRE, Recursive, Fun, Acc, Mod) -> case eval_list_dir(Dir, Mod) of - {ok, Files} -> do_fold_files2(Files, Dir, RegExp, Recursive, Fun, Acc, Mod); + {ok, Files} -> do_fold_files2(Files, Dir, RegExp, OrigRE, + Recursive, Fun, Acc, Mod); {error, _} -> Acc end. -do_fold_files2([], _Dir, _RegExp, _Recursive, _Fun, Acc, _Mod) -> +%% OrigRE is not to be compiled as it's for non conforming filenames, +%% i.e. for filenames that does not comply to the current encoding, which should +%% be very rare. We use it only in those cases and do not want to precompile. +do_fold_files2([], _Dir, _RegExp, _OrigRE, _Recursive, _Fun, Acc, _Mod) -> Acc; -do_fold_files2([File|T], Dir, RegExp, Recursive, Fun, Acc0, Mod) -> +do_fold_files2([File|T], Dir, RegExp, OrigRE, Recursive, Fun, Acc0, Mod) -> FullName = filename:join(Dir, File), case do_is_regular(FullName, Mod) of true -> - case re:run(File, RegExp, [{capture,none}]) of + case (catch re:run(File, if is_binary(File) -> OrigRE; + true -> RegExp end, + [{capture,none}])) of match -> Acc = Fun(FullName, Acc0), - do_fold_files2(T, Dir, RegExp, Recursive, Fun, Acc, Mod); + do_fold_files2(T, Dir, RegExp, OrigRE, Recursive, Fun, Acc, Mod); + {'EXIT',_} -> + do_fold_files2(T, Dir, RegExp, OrigRE, Recursive, Fun, Acc0, Mod); nomatch -> - do_fold_files2(T, Dir, RegExp, Recursive, Fun, Acc0, Mod) + do_fold_files2(T, Dir, RegExp, OrigRE, Recursive, Fun, Acc0, Mod) end; false -> case Recursive andalso do_is_dir(FullName, Mod) of true -> - Acc1 = do_fold_files1(FullName, RegExp, Recursive, + Acc1 = do_fold_files1(FullName, RegExp, OrigRE, Recursive, Fun, Acc0, Mod), - do_fold_files2(T, Dir, RegExp, Recursive, Fun, Acc1, Mod); + do_fold_files2(T, Dir, RegExp, OrigRE, Recursive, Fun, Acc1, Mod); false -> - do_fold_files2(T, Dir, RegExp, Recursive, Fun, Acc0, Mod) + do_fold_files2(T, Dir, RegExp, OrigRE, Recursive, Fun, Acc0, Mod) end end. @@ -218,7 +254,9 @@ do_file_size(File, Mod) -> %% +type X = filename() | dirname() %% ensures that the directory name required to create D exists --spec ensure_dir(name()) -> 'ok' | {'error', posix()}. +-spec ensure_dir(Name) -> 'ok' | {'error', Reason} when + Name :: filename() | dirname(), + Reason :: file:posix(). ensure_dir("/") -> ok; ensure_dir(F) -> @@ -266,6 +304,13 @@ 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]); + false -> + wildcard_4(Pattern, Rest, Base, Result) + end; wildcard_4(Pattern, [File|Rest], Base, Result) -> case wildcard_5(Pattern, File) of true -> diff --git a/lib/stdlib/src/filename.erl b/lib/stdlib/src/filename.erl index cd26b2e219..1cb9e4a25e 100644 --- a/lib/stdlib/src/filename.erl +++ b/lib/stdlib/src/filename.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1997-2011. 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(filename). @@ -41,6 +41,9 @@ -include_lib("kernel/include/file.hrl"). +-define(IS_DRIVELETTER(Letter),(((Letter >= $A) andalso (Letter =< $Z)) orelse + ((Letter >= $a) andalso (Letter =< $z)))). + %% Converts a relative filename to an absolute filename %% or the filename itself if it already is an absolute filename %% Note that no attempt is made to create the most beatiful @@ -57,12 +60,21 @@ %% (for Unix) : absname("/") -> "/" %% (for WIN32): absname("/") -> "D:/" --spec absname(name()) -> string(). + +-spec absname(Filename) -> file:filename() when + Filename :: file:name(). absname(Name) -> {ok, Cwd} = file:get_cwd(), absname(Name, Cwd). --spec absname(name(), string()) -> string(). +-spec absname(Filename, Dir) -> file:filename() when + Filename :: file:name(), + Dir :: file:filename(). +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) -> + absname(filename_string_to_binary(Name),AbsBase); + absname(Name, AbsBase) -> case pathtype(Name) of relative -> @@ -77,6 +89,20 @@ absname(Name, AbsBase) -> %% Handles volumerelative names (on Windows only). +absname_vr([<<"/">>|Rest1], [Volume|_], _AbsBase) -> + %% Absolute path on current drive. + join([Volume|Rest1]); +absname_vr([<<X, $:>>|Rest1], [<<X,_/binary>>|_], AbsBase) -> + %% Relative to current directory on current drive. + absname(join(Rest1), AbsBase); +absname_vr([<<X, $:>>|Name], _, _AbsBase) -> + %% Relative to current directory on another drive. + Dcwd = + case file:get_cwd([X, $:]) of + {ok, Dir} -> filename_string_to_binary(Dir); + {error, _} -> <<X, $:, $/>> + end, + absname(join(Name), Dcwd); absname_vr(["/"|Rest1], [Volume|_], _AbsBase) -> %% Absolute path on current drive. join([Volume|Rest1]); @@ -92,41 +118,15 @@ absname_vr([[X, $:]|Name], _, _AbsBase) -> end, absname(join(Name), Dcwd). -%% Joins a relative filename to an absolute base. For VxWorks the -%% resulting name is fixed to minimize the length by collapsing -%% ".." directories. -%% For other systems this is just a join/2, but assumes that +%% Joins a relative filename to an absolute base. +%% This is just a join/2, but assumes that %% AbsBase must be absolute and Name must be relative. --spec absname_join(string(), name()) -> string(). +-spec absname_join(Dir, Filename) -> file:filename() when + Dir :: file:filename(), + Filename :: file:name(). absname_join(AbsBase, Name) -> - case major_os_type() of - vxworks -> - absname_pretty(AbsBase, split(Name), lists:reverse(split(AbsBase))); - _Else -> - join(AbsBase, flatten(Name)) - end. - -%% Handles absolute filenames for VxWorks - these are 'pretty-printed', -%% since a C function call chdir("/erlang/lib/../bin") really sets -%% cwd to '/erlang/lib/../bin' which also works, but the long term -%% effect is potentially not so good ... -%% -%% absname_pretty("../bin", "/erlang/lib") -> "/erlang/bin" -%% absname_pretty("../../../..", "/erlang") -> "/erlang" - -absname_pretty(Abspath, Relpath, []) -> - %% AbsBase _must_ begin with a vxworks device name - {device, _Rest, Dev} = vxworks_first(Abspath), - absname_pretty(Abspath, Relpath, [lists:reverse(Dev)]); -absname_pretty(_Abspath, [], AbsBase) -> - join(lists:reverse(AbsBase)); -absname_pretty(Abspath, [[$.]|Rest], AbsBase) -> - absname_pretty(Abspath, Rest, AbsBase); -absname_pretty(Abspath, [[$.,$.]|Rest], [_|AbsRest]) -> - absname_pretty(Abspath, Rest, AbsRest); -absname_pretty(Abspath, [First|Rest], AbsBase) -> - absname_pretty(Abspath, Rest, [First|AbsBase]). + join(AbsBase, flatten(Name)). %% Returns the part of the filename after the last directory separator, %% or the filename itself if it has no separators. @@ -136,18 +136,41 @@ absname_pretty(Abspath, [First|Rest], AbsBase) -> %% basename("/usr/foo/") -> "foo" (trailing slashes ignored) %% basename("/") -> [] --spec basename(name()) -> string(). +-spec basename(Filename) -> file:filename() when + Filename :: file:name(). +basename(Name) when is_binary(Name) -> + case os:type() of + {win32,_} -> + win_basenameb(Name); + _ -> + basenameb(Name,[<<"/">>]) + end; + basename(Name0) -> Name = flatten(Name0), {DirSep2, DrvSep} = separators(), basename1(skip_prefix(Name, DrvSep), [], DirSep2). +win_basenameb(<<Letter,$:,Rest/binary>>) when ?IS_DRIVELETTER(Letter) -> + basenameb(Rest,[<<"/">>,<<"\\">>]); +win_basenameb(O) -> + basenameb(O,[<<"/">>,<<"\\">>]). +basenameb(Bin,Sep) -> + Parts = [ X || X <- binary:split(Bin,Sep,[global]), + X =/= <<>> ], + if + Parts =:= [] -> + <<>>; + true -> + lists:last(Parts) + end. + + + basename1([$/|[]], Tail, DirSep2) -> basename1([], Tail, DirSep2); basename1([$/|Rest], _Tail, DirSep2) -> basename1(Rest, [], DirSep2); -basename1([[_|_]=List|Rest], Tail, DirSep2) -> - basename1(List++Rest, Tail, DirSep2); basename1([DirSep2|Rest], Tail, DirSep2) when is_integer(DirSep2) -> basename1([$/|Rest], Tail, DirSep2); basename1([Char|Rest], Tail, DirSep2) when is_integer(Char) -> @@ -155,26 +178,11 @@ basename1([Char|Rest], Tail, DirSep2) when is_integer(Char) -> basename1([], Tail, _DirSep2) -> lists:reverse(Tail). -skip_prefix(Name, false) -> % No prefix for unix, but for VxWorks. - case major_os_type() of - vxworks -> - case vxworks_first(Name) of - {device, Rest, _Device} -> - Rest; - {not_device, _Rest, _First} -> - Name - end; - _Else -> - Name - end; -skip_prefix(Name, DrvSep) -> - skip_prefix1(Name, DrvSep). - -skip_prefix1([L, DrvSep|Name], DrvSep) when is_integer(L) -> +skip_prefix(Name, false) -> + Name; +skip_prefix([L, DrvSep|Name], DrvSep) when ?IS_DRIVELETTER(L) -> Name; -skip_prefix1([L], _) when is_integer(L) -> - [L]; -skip_prefix1(Name, _) -> +skip_prefix(Name, _) -> Name. %% Returns the last component of the filename, with the given @@ -190,7 +198,31 @@ skip_prefix1(Name, _) -> %% rootname(basename("xxx.jam")) -> "xxx" %% rootname(basename("xxx.erl")) -> "xxx" --spec basename(name(), name()) -> string(). +-spec basename(Filename, Ext) -> file:filename() when + Filename :: file:name(), + Ext :: file:name(). +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) -> + basename(filename_string_to_binary(Name),Ext); +basename(Name, Ext) when is_binary(Name), is_binary(Ext) -> + BName = basename(Name), + LAll = byte_size(Name), + LN = byte_size(BName), + LE = byte_size(Ext), + case LN - LE of + Neg when Neg < 0 -> + BName; + Pos -> + StartLen = LAll - Pos - LE, + case Name of + <<_:StartLen/binary,Part:Pos/binary,Ext/binary>> -> + Part; + _Other -> + BName + end + end; + basename(Name0, Ext0) -> Name = flatten(Name0), Ext = flatten(Ext0), @@ -204,7 +236,7 @@ basename([$/|[]], Ext, Tail, DrvSep2) -> basename([], Ext, Tail, DrvSep2); basename([$/|Rest], Ext, _Tail, DrvSep2) -> basename(Rest, Ext, [], DrvSep2); -basename([$\\|Rest], Ext, Tail, DirSep2) when is_integer(DirSep2) -> +basename([DirSep2|Rest], Ext, Tail, DirSep2) when is_integer(DirSep2) -> basename([$/|Rest], Ext, Tail, DirSep2); basename([Char|Rest], Ext, Tail, DrvSep2) when is_integer(Char) -> basename(Rest, Ext, [Char|Tail], DrvSep2); @@ -216,24 +248,45 @@ basename([], _Ext, Tail, _DrvSep2) -> %% Example: dirname("/usr/src/kalle.erl") -> "/usr/src", %% dirname("kalle.erl") -> "." --spec dirname(name()) -> string(). +-spec dirname(Filename) -> file:filename() when + Filename :: file:name(). +dirname(Name) when is_binary(Name) -> + {Dsep,Drivesep} = separators(), + SList = case Dsep of + Sep when is_integer(Sep) -> + [ <<Sep>> ]; + _ -> + [] + end, + {XPart0,Dirs} = case Drivesep of + X when is_integer(X) -> + case Name of + <<DL,X,Rest/binary>> when ?IS_DRIVELETTER(DL) -> + {<<DL,X>>,Rest}; + _ -> + {<<>>,Name} + end; + _ -> + {<<>>,Name} + end, + Parts0 = binary:split(Dirs,[<<"/">>|SList],[global]), + %% Fairly short lists of parts, OK to reverse twice... + Parts = case Parts0 of + [] -> []; + _ -> lists:reverse(fstrip(tl(lists:reverse(Parts0)))) + end, + XPart = case {Parts,XPart0} of + {[],<<>>} -> + <<".">>; + _ -> + XPart0 + end, + dirjoin(Parts,XPart,<<"/">>); + dirname(Name0) -> Name = flatten(Name0), - case os:type() of - vxworks -> - {Devicep, Restname, FirstComp} = vxworks_first(Name), - case Devicep of - device -> - dirname(Restname, FirstComp, [], separators()); - _ -> - dirname(Name, [], [], separators()) - end; - _ -> - dirname(Name, [], [], separators()) - end. + dirname(Name, [], [], separators()). -dirname([[_|_]=List|Rest], Dir, File, Seps) -> - dirname(List++Rest, Dir, File, Seps); dirname([$/|Rest], Dir, File, Seps) -> dirname(Rest, File++Dir, [$/], Seps); dirname([DirSep|Rest], Dir, File, {DirSep,_}=Seps) when is_integer(DirSep) -> @@ -258,6 +311,26 @@ dirname([], [DrvSep,Dl], File, {_,DrvSep}) -> end; dirname([], Dir, _, _) -> lists:reverse(Dir). + +%% Compatibility with lists variant, remove trailing slashes +fstrip([<<>>,X|Y]) -> + fstrip([X|Y]); +fstrip(A) -> + A. + + +dirjoin([<<>>|T],Acc,Sep) -> + dirjoin1(T,<<Acc/binary,"/">>,Sep); +dirjoin(A,B,C) -> + dirjoin1(A,B,C). + +dirjoin1([],Acc,_) -> + Acc; +dirjoin1([One],Acc,_) -> + <<Acc/binary,One/binary>>; +dirjoin1([H|T],Acc,Sep) -> + dirjoin(T,<<Acc/binary,H/binary,Sep/binary>>,Sep). + %% Given a filename string, returns the file extension, %% including the period. Returns an empty list if there @@ -268,7 +341,30 @@ dirname([], Dir, _, _) -> %% %% On Windows: fn:dirname("\\usr\\src/kalle.erl") -> "/usr/src" --spec extension(name()) -> string(). +-spec extension(Filename) -> file:filename() when + Filename :: file:name(). +extension(Name) when is_binary(Name) -> + {Dsep,_} = separators(), + SList = case Dsep of + Sep when is_integer(Sep) -> + [ <<Sep>> ]; + _ -> + [] + end, + case binary:matches(Name,[<<".">>]) of + [] -> + <<>>; + List -> + {Pos,_} = lists:last(List), + <<_:Pos/binary,Part/binary>> = Name, + case binary:match(Part,[<<"/">>|SList]) of + nomatch -> + Part; + _ -> + <<>> + end + end; + extension(Name0) -> Name = flatten(Name0), extension(Name, [], major_os_type()). @@ -281,8 +377,6 @@ extension([$/|Rest], _Result, OsType) -> extension(Rest, [], OsType); extension([$\\|Rest], _Result, win32) -> extension(Rest, [], win32); -extension([$\\|Rest], _Result, vxworks) -> - extension(Rest, [], vxworks); extension([Char|Rest], Result, OsType) when is_integer(Char) -> extension(Rest, [Char|Result], OsType); extension([], Result, _OsType) -> @@ -290,23 +384,39 @@ extension([], Result, _OsType) -> %% Joins a list of filenames with directory separators. --spec join([string()]) -> string(). +-spec join(Components) -> file:filename() when + Components :: [file:filename()]. join([Name1, Name2|Rest]) -> join([join(Name1, Name2)|Rest]); join([Name]) when is_list(Name) -> join1(Name, [], [], major_os_type()); +join([Name]) when is_binary(Name) -> + join1b(Name, <<>>, [], major_os_type()); join([Name]) when is_atom(Name) -> join([atom_to_list(Name)]). %% Joins two filenames with directory separators. --spec join(string(), string()) -> string(). +-spec join(Name1, Name2) -> file:filename() when + Name1 :: file:filename(), + Name2 :: file:filename(). join(Name1, Name2) when is_list(Name1), is_list(Name2) -> OsType = major_os_type(), case pathtype(Name2) of relative -> join1(Name1, Name2, [], OsType); _Other -> join1(Name2, [], [], OsType) end; +join(Name1, Name2) when is_binary(Name1), is_list(Name2) -> + join(Name1,filename_string_to_binary(Name2)); +join(Name1, Name2) when is_list(Name1), is_binary(Name2) -> + join(filename_string_to_binary(Name1),Name2); +join(Name1, Name2) when is_binary(Name1), is_binary(Name2) -> + OsType = major_os_type(), + case pathtype(Name2) of + relative -> join1b(Name1, Name2, [], OsType); + _Other -> join1b(Name2, <<>>, [], OsType) + end; + join(Name1, Name2) when is_atom(Name1) -> join(atom_to_list(Name1), Name2); join(Name1, Name2) when is_atom(Name2) -> @@ -321,8 +431,6 @@ when is_integer(UcLetter), UcLetter >= $A, UcLetter =< $Z -> join1(Rest, RelativeName, [$:, UcLetter+$a-$A], win32); join1([$\\|Rest], RelativeName, Result, win32) -> join1([$/|Rest], RelativeName, Result, win32); -join1([$\\|Rest], RelativeName, Result, vxworks) -> - join1([$/|Rest], RelativeName, Result, vxworks); join1([$/|Rest], RelativeName, [$., $/|Result], OsType) -> join1(Rest, RelativeName, [$/|Result], OsType); join1([$/|Rest], RelativeName, [$/|Result], OsType) -> @@ -344,6 +452,26 @@ join1([Char|Rest], RelativeName, Result, OsType) when is_integer(Char) -> join1([Atom|Rest], RelativeName, Result, OsType) when is_atom(Atom) -> join1(atom_to_list(Atom)++Rest, RelativeName, Result, OsType). +join1b(<<UcLetter, $:, Rest/binary>>, RelativeName, [], win32) +when is_integer(UcLetter), UcLetter >= $A, UcLetter =< $Z -> + join1b(Rest, RelativeName, [$:, UcLetter+$a-$A], win32); +join1b(<<$\\,Rest/binary>>, RelativeName, Result, win32) -> + join1b(<<$/,Rest/binary>>, RelativeName, Result, win32); +join1b(<<$/,Rest/binary>>, RelativeName, [$., $/|Result], OsType) -> + join1b(Rest, RelativeName, [$/|Result], OsType); +join1b(<<$/,Rest/binary>>, RelativeName, [$/|Result], OsType) -> + join1b(Rest, RelativeName, [$/|Result], OsType); +join1b(<<>>, <<>>, Result, OsType) -> + list_to_binary(maybe_remove_dirsep(Result, OsType)); +join1b(<<>>, RelativeName, [$:|Rest], win32) -> + join1b(RelativeName, <<>>, [$:|Rest], win32); +join1b(<<>>, RelativeName, [$/|Result], OsType) -> + join1b(RelativeName, <<>>, [$/|Result], OsType); +join1b(<<>>, RelativeName, Result, OsType) -> + join1b(RelativeName, <<>>, [$/|Result], OsType); +join1b(<<Char,Rest/binary>>, RelativeName, Result, OsType) when is_integer(Char) -> + join1b(Rest, RelativeName, [Char|Result], OsType). + maybe_remove_dirsep([$/, $:, Letter], win32) -> [Letter, $:, $/]; maybe_remove_dirsep([$/], _) -> @@ -357,7 +485,13 @@ 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(string(), name()) -> string(). +-spec append(file:filename(), file:name()) -> file:filename(). +append(Dir, Name) when is_binary(Dir), is_binary(Name) -> + <<Dir/binary,$/:8,Name/binary>>; +append(Dir, Name) when is_binary(Dir) -> + append(Dir,filename_string_to_binary(Name)); +append(Dir, Name) when is_binary(Name) -> + append(filename_string_to_binary(Dir),Name); append(Dir, Name) -> Dir ++ [$/|Name]. @@ -373,22 +507,18 @@ append(Dir, Name) -> %% current working volume. (Windows only) %% Example: a:bar.erl, /temp/foo.erl --spec pathtype(name()) -> 'absolute' | 'relative' | 'volumerelative'. +-spec pathtype(Path) -> 'absolute' | 'relative' | 'volumerelative' when + Path :: file:name(). pathtype(Atom) when is_atom(Atom) -> pathtype(atom_to_list(Atom)); -pathtype(Name) when is_list(Name) -> +pathtype(Name) when is_list(Name) or is_binary(Name) -> case os:type() of {unix, _} -> unix_pathtype(Name); - {win32, _} -> win32_pathtype(Name); - vxworks -> case vxworks_first(Name) of - {device, _Rest, _Dev} -> - absolute; - _ -> - relative - end; - {ose,_} -> unix_pathtype(Name) + {win32, _} -> win32_pathtype(Name) end. +unix_pathtype(<<$/,_/binary>>) -> + absolute; unix_pathtype([$/|_]) -> absolute; unix_pathtype([List|Rest]) when is_list(List) -> @@ -404,6 +534,15 @@ win32_pathtype([Atom|Rest]) when is_atom(Atom) -> win32_pathtype(atom_to_list(Atom)++Rest); win32_pathtype([Char, List|Rest]) when is_list(List) -> win32_pathtype([Char|List++Rest]); +win32_pathtype(<<$/, $/, _/binary>>) -> absolute; +win32_pathtype(<<$\\, $/, _/binary>>) -> absolute; +win32_pathtype(<<$/, $\\, _/binary>>) -> absolute; +win32_pathtype(<<$\\, $\\, _/binary>>) -> absolute; +win32_pathtype(<<$/, _/binary>>) -> volumerelative; +win32_pathtype(<<$\\, _/binary>>) -> volumerelative; +win32_pathtype(<<_Letter, $:, $/, _/binary>>) -> absolute; +win32_pathtype(<<_Letter, $:, $\\, _/binary>>) -> absolute; +win32_pathtype(<<_Letter, $:, _/binary>>) -> volumerelative; win32_pathtype([$/, $/|_]) -> absolute; win32_pathtype([$\\, $/|_]) -> absolute; win32_pathtype([$/, $\\|_]) -> absolute; @@ -422,7 +561,10 @@ win32_pathtype(_) -> relative. %% Examples: rootname("/jam.src/kalle") -> "/jam.src/kalle" %% rootname("/jam.src/foo.erl") -> "/jam.src/foo" --spec rootname(name()) -> string(). +-spec rootname(Filename) -> file:filename() when + Filename :: file:name(). +rootname(Name) when is_binary(Name) -> + list_to_binary(rootname(binary_to_list(Name))); % No need to handle unicode, . is < 128 rootname(Name0) -> Name = flatten(Name0), rootname(Name, [], [], major_os_type()). @@ -431,8 +573,6 @@ rootname([$/|Rest], Root, Ext, OsType) -> rootname(Rest, [$/]++Ext++Root, [], OsType); rootname([$\\|Rest], Root, Ext, win32) -> rootname(Rest, [$/]++Ext++Root, [], win32); -rootname([$\\|Rest], Root, Ext, vxworks) -> - rootname(Rest, [$/]++Ext++Root, [], vxworks); rootname([$.|Rest], Root, [], OsType) -> rootname(Rest, Root, ".", OsType); rootname([$.|Rest], Root, Ext, OsType) -> @@ -451,7 +591,15 @@ 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(name(), name()) -> string(). +-spec rootname(Filename, Ext) -> file:filename() when + Filename :: file:name(), + Ext :: file:name(). +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) -> + rootname(Name,filename_string_to_binary(Ext)); +rootname(Name, Ext) when is_binary(Ext) -> + rootname(filename_string_to_binary(Name),Ext); rootname(Name0, Ext0) -> Name = flatten(Name0), Ext = flatten(Ext0), @@ -471,27 +619,57 @@ rootname2([Char|Rest], Ext, Result) when is_integer(Char) -> %% split("foo/bar") -> ["foo", "bar"] %% split("a:\\msdev\\include") -> ["a:/", "msdev", "include"] --spec split(name()) -> [string()]. +-spec split(Filename) -> Components when + Filename :: file:name(), + Components :: [file:filename()]. +split(Name) when is_binary(Name) -> + case os:type() of + {win32, _} -> win32_splitb(Name); + _ -> unix_splitb(Name) + end; + split(Name0) -> Name = flatten(Name0), case os:type() of - {unix, _} -> unix_split(Name); {win32, _} -> win32_split(Name); - vxworks -> vxworks_split(Name); - {ose,_} -> unix_split(Name) + _ -> unix_split(Name) end. -%% If a VxWorks filename starts with '[/\].*[^/\]' '[/\].*:' or '.*:' -%% that part of the filename is considered a device. -%% The rest of the name is interpreted exactly as for win32. -%% XXX - dirty solution to make filename:split([]) return the same thing on -%% VxWorks as on unix and win32. -vxworks_split([]) -> - []; -vxworks_split(L) -> - {_Devicep, Rest, FirstComp} = vxworks_first(L), - split(Rest, [], [lists:reverse(FirstComp)], win32). +unix_splitb(Name) -> + L = binary:split(Name,[<<"/">>],[global]), + LL = case L of + [<<>>|Rest] -> + [<<"/">>|Rest]; + _ -> + L + end, + [ X || X <- LL, X =/= <<>>]. + + +fix_driveletter(Letter0) -> + if + Letter0 >= $A, Letter0 =< $Z -> + Letter0+$a-$A; + true -> + Letter0 + end. +win32_splitb(<<Letter0,$:, Slash, Rest/binary>>) when (((Slash =:= $\\) orelse (Slash =:= $/)) andalso + ?IS_DRIVELETTER(Letter0)) -> + Letter = fix_driveletter(Letter0), + L = binary:split(Rest,[<<"/">>,<<"\\">>],[global]), + [<<Letter,$:,$/>> | [ X || X <- L, X =/= <<>> ]]; +win32_splitb(<<Letter0,$:,Rest/binary>>) when ?IS_DRIVELETTER(Letter0) -> + Letter = fix_driveletter(Letter0), + L = binary:split(Rest,[<<"/">>,<<"\\">>],[global]), + [<<Letter,$:>> | [ X || X <- L, X =/= <<>> ]]; +win32_splitb(<<Slash,Rest/binary>>) when ((Slash =:= $\\) orelse (Slash =:= $/)) -> + L = binary:split(Rest,[<<"/">>,<<"\\">>],[global]), + [<<$/>> | [ X || X <- L, X =/= <<>> ]]; +win32_splitb(Name) -> + L = binary:split(Name,[<<"/">>,<<"\\">>],[global]), + [ X || X <- L, X =/= <<>> ]. + unix_split(Name) -> split(Name, [], unix). @@ -502,8 +680,6 @@ win32_split([X, $\\|Rest]) when is_integer(X) -> win32_split([X, $/|Rest]); win32_split([X, Y, $\\|Rest]) when is_integer(X), is_integer(Y) -> win32_split([X, Y, $/|Rest]); -win32_split([$/, $/|Rest]) -> - split(Rest, [], [[$/, $/]]); win32_split([UcLetter, $:|Rest]) when UcLetter >= $A, UcLetter =< $Z -> win32_split([UcLetter+$a-$A, $:|Rest]); win32_split([Letter, $:, $/|Rest]) -> @@ -528,8 +704,6 @@ split([$/|Rest], Comp, Components, OsType) -> split(Rest, [], [lists:reverse(Comp)|Components], OsType); split([Char|Rest], Comp, Components, OsType) when is_integer(Char) -> split(Rest, [Char|Comp], Components, OsType); -split([List|Rest], Comp, Components, OsType) when is_list(List) -> - split(List++Rest, Comp, Components, OsType); split([], [], Components, _OsType) -> lists:reverse(Components); split([], Comp, Components, OsType) -> @@ -540,7 +714,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(string()) -> string(). +-spec nativename(Path) -> file:filename() when + Path :: file:filename(). nativename(Name0) -> Name = join([Name0]), %Normalize. case os:type() of @@ -557,13 +732,12 @@ win32_nativename([]) -> separators() -> case os:type() of - {unix, _} -> {false, false}; {win32, _} -> {$\\, $:}; - vxworks -> {$\\, false}; - {ose,_} -> {false, false} + _ -> {false, false} end. + %% find_src(Module) -- %% find_src(Module, Rules) -- %% @@ -593,12 +767,17 @@ separators() -> %% The paths in the {outdir, Path} and {i, Path} options are guaranteed %% to be absolute. --type rule() :: {string(), string()}. --type ecode() :: 'non_existing' | 'preloaded' | 'interpreted'. --type option() :: {'i', string()} | {'outdir', string()} | {'d', atom()}. - --spec find_src(atom() | string()) -> - {string(), [option()]} | {'error', {ecode(), atom()}}. +-spec find_src(Beam) -> {SourceFile, Options} + | {error, {ErrorReason, Module}} when + Beam :: Module | Filename, + Filename :: atom() | string(), + Module :: module(), + SourceFile :: string(), + Options :: [Option], + Option :: {'i', Path :: string()} + | {'outdir', Path :: string()} + | {'d', atom()}, + ErrorReason :: 'non_existing' | 'preloaded' | 'interpreted'. find_src(Mod) -> Default = [{"", ""}, {"ebin", "src"}, {"ebin", "esrc"}], Rules = @@ -609,8 +788,18 @@ find_src(Mod) -> end, find_src(Mod, Rules). --spec find_src(atom() | string(), [rule()]) -> - {string(), [option()]} | {'error', {ecode(), atom()}}. +-spec find_src(Beam, Rules) -> {SourceFile, Options} + | {error, {ErrorReason, Module}} when + Beam :: Module | Filename, + Filename :: atom() | string(), + Rules :: [{BinSuffix :: string(), SourceSuffix :: string()}], + Module :: module(), + SourceFile :: string(), + Options :: [Option], + Option :: {'i', Path :: string()} + | {'outdir', Path :: string()} + | {'d', atom()}, + ErrorReason :: 'non_existing' | 'preloaded' | 'interpreted'. find_src(Mod, Rules) when is_atom(Mod) -> find_src(atom_to_list(Mod), Rules); find_src(File0, Rules) when is_list(File0) -> @@ -733,45 +922,13 @@ major_os_type() -> OsT -> OsT end. -%% Need to take care of the first pathname component separately -%% due to VxWorks less than good device naming rules. -%% (i.e. this is VxWorks specific ...) -%% The following four all starts with device names -%% elrond:/foo -> elrond: -%% elrond:\\foo.bar -> elrond: -%% /DISK1:foo -> /DISK1: -%% /usr/include -> /usr -%% This one doesn't: -%% foo/bar - -vxworks_first([]) -> - {not_device, [], []}; -vxworks_first([$/|T]) -> - vxworks_first2(device, T, [$/]); -vxworks_first([$\\|T]) -> - vxworks_first2(device, T, [$/]); -vxworks_first([H|T]) when is_list(H) -> - vxworks_first(H++T); -vxworks_first([H|T]) -> - vxworks_first2(not_device, T, [H]). - -vxworks_first2(Devicep, [], FirstComp) -> - {Devicep, [], FirstComp}; -vxworks_first2(Devicep, [$/|T], FirstComp) -> - {Devicep, [$/|T], FirstComp}; -vxworks_first2(Devicep, [$\\|T], FirstComp) -> - {Devicep, [$/|T], FirstComp}; -vxworks_first2(_Devicep, [$:|T], FirstComp)-> - {device, T, [$:|FirstComp]}; -vxworks_first2(Devicep, [H|T], FirstComp) when is_list(H) -> - vxworks_first2(Devicep, H++T, FirstComp); -vxworks_first2(Devicep, [H|T], FirstComp) -> - vxworks_first2(Devicep, T, [H|FirstComp]). - %% flatten(List) %% Flatten a list, also accepting atoms. --spec flatten(name()) -> string(). +-spec flatten(Filename) -> file:filename() when + Filename :: file:name(). +flatten(Bin) when is_binary(Bin) -> + Bin; flatten(List) -> do_flatten(List, []). @@ -785,3 +942,12 @@ do_flatten([], Tail) -> Tail; do_flatten(Atom, Tail) when is_atom(Atom) -> atom_to_list(Atom) ++ flatten(Tail). + +filename_string_to_binary(List) -> + case unicode:characters_to_binary(flatten(List),unicode,file:native_name_encoding()) of + {error,_,_} -> + erlang:error(badarg); + Bin when is_binary(Bin) -> + Bin + end. + diff --git a/lib/stdlib/src/gb_sets.erl b/lib/stdlib/src/gb_sets.erl index 086dc79b46..91d21d869c 100644 --- a/lib/stdlib/src/gb_sets.erl +++ b/lib/stdlib/src/gb_sets.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2009. All Rights Reserved. +%% Copyright Ericsson AB 2001-2011. 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 @@ -197,6 +197,7 @@ %% Some types. -type gb_set_node() :: 'nil' | {term(), _, _}. +-opaque iter() :: [gb_set_node()]. %% A declaration equivalent to the following is currently hard-coded %% in erl_types.erl @@ -205,38 +206,47 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec empty() -> gb_set(). +-spec empty() -> Set when + Set :: gb_set(). empty() -> {0, nil}. --spec new() -> gb_set(). +-spec new() -> Set when + Set :: gb_set(). new() -> empty(). --spec is_empty(gb_set()) -> boolean(). +-spec is_empty(Set) -> boolean() when + Set :: gb_set(). is_empty({0, nil}) -> true; is_empty(_) -> false. --spec size(gb_set()) -> non_neg_integer(). +-spec size(Set) -> non_neg_integer() when + Set :: gb_set(). size({Size, _}) -> Size. --spec singleton(term()) -> gb_set(). +-spec singleton(Element) -> gb_set() when + Element :: term(). singleton(Key) -> {1, {Key, nil, nil}}. --spec is_element(term(), gb_set()) -> boolean(). +-spec is_element(Element, Set) -> boolean() when + Element :: term(), + Set :: gb_set(). is_element(Key, S) -> is_member(Key, S). --spec is_member(term(), gb_set()) -> boolean(). +-spec is_member(Element, Set) -> boolean() when + Element :: term(), + Set :: gb_set(). is_member(Key, {_, T}) -> is_member_1(Key, T). @@ -250,7 +260,10 @@ is_member_1(_, {_, _, _}) -> is_member_1(_, nil) -> false. --spec insert(term(), gb_set()) -> gb_set(). +-spec insert(Element, Set1) -> Set2 when + Element :: term(), + Set1 :: gb_set(), + Set2 :: gb_set(). insert(Key, {S, T}) -> S1 = S + 1, @@ -306,7 +319,9 @@ count({_, Sm, Bi}) -> count(nil) -> {1, 0}. --spec balance(gb_set()) -> gb_set(). +-spec balance(Set1) -> Set2 when + Set1 :: gb_set(), + Set2 :: gb_set(). balance({S, T}) -> {S, balance(T, S)}. @@ -331,12 +346,18 @@ balance_list_1([Key | L], 1) -> balance_list_1(L, 0) -> {nil, L}. --spec add_element(term(), gb_set()) -> gb_set(). +-spec add_element(Element, Set1) -> Set2 when + Element :: term(), + Set1 :: gb_set(), + Set2 :: gb_set(). add_element(X, S) -> add(X, S). --spec add(term(), gb_set()) -> gb_set(). +-spec add(Element, Set1) -> Set2 when + Element :: term(), + Set1 :: gb_set(), + Set2 :: gb_set(). add(X, S) -> case is_member(X, S) of @@ -346,23 +367,33 @@ add(X, S) -> insert(X, S) end. --spec from_list([term()]) -> gb_set(). +-spec from_list(List) -> Set when + List :: [term()], + Set :: gb_set(). from_list(L) -> from_ordset(ordsets:from_list(L)). --spec from_ordset([term()]) -> gb_set(). +-spec from_ordset(List) -> Set when + List :: [term()], + Set :: gb_set(). from_ordset(L) -> S = length(L), {S, balance_list(L, S)}. --spec del_element(term(), gb_set()) -> gb_set(). +-spec del_element(Element, Set1) -> Set2 when + Element :: term(), + Set1 :: gb_set(), + Set2 :: gb_set(). del_element(Key, S) -> delete_any(Key, S). --spec delete_any(term(), gb_set()) -> gb_set(). +-spec delete_any(Element, Set1) -> Set2 when + Element :: term(), + Set1 :: gb_set(), + Set2 :: gb_set(). delete_any(Key, S) -> case is_member(Key, S) of @@ -372,7 +403,10 @@ delete_any(Key, S) -> S end. --spec delete(term(), gb_set()) -> gb_set(). +-spec delete(Element, Set1) -> Set2 when + Element :: term(), + Set1 :: gb_set(), + Set2 :: gb_set(). delete(Key, {S, T}) -> {S - 1, delete_1(Key, T)}. @@ -394,7 +428,10 @@ merge(Smaller, Larger) -> {Key, Larger1} = take_smallest1(Larger), {Key, Smaller, Larger1}. --spec take_smallest(gb_set()) -> {term(), gb_set()}. +-spec take_smallest(Set1) -> {Element, Set2} when + Set1 :: gb_set(), + Set2 :: gb_set(), + Element :: term(). take_smallest({S, T}) -> {Key, Larger} = take_smallest1(T), @@ -406,7 +443,8 @@ take_smallest1({Key, Smaller, Larger}) -> {Key1, Smaller1} = take_smallest1(Smaller), {Key1, {Key, Smaller1, Larger}}. --spec smallest(gb_set()) -> term(). +-spec smallest(Set) -> term() when + Set :: gb_set(). smallest({_, T}) -> smallest_1(T). @@ -416,7 +454,10 @@ smallest_1({Key, nil, _Larger}) -> smallest_1({_Key, Smaller, _Larger}) -> smallest_1(Smaller). --spec take_largest(gb_set()) -> {term(), gb_set()}. +-spec take_largest(Set1) -> {Element, Set2} when + Set1 :: gb_set(), + Set2 :: gb_set(), + Element :: term(). take_largest({S, T}) -> {Key, Smaller} = take_largest1(T), @@ -428,7 +469,8 @@ take_largest1({Key, Smaller, Larger}) -> {Key1, Larger1} = take_largest1(Larger), {Key1, {Key, Smaller, Larger1}}. --spec largest(gb_set()) -> term(). +-spec largest(Set) -> term() when + Set :: gb_set(). largest({_, T}) -> largest_1(T). @@ -438,7 +480,9 @@ largest_1({Key, _Smaller, nil}) -> largest_1({_Key, _Smaller, Larger}) -> largest_1(Larger). --spec to_list(gb_set()) -> [term()]. +-spec to_list(Set) -> List when + Set :: gb_set(), + List :: [term()]. to_list({_, T}) -> to_list(T, []). @@ -449,7 +493,9 @@ to_list({Key, Small, Big}, L) -> to_list(Small, [Key | to_list(Big, L)]); to_list(nil, L) -> L. --spec iterator(gb_set()) -> [term()]. +-spec iterator(Set) -> Iter when + Set :: gb_set(), + Iter :: iter(). iterator({_, T}) -> iterator(T, []). @@ -464,7 +510,10 @@ iterator({_, L, _} = T, As) -> iterator(nil, As) -> As. --spec next([term()]) -> {term(), [term()]} | 'none'. +-spec next(Iter1) -> {Element, Iter2} | 'none' when + Iter1 :: iter(), + Iter2 :: iter(), + Element :: term(). next([{X, _, T} | As]) -> {X, iterator(T, As)}; @@ -494,7 +543,10 @@ next([]) -> %% traversing the elements can be devised, but they all have higher %% overhead. --spec union(gb_set(), gb_set()) -> gb_set(). +-spec union(Set1, Set2) -> Set3 when + Set1 :: gb_set(), + Set2 :: gb_set(), + Set3 :: gb_set(). union({N1, T1}, {N2, T2}) when N2 < N1 -> union(to_list_1(T2), N2, T1, N1); @@ -596,7 +648,9 @@ balance_revlist_1([Key | L], 1) -> balance_revlist_1(L, 0) -> {nil, L}. --spec union([gb_set()]) -> gb_set(). +-spec union(SetList) -> Set when + SetList :: [gb_set(),...], + Set :: gb_set(). union([S | Ss]) -> union_list(S, Ss); @@ -609,7 +663,10 @@ union_list(S, []) -> S. %% The rest is modelled on the above. --spec intersection(gb_set(), gb_set()) -> gb_set(). +-spec intersection(Set1, Set2) -> Set3 when + Set1 :: gb_set(), + Set2 :: gb_set(), + Set3 :: gb_set(). intersection({N1, T1}, {N2, T2}) when N2 < N1 -> intersection(to_list_1(T2), N2, T1, N1); @@ -657,7 +714,9 @@ intersection_2([], _, As, S) -> intersection_2(_, [], As, S) -> {S, balance_revlist(As, S)}. --spec intersection([gb_set()]) -> gb_set(). +-spec intersection(SetList) -> Set when + SetList :: [gb_set(),...], + Set :: gb_set(). intersection([S | Ss]) -> intersection_list(S, Ss). @@ -666,7 +725,9 @@ intersection_list(S, [S1 | Ss]) -> intersection_list(intersection(S, S1), Ss); intersection_list(S, []) -> S. --spec is_disjoint(gb_set(), gb_set()) -> boolean(). +-spec is_disjoint(Set1, Set2) -> boolean() when + Set1 :: gb_set(), + Set2 :: gb_set(). is_disjoint({N1, T1}, {N2, T2}) when N1 < N2 -> is_disjoint_1(T1, T2); @@ -694,12 +755,18 @@ is_disjoint_1(_, nil) -> %% the sets. Therefore, we always build a new tree, and thus we need to %% traverse the whole element list of the left operand. --spec subtract(gb_set(), gb_set()) -> gb_set(). +-spec subtract(Set1, Set2) -> Set3 when + Set1 :: gb_set(), + Set2 :: gb_set(), + Set3 :: gb_set(). subtract(S1, S2) -> difference(S1, S2). --spec difference(gb_set(), gb_set()) -> gb_set(). +-spec difference(Set1, Set2) -> Set3 when + Set1 :: gb_set(), + Set2 :: gb_set(), + Set3 :: gb_set(). difference({N1, T1}, {N2, T2}) -> difference(to_list_1(T1), N1, T2, N2). @@ -747,7 +814,9 @@ difference_2(Xs, [], As, S) -> %% Subset testing is much the same thing as set difference, but %% without the construction of a new set. --spec is_subset(gb_set(), gb_set()) -> boolean(). +-spec is_subset(Set1, Set2) -> boolean() when + Set1 :: gb_set(), + Set2 :: gb_set(). is_subset({N1, T1}, {N2, T2}) -> is_subset(to_list_1(T1), N1, T2, N2). @@ -788,18 +857,28 @@ is_subset_2(_, []) -> %% For compatibility with `sets': --spec is_set(term()) -> boolean(). +-spec is_set(Term) -> boolean() when + Term :: term(). is_set({0, nil}) -> true; is_set({N, {_, _, _}}) when is_integer(N), N >= 0 -> true; is_set(_) -> false. --spec filter(fun((term()) -> boolean()), gb_set()) -> gb_set(). +-spec filter(Pred, Set1) -> Set2 when + Pred :: fun((E :: term()) -> boolean()), + Set1 :: gb_set(), + Set2 :: gb_set(). filter(F, S) -> from_ordset([X || X <- to_list(S), F(X)]). --spec fold(fun((term(), term()) -> term()), term(), gb_set()) -> term(). +-spec fold(Function, Acc0, Set) -> Acc1 when + Function :: fun((E :: term(), AccIn) -> AccOut), + Acc0 :: term(), + Acc1 :: term(), + AccIn :: term(), + AccOut :: term(), + Set :: gb_set(). fold(F, A, {_, T}) when is_function(F, 2) -> fold_1(F, A, T). diff --git a/lib/stdlib/src/gb_trees.erl b/lib/stdlib/src/gb_trees.erl index d37786a100..6ad861ff5b 100644 --- a/lib/stdlib/src/gb_trees.erl +++ b/lib/stdlib/src/gb_trees.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2009. All Rights Reserved. +%% Copyright Ericsson AB 2001-2011. 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 @@ -153,6 +153,7 @@ %% Some types. -type gb_tree_node() :: 'nil' | {_, _, _, _}. +-opaque iter() :: [gb_tree_node()]. %% A declaration equivalent to the following is currently hard-coded %% in erl_types.erl @@ -166,21 +167,26 @@ empty() -> {0, nil}. --spec is_empty(gb_tree()) -> boolean(). +-spec is_empty(Tree) -> boolean() when + Tree :: gb_tree(). is_empty({0, nil}) -> true; is_empty(_) -> false. --spec size(gb_tree()) -> non_neg_integer(). +-spec size(Tree) -> non_neg_integer() when + Tree :: gb_tree(). size({Size, _}) when is_integer(Size), Size >= 0 -> Size. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec lookup(term(), gb_tree()) -> 'none' | {'value', term()}. +-spec lookup(Key, Tree) -> 'none' | {'value', Val} when + Key :: term(), + Val :: term(), + Tree :: gb_tree(). lookup(Key, {_, T}) -> lookup_1(Key, T). @@ -205,7 +211,9 @@ lookup_1(_, nil) -> %% This is a specialized version of `lookup'. --spec is_defined(term(), gb_tree()) -> boolean(). +-spec is_defined(Key, Tree) -> boolean() when + Key :: term(), + Tree :: gb_tree(). is_defined(Key, {_, T}) -> is_defined_1(Key, T). @@ -223,7 +231,10 @@ is_defined_1(_, nil) -> %% This is a specialized version of `lookup'. --spec get(term(), gb_tree()) -> term(). +-spec get(Key, Tree) -> Val when + Key :: term(), + Tree :: gb_tree(), + Val :: term(). get(Key, {_, T}) -> get_1(Key, T). @@ -237,7 +248,11 @@ get_1(_, {_, Value, _, _}) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec update(term(), term(), gb_tree()) -> gb_tree(). +-spec update(Key, Val, Tree1) -> Tree2 when + Key :: term(), + Val :: term(), + Tree1 :: gb_tree(), + Tree2 :: gb_tree(). update(Key, Val, {S, T}) -> T1 = update_1(Key, Val, T), @@ -254,7 +269,11 @@ update_1(Key, Value, {_, _, Smaller, Bigger}) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec insert(term(), term(), gb_tree()) -> gb_tree(). +-spec insert(Key, Val, Tree1) -> Tree2 when + Key :: term(), + Val :: term(), + Tree1 :: gb_tree(), + Tree2 :: gb_tree(). insert(Key, Val, {S, T}) when is_integer(S) -> S1 = S+1, @@ -303,7 +322,11 @@ insert_1(Key, _, _, _) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec enter(term(), term(), gb_tree()) -> gb_tree(). +-spec enter(Key, Val, Tree1) -> Tree2 when + Key :: term(), + Val :: term(), + Tree1 :: gb_tree(), + Tree2 :: gb_tree(). enter(Key, Val, T) -> case is_defined(Key, T) of @@ -326,7 +349,9 @@ count(nil) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec balance(gb_tree()) -> gb_tree(). +-spec balance(Tree1) -> Tree2 when + Tree1 :: gb_tree(), + Tree2 :: gb_tree(). balance({S, T}) -> {S, balance(T, S)}. @@ -351,7 +376,9 @@ balance_list_1([{Key, Val} | L], 1) -> balance_list_1(L, 0) -> {nil, L}. --spec from_orddict([{_,_}]) -> gb_tree(). +-spec from_orddict(List) -> Tree when + List :: [{Key :: term(), Val :: term()}], + Tree :: gb_tree(). from_orddict(L) -> S = length(L), @@ -359,7 +386,10 @@ from_orddict(L) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec delete_any(term(), gb_tree()) -> gb_tree(). +-spec delete_any(Key, Tree1) -> Tree2 when + Key :: term(), + Tree1 :: gb_tree(), + Tree2 :: gb_tree(). delete_any(Key, T) -> case is_defined(Key, T) of @@ -371,7 +401,10 @@ delete_any(Key, T) -> %%% delete. Assumes that key is present. --spec delete(term(), gb_tree()) -> gb_tree(). +-spec delete(Key, Tree1) -> Tree2 when + Key :: term(), + Tree1 :: gb_tree(), + Tree2 :: gb_tree(). delete(Key, {S, T}) when is_integer(S), S >= 0 -> {S - 1, delete_1(Key, T)}. @@ -397,7 +430,11 @@ merge(Smaller, Larger) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec take_smallest(gb_tree()) -> {term(), term(), gb_tree()}. +-spec take_smallest(Tree1) -> {Key, Val, Tree2} when + Tree1 :: gb_tree(), + Tree2 :: gb_tree(), + Key :: term(), + Val :: term(). take_smallest({Size, Tree}) when is_integer(Size), Size >= 0 -> {Key, Value, Larger} = take_smallest1(Tree), @@ -409,7 +446,10 @@ take_smallest1({Key, Value, Smaller, Larger}) -> {Key1, Value1, Smaller1} = take_smallest1(Smaller), {Key1, Value1, {Key, Value, Smaller1, Larger}}. --spec smallest(gb_tree()) -> {term(), term()}. +-spec smallest(Tree) -> {Key, Val} when + Tree :: gb_tree(), + Key :: term(), + Val :: term(). smallest({_, Tree}) -> smallest_1(Tree). @@ -419,7 +459,11 @@ smallest_1({Key, Value, nil, _Larger}) -> smallest_1({_Key, _Value, Smaller, _Larger}) -> smallest_1(Smaller). --spec take_largest(gb_tree()) -> {term(), term(), gb_tree()}. +-spec take_largest(Tree1) -> {Key, Val, Tree2} when + Tree1 :: gb_tree(), + Tree2 :: gb_tree(), + Key :: term(), + Val :: term(). take_largest({Size, Tree}) when is_integer(Size), Size >= 0 -> {Key, Value, Smaller} = take_largest1(Tree), @@ -431,7 +475,10 @@ take_largest1({Key, Value, Smaller, Larger}) -> {Key1, Value1, Larger1} = take_largest1(Larger), {Key1, Value1, {Key, Value, Smaller, Larger1}}. --spec largest(gb_tree()) -> {term(), term()}. +-spec largest(Tree) -> {Key, Val} when + Tree :: gb_tree(), + Key :: term(), + Val :: term(). largest({_, Tree}) -> largest_1(Tree). @@ -443,7 +490,10 @@ largest_1({_Key, _Value, _Smaller, Larger}) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec to_list(gb_tree()) -> [{term(), term()}]. +-spec to_list(Tree) -> [{Key, Val}] when + Tree :: gb_tree(), + Key :: term(), + Val :: term(). to_list({_, T}) -> to_list(T, []). @@ -456,7 +506,9 @@ to_list(nil, L) -> L. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec keys(gb_tree()) -> [term()]. +-spec keys(Tree) -> [Key] when + Tree :: gb_tree(), + Key :: term(). keys({_, T}) -> keys(T, []). @@ -467,7 +519,9 @@ keys(nil, L) -> L. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec values(gb_tree()) -> [term()]. +-spec values(Tree) -> [Val] when + Tree :: gb_tree(), + Val :: term(). values({_, T}) -> values(T, []). @@ -478,7 +532,9 @@ values(nil, L) -> L. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec iterator(gb_tree()) -> [gb_tree_node()]. +-spec iterator(Tree) -> Iter when + Tree :: gb_tree(), + Iter :: iter(). iterator({_, T}) -> iterator_1(T). @@ -496,7 +552,11 @@ iterator({_, _, L, _} = T, As) -> iterator(nil, As) -> As. --spec next([gb_tree_node()]) -> 'none' | {term(), term(), [gb_tree_node()]}. +-spec next(Iter1) -> 'none' | {Key, Val, Iter2} when + Iter1 :: iter(), + Iter2 :: iter(), + Key :: term(), + Val :: term(). next([{X, V, _, T} | As]) -> {X, V, iterator(T, As)}; @@ -505,7 +565,10 @@ next([]) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec map(fun((term(), term()) -> term()), gb_tree()) -> gb_tree(). +-spec map(Function, Tree1) -> Tree2 when + Function :: fun((K :: term(), V1 :: term()) -> V2 :: term()), + Tree1 :: gb_tree(), + Tree2 :: gb_tree(). map(F, {Size, Tree}) when is_function(F, 2) -> {Size, map_1(F, Tree)}. diff --git a/lib/stdlib/src/gen.erl b/lib/stdlib/src/gen.erl index 5aab547644..574146b1cd 100644 --- a/lib/stdlib/src/gen.erl +++ b/lib/stdlib/src/gen.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1996-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(gen). @@ -29,6 +29,8 @@ -export([init_it/6, init_it/7]). +-export([format_status_header/2]). + -define(default_timeout, 5000). %%----------------------------------------------------------------- @@ -212,7 +214,22 @@ do_call(Process, Label, Request, Timeout) -> catch erlang:send(Process, {Label, {self(), Mref}, Request}, [noconnect]), - wait_resp_mon(Node, Mref, Timeout) + receive + {Mref, Reply} -> + erlang:demonitor(Mref, [flush]), + {ok, Reply}; + {'DOWN', Mref, _, _, noconnection} -> + exit({nodedown, Node}); + {'DOWN', Mref, _, _, Reason} -> + exit(Reason) + after Timeout -> + erlang:demonitor(Mref), + receive + {'DOWN', Mref, _, _, _} -> true + after 0 -> true + end, + exit(timeout) + end catch error:_ -> %% Node (C/Java?) is not supporting the monitor. @@ -233,24 +250,6 @@ do_call(Process, Label, Request, Timeout) -> end end. -wait_resp_mon(Node, Mref, Timeout) -> - receive - {Mref, Reply} -> - erlang:demonitor(Mref, [flush]), - {ok, Reply}; - {'DOWN', Mref, _, _, noconnection} -> - exit({nodedown, Node}); - {'DOWN', Mref, _, _, Reason} -> - exit(Reason) - after Timeout -> - erlang:demonitor(Mref), - receive - {'DOWN', Mref, _, _, _} -> true - after 0 -> true - end, - exit(timeout) - end. - wait_resp(Node, Tag, Timeout) -> receive {Tag, Reply} -> @@ -318,3 +317,10 @@ debug_options(Opts) -> {ok, Options} -> sys:debug_options(Options); _ -> [] end. + +format_status_header(TagLine, Pid) when is_pid(Pid) -> + lists:concat([TagLine, " ", pid_to_list(Pid)]); +format_status_header(TagLine, RegName) when is_atom(RegName) -> + lists:concat([TagLine, " ", RegName]); +format_status_header(TagLine, Name) -> + {TagLine, Name}. diff --git a/lib/stdlib/src/gen_event.erl b/lib/stdlib/src/gen_event.erl index 1b30aaf5eb..1c4a73680b 100644 --- a/lib/stdlib/src/gen_event.erl +++ b/lib/stdlib/src/gen_event.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1996-2011. 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(gen_event). @@ -42,9 +42,11 @@ system_continue/3, system_terminate/4, system_code_change/4, - print_event/3, format_status/2]). +-export_type([handler/0, handler_args/0, add_handler_ret/0, + del_handler_ret/0]). + -import(error_logger, [error_msg/2]). -define(reply(X), From ! {element(2,Tag), X}). @@ -114,7 +116,11 @@ behaviour_info(_Other) -> %%--------------------------------------------------------------------------- --type handler() :: atom() | {atom(), term()}. +-type handler() :: atom() | {atom(), term()}. +-type handler_args() :: term(). +-type add_handler_ret() :: ok | term() | {'EXIT',term()}. +-type del_handler_ret() :: ok | term() | {'EXIT',term()}. + -type emgr_name() :: {'local', atom()} | {'global', atom()}. -type emgr_ref() :: atom() | {atom(), atom()} | {'global', atom()} | pid(). -type start_ret() :: {'ok', pid()} | {'error', term()}. @@ -239,7 +245,7 @@ fetch_msg(Parent, ServerName, MSL, Debug, Hib) -> Msg when Debug =:= [] -> handle_msg(Msg, Parent, ServerName, MSL, []); Msg -> - Debug1 = sys:handle_debug(Debug, {?MODULE, print_event}, + Debug1 = sys:handle_debug(Debug, fun print_event/3, ServerName, {in, Msg}), handle_msg(Msg, Parent, ServerName, MSL, Debug1) end. @@ -678,12 +684,23 @@ report_error(Handler, Reason, State, LastIn, SName) -> _ -> Reason end, + Mod = Handler#handler.module, + FmtState = case erlang:function_exported(Mod, format_status, 2) of + true -> + Args = [get(), State], + case catch Mod:format_status(terminate, Args) of + {'EXIT', _} -> State; + Else -> Else + end; + _ -> + State + end, error_msg("** gen_event handler ~p crashed.~n" "** Was installed in ~p~n" "** Last event was: ~p~n" "** When handler state == ~p~n" "** Reason == ~p~n", - [handler(Handler),SName,LastIn,State,Reason1]). + [handler(Handler),SName,LastIn,FmtState,Reason1]). handler(Handler) when not Handler#handler.id -> Handler#handler.module; @@ -712,10 +729,21 @@ get_modules(MSL) -> %%----------------------------------------------------------------- %% Status information %%----------------------------------------------------------------- -format_status(_Opt, StatusData) -> - [_PDict, SysState, Parent, _Debug, [ServerName, MSL, _Hib]] = StatusData, - Header = lists:concat(["Status for event handler ", ServerName]), +format_status(Opt, StatusData) -> + [PDict, SysState, Parent, _Debug, [ServerName, MSL, _Hib]] = StatusData, + Header = gen:format_status_header("Status for event handler", + ServerName), + FmtMSL = [case erlang:function_exported(Mod, format_status, 2) of + true -> + Args = [PDict, State], + case catch Mod:format_status(Opt, Args) of + {'EXIT', _} -> MSL; + Else -> MS#handler{state = Else} + end; + _ -> + MS + end || #handler{module = Mod, state = State} = MS <- MSL], [{header, Header}, {data, [{"Status", SysState}, {"Parent", Parent}]}, - {items, {"Installed handlers", MSL}}]. + {items, {"Installed handlers", FmtMSL}}]. diff --git a/lib/stdlib/src/gen_fsm.erl b/lib/stdlib/src/gen_fsm.erl index ba0275ae2b..f2f1365d3d 100644 --- a/lib/stdlib/src/gen_fsm.erl +++ b/lib/stdlib/src/gen_fsm.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1996-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(gen_fsm). @@ -116,7 +116,7 @@ -export([behaviour_info/1]). %% Internal exports --export([init_it/6, print_event/3, +-export([init_it/6, system_continue/3, system_terminate/4, system_code_change/4, @@ -376,7 +376,7 @@ decode_msg(Msg,Parent, Name, StateName, StateData, Mod, Time, Debug, Hib) -> _Msg when Debug =:= [] -> handle_msg(Msg, Parent, Name, StateName, StateData, Mod, Time); _Msg -> - Debug1 = sys:handle_debug(Debug, {?MODULE, print_event}, + Debug1 = sys:handle_debug(Debug, fun print_event/3, {Name, StateName}, {in, Msg}), handle_msg(Msg, Parent, Name, StateName, StateData, Mod, Time, Debug1) @@ -466,11 +466,11 @@ handle_msg(Msg, Parent, Name, StateName, StateData, Mod, _Time, Debug) -> From = from(Msg), case catch dispatch(Msg, Mod, StateName, StateData) of {next_state, NStateName, NStateData} -> - Debug1 = sys:handle_debug(Debug, {?MODULE, print_event}, + Debug1 = sys:handle_debug(Debug, fun print_event/3, {Name, NStateName}, return), loop(Parent, Name, NStateName, NStateData, Mod, infinity, Debug1); {next_state, NStateName, NStateData, Time1} -> - Debug1 = sys:handle_debug(Debug, {?MODULE, print_event}, + Debug1 = sys:handle_debug(Debug, fun print_event/3, {Name, NStateName}, return), loop(Parent, Name, NStateName, NStateData, Mod, Time1, Debug1); {reply, Reply, NStateName, NStateData} when From =/= undefined -> @@ -519,7 +519,7 @@ reply({To, Tag}, Reply) -> reply(Name, {To, Tag}, Reply, Debug, StateName) -> reply({To, Tag}, Reply), - sys:handle_debug(Debug, {?MODULE, print_event}, Name, + sys:handle_debug(Debug, fun print_event/3, Name, {out, Reply, To, StateName}). %%% --------------------------------------------------- @@ -542,7 +542,18 @@ terminate(Reason, Name, Msg, Mod, StateName, StateData, Debug) -> {shutdown,_}=Shutdown -> exit(Shutdown); _ -> - error_info(Reason, Name, Msg, StateName, StateData, Debug), + FmtStateData = + case erlang:function_exported(Mod, format_status, 2) of + true -> + Args = [get(), StateData], + case catch Mod:format_status(terminate, Args) of + {'EXIT', _} -> StateData; + Else -> Else + end; + _ -> + StateData + end, + error_info(Reason,Name,Msg,StateName,FmtStateData,Debug), exit(Reason) end end. @@ -603,22 +614,20 @@ get_msg(Msg) -> Msg. format_status(Opt, StatusData) -> [PDict, SysState, Parent, Debug, [Name, StateName, StateData, Mod, _Time]] = StatusData, - NameTag = if is_pid(Name) -> - pid_to_list(Name); - is_atom(Name) -> - Name - end, - Header = lists:concat(["Status for state machine ", NameTag]), + Header = gen:format_status_header("Status for state machine", + Name), Log = sys:get_debug(log, Debug, []), - Specfic = + DefaultStatus = [{data, [{"StateData", StateData}]}], + Specfic = case erlang:function_exported(Mod, format_status, 2) of true -> case catch Mod:format_status(Opt,[PDict,StateData]) of - {'EXIT', _} -> [{data, [{"StateData", StateData}]}]; - Else -> Else + {'EXIT', _} -> DefaultStatus; + StatusList when is_list(StatusList) -> StatusList; + Else -> [Else] end; _ -> - [{data, [{"StateData", StateData}]}] + DefaultStatus end, [{header, Header}, {data, [{"Status", SysState}, diff --git a/lib/stdlib/src/gen_server.erl b/lib/stdlib/src/gen_server.erl index f1a9a31c63..09d94a9c40 100644 --- a/lib/stdlib/src/gen_server.erl +++ b/lib/stdlib/src/gen_server.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1996-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(gen_server). @@ -103,7 +103,7 @@ format_status/2]). %% Internal exports --export([init_it/6, print_event/3]). +-export([init_it/6]). -import(error_logger, [format/2]). @@ -353,7 +353,7 @@ decode_msg(Msg, Parent, Name, State, Mod, Time, Debug, Hib) -> _Msg when Debug =:= [] -> handle_msg(Msg, Parent, Name, State, Mod); _Msg -> - Debug1 = sys:handle_debug(Debug, {?MODULE, print_event}, + Debug1 = sys:handle_debug(Debug, fun print_event/3, Name, {in, Msg}), handle_msg(Msg, Parent, Name, State, Mod, Debug1) end. @@ -589,11 +589,11 @@ handle_msg({'$gen_call', From, Msg}, Parent, Name, State, Mod, Debug) -> Debug1 = reply(Name, From, Reply, NState, Debug), loop(Parent, Name, NState, Mod, Time1, Debug1); {noreply, NState} -> - Debug1 = sys:handle_debug(Debug, {?MODULE, print_event}, Name, + Debug1 = sys:handle_debug(Debug, fun print_event/3, Name, {noreply, NState}), loop(Parent, Name, NState, Mod, infinity, Debug1); {noreply, NState, Time1} -> - Debug1 = sys:handle_debug(Debug, {?MODULE, print_event}, Name, + Debug1 = sys:handle_debug(Debug, fun print_event/3, Name, {noreply, NState}), loop(Parent, Name, NState, Mod, Time1, Debug1); {stop, Reason, Reply, NState} -> @@ -625,11 +625,11 @@ handle_common_reply(Reply, Parent, Name, Msg, Mod, State) -> handle_common_reply(Reply, Parent, Name, Msg, Mod, State, Debug) -> case Reply of {noreply, NState} -> - Debug1 = sys:handle_debug(Debug, {?MODULE, print_event}, Name, + Debug1 = sys:handle_debug(Debug, fun print_event/3, Name, {noreply, NState}), loop(Parent, Name, NState, Mod, infinity, Debug1); {noreply, NState, Time1} -> - Debug1 = sys:handle_debug(Debug, {?MODULE, print_event}, Name, + Debug1 = sys:handle_debug(Debug, fun print_event/3, Name, {noreply, NState}), loop(Parent, Name, NState, Mod, Time1, Debug1); {stop, Reason, NState} -> @@ -642,7 +642,7 @@ handle_common_reply(Reply, Parent, Name, Msg, Mod, State, Debug) -> reply(Name, {To, Tag}, Reply, State, Debug) -> reply({To, Tag}, Reply), - sys:handle_debug(Debug, {?MODULE, print_event}, Name, + sys:handle_debug(Debug, fun print_event/3, Name, {out, Reply, To, State} ). @@ -705,7 +705,18 @@ terminate(Reason, Name, Msg, Mod, State, Debug) -> {shutdown,_}=Shutdown -> exit(Shutdown); _ -> - error_info(Reason, Name, Msg, State, Debug), + FmtState = + case erlang:function_exported(Mod, format_status, 2) of + true -> + Args = [get(), State], + case catch Mod:format_status(terminate, Args) of + {'EXIT', _} -> State; + Else -> Else + end; + _ -> + State + end, + error_info(Reason, Name, Msg, FmtState, Debug), exit(Reason) end end. @@ -829,22 +840,20 @@ name_to_pid(Name) -> %%----------------------------------------------------------------- format_status(Opt, StatusData) -> [PDict, SysState, Parent, Debug, [Name, State, Mod, _Time]] = StatusData, - NameTag = if is_pid(Name) -> - pid_to_list(Name); - is_atom(Name) -> - Name - end, - Header = lists:concat(["Status for generic server ", NameTag]), + Header = gen:format_status_header("Status for generic server", + Name), Log = sys:get_debug(log, Debug, []), - Specfic = + DefaultStatus = [{data, [{"State", State}]}], + Specfic = case erlang:function_exported(Mod, format_status, 2) of true -> case catch Mod:format_status(Opt, [PDict, State]) of - {'EXIT', _} -> [{data, [{"State", State}]}]; - Else -> Else + {'EXIT', _} -> DefaultStatus; + StatusList when is_list(StatusList) -> StatusList; + Else -> [Else] end; _ -> - [{data, [{"State", State}]}] + DefaultStatus end, [{header, Header}, {data, [{"Status", SysState}, diff --git a/lib/stdlib/src/io.erl b/lib/stdlib/src/io.erl index 1f8076e864..9f65bbfa3a 100644 --- a/lib/stdlib/src/io.erl +++ b/lib/stdlib/src/io.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1996-2011. 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(io). @@ -32,12 +32,16 @@ parse_erl_form/1,parse_erl_form/2,parse_erl_form/3]). -export([request/1,request/2,requests/1,requests/2]). +-export_type([device/0, format/0]). %%------------------------------------------------------------------------- -type device() :: atom() | pid(). -type prompt() :: atom() | string(). +-type error_description() :: term(). % Whatever the io-server sends. +-type request_error() :: {'error',error_description()}. + %% XXX: Some uses of line() in this file may need to read erl_scan:location() -type line() :: pos_integer(). @@ -52,37 +56,26 @@ to_tuple(T) when is_tuple(T) -> T; to_tuple(T) -> {T}. -%% Problem: the variables Other, Name and Args may collide with surrounding -%% ones. -%% Give extra args to macro, being the variables to use. --define(O_REQUEST(Io, Request), - case request(Io, Request) of - {error, Reason} -> - [Name | Args] = tuple_to_list(to_tuple(Request)), - erlang:error(conv_reason(Name, Reason), [Name, Io | Args]); - Other -> - Other - end). - o_request(Io, Request, Func) -> case request(Io, Request) of {error, Reason} -> [_Name | Args] = tuple_to_list(to_tuple(Request)), - {'EXIT',{undef,[_Current|Mfas]}} = (catch erlang:error(undef)), - MFA = {io, Func, [Io | Args]}, - exit({conv_reason(Func, Reason),[MFA|Mfas]}); -% erlang:error(conv_reason(Name, Reason), [Name, Io | Args]); + {'EXIT',{get_stacktrace,[_Current|Mfas]}} = (catch erlang:error(get_stacktrace)), + erlang:raise(error, conv_reason(Func, Reason), [{io, Func, [Io | Args]}|Mfas]); Other -> Other end. %% Put chars takes mixed *unicode* list from R13 onwards. --spec put_chars(iodata()) -> 'ok'. +-spec put_chars(CharData) -> 'ok' when + CharData :: unicode:chardata(). put_chars(Chars) -> put_chars(default_output(), Chars). --spec put_chars(device(), iodata()) -> 'ok'. +-spec put_chars(IoDevice, IoData) -> 'ok' when + IoDevice :: device(), + IoData :: unicode:chardata(). put_chars(Io, Chars) -> o_request(Io, {put_chars,unicode,Chars}, put_chars). @@ -92,7 +85,8 @@ put_chars(Io, Chars) -> nl() -> nl(default_output()). --spec nl(device()) -> 'ok'. +-spec nl(IoDevice) -> 'ok' when + IoDevice :: device(). nl(Io) -> % o_request(Io, {put_chars,io_lib:nl()}). @@ -103,7 +97,8 @@ nl(Io) -> columns() -> columns(default_output()). --spec columns(device()) -> {'ok', pos_integer()} | {'error', 'enotsup'}. +-spec columns(IoDevice) -> {'ok', pos_integer()} | {'error', 'enotsup'} when + IoDevice :: device(). columns(Io) -> case request(Io, {get_geometry,columns}) of @@ -118,7 +113,8 @@ columns(Io) -> rows() -> rows(default_output()). --spec rows(device()) -> {'ok', pos_integer()} | {'error', 'enotsup'}. +-spec rows(IoDevice) -> {'ok', pos_integer()} | {'error', 'enotsup'} when + IoDevice :: device(). rows(Io) -> case request(Io,{get_geometry,rows}) of @@ -128,22 +124,36 @@ rows(Io) -> {error,enotsup} end. --spec get_chars(prompt(), non_neg_integer()) -> iodata() | 'eof'. +-spec get_chars(Prompt, Count) -> Data | 'eof' when + Prompt :: prompt(), + Count :: non_neg_integer(), + Data :: [unicode:unicode_char()] | unicode:unicode_binary(). get_chars(Prompt, N) -> get_chars(default_input(), Prompt, N). --spec get_chars(device(), prompt(), non_neg_integer()) -> iodata() | 'eof'. +-spec get_chars(IoDevice, Prompt, Count) -> Data | 'eof' | {error, Reason} when + IoDevice :: device(), + Prompt :: prompt(), + Count :: non_neg_integer(), + Reason :: term(), + Data :: [unicode:unicode_char()] | 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()) -> iodata() | 'eof' | {'error', term()}. +-spec get_line(Prompt) -> Data | 'eof' | {'error', Reason} when + Prompt :: prompt(), + Reason :: term(), + Data :: [unicode:unicode_char()] | unicode:unicode_binary(). get_line(Prompt) -> get_line(default_input(), Prompt). --spec get_line(device(), prompt()) -> iodata() | 'eof' | {'error', term()}. +-spec get_line(IoDevice, Prompt) -> Data | 'eof' | {'error', term()} when + IoDevice :: device(), + Prompt :: prompt(), + Data :: [unicode:unicode_char()] | unicode:unicode_binary(). get_line(Io, Prompt) -> request(Io, {get_line,unicode,Prompt}). @@ -167,46 +177,62 @@ get_password(Io) -> getopts() -> getopts(default_input()). --spec getopts(device()) -> [opt_pair()]. +-spec getopts(IoDevice) -> [opt_pair()] when + IoDevice :: device(). getopts(Io) -> request(Io, getopts). -type setopt() :: 'binary' | 'list' | opt_pair(). --spec setopts([setopt()]) -> 'ok' | {'error', term()}. +-spec setopts(Opts) -> 'ok' | {'error', Reason} when + Opts :: [setopt()], + Reason :: term(). setopts(Opts) -> setopts(default_input(), Opts). --spec setopts(device(), [setopt()]) -> 'ok' | {'error', term()}. +-spec setopts(IoDevice, Opts) -> 'ok' | {'error', Reason} when + IoDevice :: device(), + Opts :: [setopt()], + Reason :: term(). setopts(Io, Opts) -> request(Io, {setopts, Opts}). %% Writing and reading Erlang terms. --spec write(term()) -> 'ok'. +-spec write(Term) -> 'ok' when + Term :: term(). write(Term) -> write(default_output(), Term). --spec write(device(), term()) -> 'ok'. +-spec write(IoDevice, Term) -> 'ok' when + IoDevice :: device(), + Term :: term(). write(Io, Term) -> o_request(Io, {write,Term}, write). --spec read(prompt()) -> - {'ok', term()} | 'eof' | {'error', erl_scan:error_info()}. +-spec read(Prompt) -> Result when + Prompt :: prompt(), + Result :: {'ok', Term :: term()} + | 'eof' + | {'error', ErrorInfo :: erl_scan: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). --spec read(device(), prompt()) -> - {'ok', term()} | 'eof' | {'error', erl_scan:error_info()}. +-spec read(IoDevice, Prompt) -> Result when + IoDevice :: device(), + Prompt :: prompt(), + Result :: {'ok', Term :: term()} + | 'eof' + | {'error', ErrorInfo :: erl_scan:error_info()}. read(Io, Prompt) -> case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[1]}) of @@ -222,9 +248,13 @@ read(Io, Prompt) -> Other end. --spec read(device(), prompt(), line()) -> - {'ok', term(), line()} | {'eof', line()} | - {'error', erl_scan:error_info(), line()}. +-spec read(IoDevice, Prompt, StartLine) -> 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 @@ -250,28 +280,40 @@ conv_reason(_, _Reason) -> badarg. -type format() :: atom() | string() | binary(). --spec fwrite(format()) -> 'ok'. +-spec fwrite(Format) -> 'ok' when + Format :: format(). fwrite(Format) -> format(Format). --spec fwrite(format(), [term()]) -> 'ok'. +-spec fwrite(Format, Data) -> 'ok' when + Format :: format(), + Data :: [term()]. fwrite(Format, Args) -> format(Format, Args). --spec fwrite(device(), format(), [term()]) -> 'ok'. +-spec fwrite(IoDevice, Format, Data) -> 'ok' when + IoDevice :: device(), + Format :: format(), + Data :: [term()]. fwrite(Io, Format, Args) -> format(Io, Format, Args). --spec fread(prompt(), format()) -> {'ok', [term()]} | 'eof' | {'error',term()}. +-spec fread(Prompt, Format) -> Result when + Prompt :: prompt(), + Format :: format(), + Result :: {'ok', Terms :: [term()]} | 'eof' | {'error', What :: term()}. fread(Prompt, Format) -> fread(default_input(), Prompt, Format). --spec fread(device(), prompt(), format()) -> - {'ok', [term()]} | 'eof' | {'error',term()}. +-spec fread(IoDevice, Prompt, Format) -> Result when + IoDevice :: device(), + Prompt :: prompt(), + Format :: format(), + Result :: {'ok', Terms :: [term()]} | 'eof' | {'error', What :: term()}. fread(Io, Prompt, Format) -> case request(Io, {fread,Prompt,Format}) of @@ -281,72 +323,104 @@ fread(Io, Prompt, Format) -> Other end. --spec format(format()) -> 'ok'. +-spec format(Format) -> 'ok' when + Format :: format(). format(Format) -> format(Format, []). --spec format(format(), [term()]) -> 'ok'. +-spec format(Format, Data) -> 'ok' when + Format :: format(), + Data :: [term()]. format(Format, Args) -> format(default_output(), Format, Args). --spec format(device(), format(), [term()]) -> 'ok'. +-spec format(IoDevice, Format, Data) -> 'ok' when + IoDevice :: device(), + Format :: format(), + Data :: [term()]. format(Io, Format, Args) -> o_request(Io, {format,Format,Args}, format). %% Scanning Erlang code. --spec scan_erl_exprs(prompt()) -> erl_scan:tokens_result(). +-spec scan_erl_exprs(Prompt) -> Result when + Prompt :: prompt(), + Result :: erl_scan:tokens_result() | request_error(). scan_erl_exprs(Prompt) -> scan_erl_exprs(default_input(), Prompt, 1). --spec scan_erl_exprs(device(), prompt()) -> erl_scan:tokens_result(). +-spec scan_erl_exprs(Device, Prompt) -> Result when + Device :: device(), + Prompt :: prompt(), + Result :: erl_scan:tokens_result() | request_error(). scan_erl_exprs(Io, Prompt) -> scan_erl_exprs(Io, Prompt, 1). --spec scan_erl_exprs(device(), prompt(), line()) -> erl_scan:tokens_result(). +-spec scan_erl_exprs(Device, Prompt, StartLine) -> Result when + Device :: device(), + Prompt :: prompt(), + StartLine :: line(), + Result :: erl_scan:tokens_result() | request_error(). scan_erl_exprs(Io, Prompt, Pos0) -> request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0]}). --spec scan_erl_form(prompt()) -> erl_scan:tokens_result(). +-spec scan_erl_form(Prompt) -> Result when + Prompt :: prompt(), + Result :: erl_scan:tokens_result() | request_error(). scan_erl_form(Prompt) -> scan_erl_form(default_input(), Prompt, 1). --spec scan_erl_form(device(), prompt()) -> erl_scan:tokens_result(). +-spec scan_erl_form(IoDevice, Prompt) -> Result when + IoDevice :: device(), + Prompt :: prompt(), + Result :: erl_scan:tokens_result() | request_error(). scan_erl_form(Io, Prompt) -> scan_erl_form(Io, Prompt, 1). --spec scan_erl_form(device(), prompt(), line()) -> erl_scan:tokens_result(). +-spec scan_erl_form(IoDevice, Prompt, StartLine) -> Result when + IoDevice :: device(), + Prompt :: prompt(), + StartLine :: line(), + Result :: erl_scan:tokens_result() | request_error(). scan_erl_form(Io, Prompt, Pos0) -> request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0]}). %% Parsing Erlang code. --type erl_parse_expr_list() :: [_]. %% XXX: should be imported from erl_parse - --type parse_ret() :: {'ok', erl_parse_expr_list(), line()} - | {'eof', line()} - | {'error', erl_scan:error_info(), line()}. +-type parse_ret() :: {'ok', ExprList :: erl_parse:abstract_expr(), EndLine :: line()} + | {'eof', EndLine :: line()} + | {'error', ErrorInfo :: erl_scan:error_info(), ErrorLine :: line()} + | request_error(). --spec parse_erl_exprs(prompt()) -> parse_ret(). +-spec parse_erl_exprs(Prompt) -> Result when + Prompt :: prompt(), + Result :: parse_ret(). parse_erl_exprs(Prompt) -> parse_erl_exprs(default_input(), Prompt, 1). --spec parse_erl_exprs(device(), prompt()) -> parse_ret(). +-spec parse_erl_exprs(IoDevice, Prompt) -> Result when + IoDevice :: device(), + Prompt :: prompt(), + Result :: parse_ret(). parse_erl_exprs(Io, Prompt) -> parse_erl_exprs(Io, Prompt, 1). --spec parse_erl_exprs(device(), prompt(), line()) -> parse_ret(). +-spec parse_erl_exprs(IoDevice, Prompt, StartLine) -> Result when + IoDevice :: device(), + Prompt :: prompt(), + StartLine :: line(), + Result :: parse_ret(). parse_erl_exprs(Io, Prompt, Pos0) -> case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0]}) of @@ -359,23 +433,31 @@ parse_erl_exprs(Io, Prompt, Pos0) -> Other end. --type erl_parse_absform() :: _. %% XXX: should be imported from erl_parse - --type parse_form_ret() :: {'ok', erl_parse_absform(), line()} - | {'eof', line()} - | {'error', erl_scan:error_info(), line()}. +-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(). --spec parse_erl_form(prompt()) -> parse_form_ret(). +-spec parse_erl_form(Prompt) -> Result when + Prompt :: prompt(), + Result :: parse_form_ret(). parse_erl_form(Prompt) -> parse_erl_form(default_input(), Prompt, 1). --spec parse_erl_form(device(), prompt()) -> parse_form_ret(). +-spec parse_erl_form(IoDevice, Prompt) -> Result when + IoDevice :: device(), + Prompt :: prompt(), + Result :: parse_form_ret(). parse_erl_form(Io, Prompt) -> parse_erl_form(Io, Prompt, 1). --spec parse_erl_form(device(), prompt(), line()) -> parse_form_ret(). +-spec parse_erl_form(IoDevice, Prompt, StartLine) -> Result when + IoDevice :: device(), + Prompt :: prompt(), + StartLine :: line(), + Result :: parse_form_ret(). parse_erl_form(Io, Prompt, Pos0) -> case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0]}) of diff --git a/lib/stdlib/src/io_lib.erl b/lib/stdlib/src/io_lib.erl index 26f6ec8931..54c7283abf 100644 --- a/lib/stdlib/src/io_lib.erl +++ b/lib/stdlib/src/io_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. 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 @@ -75,33 +75,57 @@ collect_line/2, collect_line/3, collect_line/4, get_until/3, get_until/4]). +-export_type([chars/0, continuation/0]). + %%---------------------------------------------------------------------- - %% XXX: overapproximates a deep list of (unicode) characters --type chars() :: [_]. +-type chars() :: [char() | chars()]. -type depth() :: -1 | non_neg_integer(). +-opaque continuation() :: {_, _, _, _}. % XXX: refine + %%---------------------------------------------------------------------- %% Interface calls to sub-modules. --spec fwrite(io:format(), [term()]) -> chars(). +-spec fwrite(Format, Data) -> chars() | UnicodeList when + Format :: io:format(), + Data :: [term()], + UnicodeList :: [unicode:unicode_char()], + Data :: [term()]. fwrite(Format, Args) -> format(Format, Args). --spec fread(string(), string()) -> io_lib_fread:fread_2_ret(). +-spec fread(Format, String) -> Result when + Format :: string(), + String :: string(), + Result :: {'ok', InputList :: chars(), LeftOverChars :: string()} + | {'more', RestFormat :: string(), + Nchars :: non_neg_integer(), + InputStack :: chars()} + | {'error', What :: term()}. fread(Chars, Format) -> io_lib_fread:fread(Chars, Format). --spec fread(io_lib_fread:continuation(), string(), string()) -> - io_lib_fread:fread_3_ret(). +-spec fread(Continuation, String, Format) -> Return when + Continuation :: continuation() | [], + String :: string(), + Format :: string(), + Return :: {'more', Continuation1 :: continuation()} + | {'done', Result, LeftOverChars :: string()}, + Result :: {'ok', InputList :: chars()} + | 'eof' + | {'error', What :: term()}. fread(Cont, Chars, Format) -> io_lib_fread:fread(Cont, Chars, Format). --spec format(io:format(), [term()]) -> chars(). +-spec format(Format, Data) -> chars() | UnicodeList when + Format :: io:format(), + Data :: [term()], + UnicodeList :: [unicode:unicode_char()]. format(Format, Args) -> case catch io_lib_format:fwrite(Format, Args) of @@ -111,17 +135,24 @@ format(Format, Args) -> Other end. --spec print(term()) -> chars(). +-spec print(Term) -> chars() when + Term :: term(). print(Term) -> io_lib_pretty:print(Term). --spec print(term(), non_neg_integer(), non_neg_integer(), depth()) -> chars(). +-spec print(Term, Column, LineLength, Depth) -> chars() when + Term :: term(), + Column :: non_neg_integer(), + LineLength :: non_neg_integer(), + Depth :: depth(). print(Term, Column, LineLength, Depth) -> io_lib_pretty:print(Term, Column, LineLength, Depth). --spec indentation(string(), integer()) -> integer(). +-spec indentation(String, StartIndent) -> integer() when + String :: string(), + StartIndent :: integer(). indentation(Chars, Current) -> io_lib_format:indentation(Chars, Current). @@ -156,7 +187,8 @@ format_prompt(Format, Args) -> %% Return a (non-flattened) list of characters giving a printed %% representation of the term. write/3 is for backward compatibility. --spec write(term()) -> chars(). +-spec write(Term) -> chars() when + Term :: term(). write(Term) -> write(Term, -1). @@ -167,7 +199,9 @@ write(Term, D, true) -> write(Term, D, false) -> write(Term, D). --spec write(term(), depth()) -> chars(). +-spec write(Term, Depth) -> chars() when + Term :: term(), + Depth :: depth(). write(_Term, 0) -> "..."; write(Term, _D) when is_integer(Term) -> integer_to_list(Term); @@ -232,7 +266,8 @@ write_binary_body(B, _D) -> %% write_atom(Atom) -> [Char] %% Generate the list of characters needed to print an atom. --spec write_atom(atom()) -> chars(). +-spec write_atom(Atom) -> chars() when + Atom :: atom(). write_atom(Atom) -> Chars = atom_to_list(Atom), @@ -281,7 +316,8 @@ name_char(_) -> false. %% write_string([Char]) -> [Char] %% Generate the list of characters needed to print a string. --spec write_string(string()) -> chars(). +-spec write_string(String) -> chars() when + String :: string(). write_string(S) -> write_string(S, $"). %" @@ -328,7 +364,8 @@ string_char(_,C, _, Tail) when C < $\240-> %Other control characters. %% Generate the list of characters needed to print a character constant. %% Must special case SPACE, $\s, here. --spec write_char(char()) -> chars(). +-spec write_char(Char) -> chars() when + Char :: char(). write_char($\s) -> "$\\s"; %Must special case this. write_char(C) when is_integer(C), C >= $\000, C =< $\377 -> @@ -344,7 +381,8 @@ write_unicode_char(Uni) -> %% Return true if CharList is a (possibly deep) list of characters, else %% false. --spec char_list(term()) -> boolean(). +-spec char_list(Term) -> boolean() when + Term :: term(). char_list([C|Cs]) when is_integer(C), C >= $\000, C =< $\377 -> char_list(Cs); @@ -360,7 +398,8 @@ unicode_char_list([C|Cs]) when is_integer(C), C >= 0, C < 16#D800; unicode_char_list([]) -> true; unicode_char_list(_) -> false. %Everything else is false --spec deep_char_list(term()) -> boolean(). +-spec deep_char_list(Term) -> boolean() when + Term :: term(). deep_char_list(Cs) -> deep_char_list(Cs, []). @@ -397,7 +436,8 @@ deep_unicode_char_list(_, _More) -> %Everything else is false %% Return true if CharList is a list of printable characters, else %% false. --spec printable_list(term()) -> boolean(). +-spec printable_list(Term) -> boolean() when + Term :: term(). printable_list([C|Cs]) when is_integer(C), C >= $\040, C =< $\176 -> printable_list(Cs); diff --git a/lib/stdlib/src/io_lib_format.erl b/lib/stdlib/src/io_lib_format.erl index eb1885021d..49a00a4ec7 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-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. 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 @@ -558,28 +558,30 @@ iolist_to_chars(B) when is_binary(B) -> string(S, none, _Adj, none, _Pad) -> S; string(S, F, Adj, none, Pad) -> - N = lists:flatlength(S), - if N > F -> flat_trunc(S, F); - N =:= F -> S; - true -> adjust(S, chars(Pad, F-N), Adj) - end; + string_field(S, F, Adj, lists:flatlength(S), Pad); string(S, none, _Adj, P, Pad) -> + string_field(S, P, left, lists:flatlength(S), Pad); +string(S, F, Adj, P, Pad) when F >= P -> N = lists:flatlength(S), - if N > P -> flat_trunc(S, P); - N =:= P -> S; - true -> [S|chars(Pad, P-N)] - end; -string(S, F, Adj, F, Pad) -> - string(S, none, Adj, F, Pad); -string(S, F, Adj, P, Pad) when F > P -> - N = lists:flatlength(S), - if N > F -> flat_trunc(S, F); - N =:= F -> S; - N > P -> adjust(flat_trunc(S, P), chars(Pad, F-P), Adj); - N =:= P -> adjust(S, chars(Pad, F-P), Adj); - true -> adjust([S|chars(Pad, P-N)], chars(Pad, F-P), Adj) + if F > P -> + if N > P -> + adjust(flat_trunc(S, P), chars(Pad, F-P), Adj); + N < P -> + adjust([S|chars(Pad, P-N)], chars(Pad, F-P), Adj); + true -> % N == P + adjust(S, chars(Pad, F-P), Adj) + end; + true -> % F == P + string_field(S, F, Adj, N, Pad) end. +string_field(S, F, _Adj, N, _Pad) when N > F -> + flat_trunc(S, F); +string_field(S, F, Adj, N, Pad) when N < F -> + adjust(S, chars(Pad, F-N), Adj); +string_field(S, _, _, _, _) -> % N == F + S. + %% unprefixed_integer(Int, Field, Adjust, Base, PadChar, Lowercase) %% -> [Char]. @@ -624,8 +626,8 @@ newline(F, right, _P, _Pad) -> chars($\n, F). %% adjust(Data, [], _) -> Data; -adjust(Data, Pad, left) -> [Data,Pad]; -adjust(Data, Pad, right) -> [Pad,Data]. +adjust(Data, Pad, left) -> [Data|Pad]; +adjust(Data, Pad, right) -> [Pad|Data]. %% Flatten and truncate a deep list to at most N elements. diff --git a/lib/stdlib/src/io_lib_fread.erl b/lib/stdlib/src/io_lib_fread.erl index 74316dc730..52aa4d073c 100644 --- a/lib/stdlib/src/io_lib_fread.erl +++ b/lib/stdlib/src/io_lib_fread.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1996-2011. 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(io_lib_fread). @@ -25,31 +25,21 @@ -import(lists, [reverse/1,reverse/2]). %%----------------------------------------------------------------------- -%% Local types -%%----------------------------------------------------------------------- - --type done_arg2() :: {'ok', io_lib:chars()} | 'eof' | {'error', term()}. - -%%----------------------------------------------------------------------- -%% Types also used in other files -%%----------------------------------------------------------------------- - --type continuation() :: [] | {_, _, _, _}. % XXX: refine - --type fread_2_ret() :: {'ok', io_lib:chars(), string()} - | {'more', string(), non_neg_integer(), io_lib:chars()} - | {'error', term()}. --type fread_3_ret() :: {'more', continuation()} - | {'done', done_arg2(), string()}. - -%%----------------------------------------------------------------------- %% fread(Continuation, CharList, FormatString) %% This is the main function into the re-entrant formatted reader. It %% repeatedly collects lines and calls fread/2 to format the input until %% all the format string has been used. And it counts the characters. --spec fread(io_lib_fread:continuation(), string(), string()) -> fread_3_ret(). +-spec fread(Continuation, String, Format) -> Return when + Continuation :: io_lib:continuation() | [], + String :: string(), + Format :: string(), + Return :: {'more', Continuation1 :: io_lib:continuation()} + | {'done', Result, LeftOverChars :: string()}, + Result :: {'ok', InputList :: io_lib:chars()} + | 'eof' + | {'error', What :: term()}. fread([], Chars, Format) -> %%io:format("FREAD: ~w `~s'~n", [Format,Chars]), @@ -104,7 +94,14 @@ fread_line(Format0, Line, N0, Results0, More, Newline) -> %% WHITE Skip white space %% X Literal X --spec fread(string(), string()) -> fread_2_ret(). +-spec fread(Format, String) -> Result when + Format :: string(), + String :: string(), + Result :: {'ok', InputList :: io_lib:chars(), LeftOverChars :: string()} + | {'more', RestFormat :: string(), + Nchars :: non_neg_integer(), + InputStack :: io_lib:chars()} + | {'error', What :: term()}. fread(Format, Line) -> fread(Format, Line, 0, []). diff --git a/lib/stdlib/src/lib.erl b/lib/stdlib/src/lib.erl index b2cfb00de9..c303ae60b5 100644 --- a/lib/stdlib/src/lib.erl +++ b/lib/stdlib/src/lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. 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,9 @@ flush_receive() -> %% %% Functions for doing standard system format i/o. %% --spec error_message(atom() | string() | binary(), [term()]) -> 'ok'. +-spec error_message(Format, Args) -> 'ok' when + Format :: io:format(), + Args :: [term()]. error_message(Format, Args) -> io:format(<<"** ~s **\n">>, [io_lib:format(Format, Args)]). @@ -55,17 +57,23 @@ progname() -> no_prog_name end. --spec nonl(string()) -> string(). +-spec nonl(String1) -> String2 when + String1 :: string(), + String2 :: string(). nonl([10]) -> []; nonl([]) -> []; nonl([H|T]) -> [H|nonl(T)]. --spec send(pid() | atom() | {atom(), node()}, term()) -> term(). +-spec send(To, Msg) -> Msg when + To :: pid() | atom() | {atom(), node()}, + Msg :: term(). send(To, Msg) -> To ! Msg. --spec sendw(pid() | atom() | {atom(), node()}, term()) -> term(). +-spec sendw(To, Msg) -> Msg when + To :: pid() | atom() | {atom(), node()}, + Msg :: term(). sendw(To, Msg) -> To ! {self(), Msg}, @@ -89,7 +97,7 @@ eval_str(Str) when is_list(Str) -> true -> case erl_parse:parse_exprs(Toks) of {ok, Exprs} -> - case catch erl_eval:exprs(Exprs, []) of + case catch erl_eval:exprs(Exprs, erl_eval:new_bindings()) of {value, Val, _} -> {ok, Val}; Other -> diff --git a/lib/stdlib/src/lists.erl b/lib/stdlib/src/lists.erl index e1f8d1c200..bba46e4cb6 100644 --- a/lib/stdlib/src/lists.erl +++ b/lib/stdlib/src/lists.erl @@ -1,23 +1,26 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1996-2011. 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(lists). +-compile({no_auto_import,[max/2]}). +-compile({no_auto_import,[min/2]}). + -export([append/2, append/1, subtract/2, reverse/1, nth/2, nthtail/2, prefix/2, suffix/2, last/1, seq/2, seq/3, sum/1, duplicate/2, min/1, max/1, sublist/2, sublist/3, @@ -25,7 +28,7 @@ unzip/1, unzip3/1, zip/2, zip3/3, zipwith/3, zipwith3/4, sort/1, merge/1, merge/2, rmerge/2, merge3/3, rmerge3/3, usort/1, umerge/1, umerge3/3, umerge/2, rumerge3/3, rumerge/2, - concat/1, flatten/1, flatten/2, flat_length/1, flatlength/1, + concat/1, flatten/1, flatten/2, flatlength/1, keydelete/3, keyreplace/4, keytake/3, keystore/4, keysort/2, keymerge/3, rkeymerge/3, rukeymerge/3, ukeysort/2, ukeymerge/3, keymap/3]). @@ -40,8 +43,6 @@ mapfoldl/3,mapfoldr/3,foreach/2,takewhile/2,dropwhile/2,splitwith/2, split/2]). --deprecated([flat_length/1]). - %% member(X, L) -> (true | false) %% test if X is a member of the list L %% Now a BIF! @@ -53,13 +54,21 @@ %% append(X, Y) appends lists X and Y --spec append([T], [T]) -> [T]. +-spec append(List1, List2) -> List3 when + List1 :: [T], + List2 :: [T], + List3 :: [T], + T :: term(). append(L1, L2) -> L1 ++ L2. %% append(L) appends the list of lists L --spec append([[T]]) -> [T]. +-spec append(ListOfLists) -> List1 when + ListOfLists :: [List], + List :: [T], + List1 :: [T], + T :: term(). append([E]) -> E; append([H|T]) -> H ++ append(T); @@ -67,13 +76,20 @@ append([]) -> []. %% subtract(List1, List2) subtract elements in List2 form List1. --spec subtract([T], [T]) -> [T]. +-spec subtract(List1, List2) -> List3 when + List1 :: [T], + List2 :: [T], + List3 :: [T], + T :: term(). subtract(L1, L2) -> L1 -- L2. %% reverse(L) reverse all elements in the list L. Is now a BIF! --spec reverse([T]) -> [T]. +-spec reverse(List1) -> List2 when + List1 :: [T], + List2 :: [T], + T :: term(). reverse([] = L) -> L; @@ -92,13 +108,21 @@ reverse([A, B | L]) -> %% nth(N, L) returns the N`th element of the list L %% nthtail(N, L) returns the N`th tail of the list L --spec nth(pos_integer(), [T,...]) -> T. +-spec nth(N, List) -> Elem when + N :: pos_integer(), + List :: [T,...], + Elem :: T, + T :: term(). nth(1, [H|_]) -> H; nth(N, [_|T]) when N > 1 -> nth(N - 1, T). --spec nthtail(non_neg_integer(), [T,...]) -> [T]. +-spec nthtail(N, List) -> Tail when + N :: non_neg_integer(), + List :: [T,...], + Tail :: [T], + T :: term(). nthtail(1, [_|T]) -> T; nthtail(N, [_|T]) when N > 1 -> @@ -107,7 +131,10 @@ nthtail(0, L) when is_list(L) -> L. %% prefix(Prefix, List) -> (true | false) --spec prefix([T], [T]) -> boolean(). +-spec prefix(List1, List2) -> boolean() when + List1 :: [T], + List2 :: [T], + T :: term(). prefix([X|PreTail], [X|Tail]) -> prefix(PreTail, Tail); @@ -116,7 +143,10 @@ prefix([_|_], List) when is_list(List) -> false. %% suffix(Suffix, List) -> (true | false) --spec suffix([T], [T]) -> boolean(). +-spec suffix(List1, List2) -> boolean() when + List1 :: [T], + List2 :: [T], + T :: term(). suffix(Suffix, List) -> Delta = length(List) - length(Suffix), @@ -124,7 +154,10 @@ suffix(Suffix, List) -> %% last(List) returns the last element in a list. --spec last([T,...]) -> T. +-spec last(List) -> Last when + List :: [T,...], + Last :: T, + T :: term(). last([E|Es]) -> last(E, Es). @@ -136,7 +169,10 @@ last(E, []) -> E. %% returns the sequence Min..Max %% Min <= Max and Min and Max must be integers --spec seq(integer(), integer()) -> [integer()]. +-spec seq(From, To) -> Seq when + From :: integer(), + To :: integer(), + Seq :: [integer()]. seq(First, Last) when is_integer(First), is_integer(Last), First-1 =< Last -> @@ -151,7 +187,11 @@ seq_loop(1, X, L) -> seq_loop(0, _, L) -> L. --spec seq(integer(), integer(), integer()) -> [integer()]. +-spec seq(From, To, Incr) -> Seq when + From :: integer(), + To :: integer(), + Incr :: integer(), + Seq :: [integer()]. seq(First, Last, Inc) when is_integer(First), is_integer(Last), is_integer(Inc) -> @@ -177,7 +217,8 @@ seq_loop(0, _, _, L) -> %% sum(L) returns the sum of the elements in L --spec sum([number()]) -> number(). +-spec sum(List) -> number() when + List :: [number()]. sum(L) -> sum(L, 0). @@ -187,7 +228,11 @@ sum([], Sum) -> Sum. %% duplicate(N, X) -> [X,X,X,.....,X] (N times) %% return N copies of X --spec duplicate(non_neg_integer(), T) -> [T]. +-spec duplicate(N, Elem) -> List when + N :: non_neg_integer(), + Elem :: T, + List :: [T], + T :: term(). duplicate(N, X) when is_integer(N), N >= 0 -> duplicate(N, X, []). @@ -196,7 +241,10 @@ duplicate(N, X, L) -> duplicate(N-1, X, [X|L]). %% min(L) -> returns the minimum element of the list L --spec min([T,...]) -> T. +-spec min(List) -> Min when + List :: [T,...], + Min :: T, + T :: term(). min([H|T]) -> min(T, H). @@ -206,7 +254,10 @@ min([], Min) -> Min. %% max(L) -> returns the maximum element of the list L --spec max([T,...]) -> T. +-spec max(List) -> Max when + List :: [T,...], + Max :: T, + T :: term(). max([H|T]) -> max(T, H). @@ -217,12 +268,21 @@ max([], Max) -> Max. %% sublist(List, Start, Length) %% Returns the sub-list starting at Start of length Length. --spec sublist([T], pos_integer(), non_neg_integer()) -> [T]. +-spec sublist(List1, Start, Len) -> List2 when + List1 :: [T], + List2 :: [T], + Start :: pos_integer(), + Len :: non_neg_integer(), + T :: term(). sublist(List, S, L) when is_integer(L), L >= 0 -> sublist(nthtail(S-1, List), L). --spec sublist([T], non_neg_integer()) -> [T]. +-spec sublist(List1, Len) -> List2 when + List1 :: [T], + List2 :: [T], + Len :: non_neg_integer(), + T :: term(). sublist(List, L) when is_integer(L), is_list(List) -> sublist_2(List, L). @@ -237,7 +297,11 @@ sublist_2(List, L) when is_list(List), L > 0 -> %% delete(Item, List) -> List' %% Delete the first occurrence of Item from the list L. --spec delete(T, [T]) -> [T]. +-spec delete(Elem, List1) -> List2 when + Elem :: T, + List1 :: [T], + List2 :: [T], + T :: term(). delete(Item, [Item|Rest]) -> Rest; delete(Item, [H|Rest]) -> @@ -247,7 +311,12 @@ delete(_, []) -> []. %% Return [{X0, Y0}, {X1, Y1}, ..., {Xn, Yn}] for lists [X0, X1, ..., %% Xn] and [Y0, Y1, ..., Yn]. --spec zip([A], [B]) -> [{A, B}]. +-spec zip(List1, List2) -> List3 when + List1 :: [A], + List2 :: [B], + List3 :: [{A, B}], + A :: term(), + B :: term(). zip([X | Xs], [Y | Ys]) -> [{X, Y} | zip(Xs, Ys)]; zip([], []) -> []. @@ -255,7 +324,12 @@ zip([], []) -> []. %% Return {[X0, X1, ..., Xn], [Y0, Y1, ..., Yn]}, for a list [{X0, Y0}, %% {X1, Y1}, ..., {Xn, Yn}]. --spec unzip([{A, B}]) -> {[A], [B]}. +-spec unzip(List1) -> {List2, List3} when + List1 :: [{A, B}], + List2 :: [A], + List3 :: [B], + A :: term(), + B :: term(). unzip(Ts) -> unzip(Ts, [], []). @@ -265,7 +339,14 @@ unzip([], Xs, Ys) -> {reverse(Xs), reverse(Ys)}. %% Return [{X0, Y0, Z0}, {X1, Y1, Z1}, ..., {Xn, Yn, Zn}] for lists [X0, %% X1, ..., Xn], [Y0, Y1, ..., Yn] and [Z0, Z1, ..., Zn]. --spec zip3([A], [B], [C]) -> [{A, B, C}]. +-spec zip3(List1, List2, List3) -> List4 when + List1 :: [A], + List2 :: [B], + List3 :: [C], + List4 :: [{A, B, C}], + A :: term(), + B :: term(), + C :: term(). zip3([X | Xs], [Y | Ys], [Z | Zs]) -> [{X, Y, Z} | zip3(Xs, Ys, Zs)]; zip3([], [], []) -> []. @@ -273,7 +354,14 @@ zip3([], [], []) -> []. %% Return {[X0, X1, ..., Xn], [Y0, Y1, ..., Yn], [Z0, Z1, ..., Zn]}, for %% a list [{X0, Y0, Z0}, {X1, Y1, Z1}, ..., {Xn, Yn, Zn}]. --spec unzip3([{A, B, C}]) -> {[A], [B], [C]}. +-spec unzip3(List1) -> {List2, List3, List4} when + List1 :: [{A, B, C}], + List2 :: [A], + List3 :: [B], + List4 :: [C], + A :: term(), + B :: term(), + C :: term(). unzip3(Ts) -> unzip3(Ts, [], [], []). @@ -285,7 +373,14 @@ unzip3([], Xs, Ys, Zs) -> %% Return [F(X0, Y0), F(X1, Y1), ..., F(Xn, Yn)] for lists [X0, X1, ..., %% Xn] and [Y0, Y1, ..., Yn]. --spec zipwith(fun((X, Y) -> R), [X], [Y]) -> [R]. +-spec zipwith(Combine, List1, List2) -> List3 when + Combine :: fun((X, Y) -> T), + List1 :: [X], + List2 :: [Y], + List3 :: [T], + X :: term(), + Y :: term(), + T :: term(). zipwith(F, [X | Xs], [Y | Ys]) -> [F(X, Y) | zipwith(F, Xs, Ys)]; zipwith(F, [], []) when is_function(F, 2) -> []. @@ -293,7 +388,16 @@ zipwith(F, [], []) when is_function(F, 2) -> []. %% Return [F(X0, Y0, Z0), F(X1, Y1, Z1), ..., F(Xn, Yn, Zn)] for lists %% [X0, X1, ..., Xn], [Y0, Y1, ..., Yn] and [Z0, Z1, ..., Zn]. --spec zipwith3(fun((X, Y, Z) -> R), [X], [Y], [Z]) -> [R]. +-spec zipwith3(Combine, List1, List2, List3) -> List4 when + Combine :: fun((X, Y, Z) -> T), + List1 :: [X], + List2 :: [Y], + List3 :: [Z], + List4 :: [T], + X :: term(), + Y :: term(), + Z :: term(), + T :: term(). zipwith3(F, [X | Xs], [Y | Ys], [Z | Zs]) -> [F(X, Y, Z) | zipwith3(F, Xs, Ys, Zs)]; @@ -302,7 +406,10 @@ zipwith3(F, [], [], []) when is_function(F, 3) -> []. %% sort(List) -> L %% sorts the list L --spec sort([T]) -> [T]. +-spec sort(List1) -> List2 when + List1 :: [T], + List2 :: [T], + T :: term(). sort([X, Y | L] = L0) when X =< Y -> case L of @@ -349,7 +456,11 @@ sort_1(X, [], R) -> %% merge(List) -> L %% merges a list of sorted lists --spec merge([T]) -> [T]. +-spec merge(ListOfLists) -> List1 when + ListOfLists :: [List], + List :: [T], + List1 :: [T], + T :: term(). merge(L) -> mergel(L, []). @@ -357,7 +468,14 @@ merge(L) -> %% merge3(X, Y, Z) -> L %% merges three sorted lists X, Y and Z --spec merge3([_], [_], [_]) -> [_]. +-spec merge3(List1, List2, List3) -> List4 when + List1 :: [X], + List2 :: [Y], + List3 :: [Z], + List4 :: [(X | Y | Z)], + X :: term(), + Y :: term(), + Z :: term(). merge3(L1, [], L3) -> merge(L1, L3); @@ -369,7 +487,7 @@ merge3(L1, [H2 | T2], [H3 | T3]) -> %% rmerge3(X, Y, Z) -> L %% merges three reversed sorted lists X, Y and Z --spec rmerge3([_], [_], [_]) -> [_]. +-spec rmerge3([X], [Y], [Z]) -> [(X | Y | Z)]. rmerge3(L1, [], L3) -> rmerge(L1, L3); @@ -381,7 +499,12 @@ rmerge3(L1, [H2 | T2], [H3 | T3]) -> %% merge(X, Y) -> L %% merges two sorted lists X and Y --spec merge([_], [_]) -> [_]. +-spec merge(List1, List2) -> List3 when + List1 :: [X], + List2 :: [Y], + List3 :: [(X | Y)], + X :: term(), + Y :: term(). merge(T1, []) -> T1; @@ -393,7 +516,7 @@ merge(T1, [H2 | T2]) -> %% reverse(rmerge(reverse(A),reverse(B))) is equal to merge(I,A,B). --spec rmerge([_], [_]) -> [_]. +-spec rmerge([X], [Y]) -> [(X | Y)]. rmerge(T1, []) -> T1; @@ -404,8 +527,9 @@ rmerge(T1, [H2 | T2]) -> %% in L - the elements in L can be atoms, numbers of strings. %% Returns a list of characters. --type concat_thing() :: atom() | integer() | float() | string(). --spec concat([concat_thing()]) -> string(). +-spec concat(Things) -> string() when + Things :: [Thing], + Thing :: atom() | integer() | float() | string(). concat(List) -> flatmap(fun thing_to_list/1, List). @@ -419,12 +543,17 @@ thing_to_list(X) when is_list(X) -> X. %Assumed to be a string %% flatten(List, Tail) %% Flatten a list, adding optional tail. --spec flatten([_]) -> [_]. +-spec flatten(DeepList) -> List when + DeepList :: [term() | DeepList], + List :: [term()]. flatten(List) when is_list(List) -> do_flatten(List, []). --spec flatten([_], [_]) -> [_]. +-spec flatten(DeepList, Tail) -> List when + DeepList :: [term() | DeepList], + Tail :: [term()], + List :: [term()]. flatten(List, Tail) when is_list(List), is_list(Tail) -> do_flatten(List, Tail). @@ -436,17 +565,11 @@ do_flatten([H|T], Tail) -> do_flatten([], Tail) -> Tail. -%% flat_length(List) (undocumented can be removed later) -%% Calculate the length of a list of lists. - --spec flat_length([_]) -> non_neg_integer(). - -flat_length(List) -> flatlength(List). - %% flatlength(List) %% Calculate the length of a list of lists. --spec flatlength([_]) -> non_neg_integer(). +-spec flatlength(DeepList) -> non_neg_integer() when + DeepList :: [term() | DeepList]. flatlength(List) -> flatlength(List, 0). @@ -487,7 +610,12 @@ flatlength([], L) -> L. % keysearch3(Key, N, T); %keysearch3(Key, N, []) -> false. --spec keydelete(_, pos_integer(), [T]) -> [T]. +-spec keydelete(Key, N, TupleList1) -> TupleList2 when + Key :: term(), + N :: pos_integer(), + TupleList1 :: [Tuple], + TupleList2 :: [Tuple], + Tuple :: tuple(). keydelete(K, N, L) when is_integer(N), N > 0 -> keydelete3(K, N, L). @@ -497,7 +625,12 @@ keydelete3(Key, N, [H|T]) -> [H|keydelete3(Key, N, T)]; keydelete3(_, _, []) -> []. --spec keyreplace(_, pos_integer(), [_], tuple()) -> [_]. +-spec keyreplace(Key, N, TupleList1, NewTuple) -> TupleList2 when + Key :: term(), + N :: pos_integer(), + TupleList1 :: [tuple()], + TupleList2 :: [tuple()], + NewTuple :: tuple(). keyreplace(K, N, L, New) when is_integer(N), N > 0, is_tuple(New) -> keyreplace3(K, N, L, New). @@ -508,7 +641,12 @@ keyreplace3(Key, Pos, [H|T], New) -> [H|keyreplace3(Key, Pos, T, New)]; keyreplace3(_, _, [], _) -> []. --spec keytake(_, pos_integer(), [_]) -> {'value', tuple(), [_]} | 'false'. +-spec keytake(Key, N, TupleList1) -> {value, Tuple, TupleList2} | false when + Key :: term(), + N :: pos_integer(), + TupleList1 :: [tuple()], + TupleList2 :: [tuple()], + Tuple :: tuple(). keytake(Key, N, L) when is_integer(N), N > 0 -> keytake(Key, N, L, []). @@ -519,7 +657,13 @@ keytake(Key, N, [H|T], L) -> keytake(Key, N, T, [H|L]); keytake(_K, _N, [], _L) -> false. --spec keystore(_, pos_integer(), [_], tuple()) -> [_]. +-spec keystore(Key, N, TupleList1, NewTuple) -> TupleList2 when + Key :: term(), + N :: pos_integer(), + TupleList1 :: [tuple()], + TupleList2 :: [tuple(), ...], + NewTuple :: tuple(). + keystore(K, N, L, New) when is_integer(N), N > 0, is_tuple(New) -> keystore2(K, N, L, New). @@ -530,7 +674,11 @@ keystore2(Key, N, [H|T], New) -> keystore2(_Key, _N, [], New) -> [New]. --spec keysort(pos_integer(), [T]) -> [T] when is_subtype(T, tuple()). +-spec keysort(N, TupleList1) -> TupleList2 when + N :: pos_integer(), + TupleList1 :: [Tuple], + TupleList2 :: [Tuple], + Tuple :: tuple(). keysort(I, L) when is_integer(I), I > 0 -> case L of @@ -587,8 +735,13 @@ keysort_1(I, X, EX, [Y | L], R) -> keysort_1(_I, X, _EX, [], R) -> lists:reverse(R, [X]). --spec keymerge(pos_integer(), [X], [Y]) -> - [R] when is_subtype(X, tuple()), is_subtype(Y, tuple()), is_subtype(R, tuple()). +-spec keymerge(N, TupleList1, TupleList2) -> TupleList3 when + N :: pos_integer(), + TupleList1 :: [T1], + TupleList2 :: [T2], + TupleList3 :: [(T1 | T2)], + T1 :: tuple(), + T2 :: tuple(). keymerge(Index, T1, L2) when is_integer(Index), Index > 0 -> case L2 of @@ -603,7 +756,7 @@ keymerge(Index, T1, L2) when is_integer(Index), Index > 0 -> %% reverse(rkeymerge(I,reverse(A),reverse(B))) is equal to keymerge(I,A,B). -spec rkeymerge(pos_integer(), [X], [Y]) -> - [R] when is_subtype(X, tuple()), is_subtype(Y, tuple()), is_subtype(R, tuple()). + [R] when X :: tuple(), Y :: tuple(), R :: tuple(). rkeymerge(Index, T1, L2) when is_integer(Index), Index > 0 -> case L2 of @@ -615,7 +768,11 @@ rkeymerge(Index, T1, L2) when is_integer(Index), Index > 0 -> lists:reverse(M, []) end. --spec ukeysort(pos_integer(), [T]) -> [T] when is_subtype(T, tuple()). +-spec ukeysort(N, TupleList1) -> TupleList2 when + N :: pos_integer(), + TupleList1 :: [Tuple], + TupleList2 :: [Tuple], + Tuple :: tuple(). ukeysort(I, L) when is_integer(I), I > 0 -> case L of @@ -680,8 +837,13 @@ ukeysort_1(I, X, EX, [Y | L]) -> ukeysort_1(_I, X, _EX, []) -> [X]. --spec ukeymerge(pos_integer(), [X], [Y]) -> - [(X | Y)] when is_subtype(X, tuple()), is_subtype(Y, tuple()). +-spec ukeymerge(N, TupleList1, TupleList2) -> TupleList3 when + N :: pos_integer(), + TupleList1 :: [T1], + TupleList2 :: [T2], + TupleList3 :: [(T1 | T2)], + T1 :: tuple(), + T2 :: tuple(). ukeymerge(Index, L1, T2) when is_integer(Index), Index > 0 -> case L1 of @@ -696,7 +858,7 @@ ukeymerge(Index, L1, T2) when is_integer(Index), Index > 0 -> %% reverse(rukeymerge(I,reverse(A),reverse(B))) is equal to ukeymerge(I,A,B). -spec rukeymerge(pos_integer(), [X], [Y]) -> - [(X | Y)] when is_subtype(X, tuple()), is_subtype(Y, tuple()). + [(X | Y)] when X :: tuple(), Y :: tuple(). rukeymerge(Index, T1, L2) when is_integer(Index), Index > 0 -> case L2 of @@ -708,7 +870,11 @@ rukeymerge(Index, T1, L2) when is_integer(Index), Index > 0 -> lists:reverse(M, []) end. --spec keymap(fun((_) -> _), pos_integer(), [tuple()]) -> [tuple()]. +-spec keymap(Fun, N, TupleList1) -> TupleList2 when + Fun :: fun((Term1 :: term()) -> Term2 :: term()), + N :: pos_integer(), + TupleList1 :: [tuple()], + TupleList2 :: [tuple()]. keymap(Fun, Index, [Tup|Tail]) -> [setelement(Index, Tup, Fun(element(Index, Tup)))|keymap(Fun, Index, Tail)]; @@ -717,7 +883,11 @@ keymap(Fun, Index, []) when is_integer(Index), Index >= 1, %%% Suggestion from OTP-2948: sort and merge with Fun. --spec sort(fun((T, T) -> boolean()), [T]) -> [T]. +-spec sort(Fun, List1) -> List2 when + Fun :: fun((A :: T, B :: T) -> boolean()), + List1 :: [T], + List2 :: [T], + T :: term(). sort(Fun, []) when is_function(Fun, 2) -> []; @@ -731,7 +901,13 @@ sort(Fun, [X, Y | T]) -> fsplit_2(Y, X, Fun, T, [], []) end. --spec merge(fun((X, Y) -> boolean()), [X], [Y]) -> [_]. +-spec merge(Fun, List1, List2) -> List3 when + Fun :: fun((A, B) -> boolean()), + List1 :: [A], + List2 :: [B], + List3 :: [(A | B)], + A :: term(), + B :: term(). merge(Fun, T1, [H2 | T2]) when is_function(Fun, 2) -> lists:reverse(fmerge2_1(T1, H2, Fun, T2, []), []); @@ -740,14 +916,18 @@ merge(Fun, T1, []) when is_function(Fun, 2) -> %% reverse(rmerge(F,reverse(A),reverse(B))) is equal to merge(F,A,B). --spec rmerge(fun((X, Y) -> boolean()), [X], [Y]) -> [_]. +-spec rmerge(fun((X, Y) -> boolean()), [X], [Y]) -> [(X | Y)]. rmerge(Fun, T1, [H2 | T2]) when is_function(Fun, 2) -> lists:reverse(rfmerge2_1(T1, H2, Fun, T2, []), []); rmerge(Fun, T1, []) when is_function(Fun, 2) -> T1. --spec usort(fun((T, T) -> boolean()), [T]) -> [T]. +-spec usort(Fun, List1) -> List2 when + Fun :: fun((T, T) -> boolean()), + List1 :: [T], + List2 :: [T], + T :: term(). usort(Fun, [_] = L) when is_function(Fun, 2) -> L; @@ -774,7 +954,13 @@ usort_1(Fun, X, [Y | L]) -> ufsplit_2(Y, L, Fun, [X]) end. --spec umerge(fun((X, Y) -> boolean()), [X], [Y]) -> [_]. +-spec umerge(Fun, List1, List2) -> List3 when + Fun :: fun((A, B) -> boolean()), + List1 :: [A], + List2 :: [B], + List3 :: [(A | B)], + A :: term(), + B :: term(). umerge(Fun, [], T2) when is_function(Fun, 2) -> T2; @@ -783,7 +969,7 @@ umerge(Fun, [H1 | T1], T2) when is_function(Fun, 2) -> %% reverse(rumerge(F,reverse(A),reverse(B))) is equal to umerge(F,A,B). --spec rumerge(fun((X, Y) -> boolean()), [X], [Y]) -> [_]. +-spec rumerge(fun((X, Y) -> boolean()), [X], [Y]) -> [(X | Y)]. rumerge(Fun, T1, []) when is_function(Fun, 2) -> T1; @@ -793,7 +979,10 @@ rumerge(Fun, T1, [H2 | T2]) when is_function(Fun, 2) -> %% usort(List) -> L %% sorts the list L, removes duplicates --spec usort([T]) -> [T]. +-spec usort(List1) -> List2 when + List1 :: [T], + List2 :: [T], + T :: term(). usort([X, Y | L] = L0) when X < Y -> case L of @@ -848,7 +1037,11 @@ usort_1(X, []) -> %% umerge(List) -> L %% merges a list of sorted lists without duplicates, removes duplicates --spec umerge([T]) -> [T]. +-spec umerge(ListOfLists) -> List1 when + ListOfLists :: [List], + List :: [T], + List1 :: [T], + T :: term(). umerge(L) -> umergel(L). @@ -857,7 +1050,14 @@ umerge(L) -> %% merges three sorted lists X, Y and Z without duplicates, %% removes duplicates --spec umerge3([_], [_], [_]) -> [_]. +-spec umerge3(List1, List2, List3) -> List4 when + List1 :: [X], + List2 :: [Y], + List3 :: [Z], + List4 :: [(X | Y | Z)], + X :: term(), + Y :: term(), + Z :: term(). umerge3(L1, [], L3) -> umerge(L1, L3); @@ -870,7 +1070,7 @@ umerge3(L1, [H2 | T2], [H3 | T3]) -> %% merges three reversed sorted lists X, Y and Z without duplicates, %% removes duplicates --spec rumerge3([_], [_], [_]) -> [_]. +-spec rumerge3([X], [Y], [Z]) -> [(X | Y | Z)]. rumerge3(L1, [], L3) -> rumerge(L1, L3); @@ -882,7 +1082,12 @@ rumerge3(L1, [H2 | T2], [H3 | T3]) -> %% umerge(X, Y) -> L %% merges two sorted lists X and Y without duplicates, removes duplicates --spec umerge([_], [_]) -> [_]. +-spec umerge(List1, List2) -> List3 when + List1 :: [X], + List2 :: [Y], + List3 :: [(X | Y)], + X :: term(), + Y :: term(). umerge([], T2) -> T2; @@ -895,7 +1100,7 @@ umerge([H1 | T1], T2) -> %% reverse(rumerge(reverse(A),reverse(B))) is equal to umerge(I,A,B). --spec rumerge([_], [_]) -> [_]. +-spec rumerge([X], [Y]) -> [(X | Y)]. rumerge(T1, []) -> T1; @@ -928,7 +1133,10 @@ rumerge(T1, [H2 | T2]) -> %% There are also versions with an extra argument, ExtraArgs, which is a %% list of extra arguments to each call. --spec all(fun((T) -> boolean()), [T]) -> boolean(). +-spec all(Pred, List) -> boolean() when + Pred :: fun((Elem :: T) -> boolean()), + List :: [T], + T :: term(). all(Pred, [Hd|Tail]) -> case Pred(Hd) of @@ -937,7 +1145,10 @@ all(Pred, [Hd|Tail]) -> end; all(Pred, []) when is_function(Pred, 1) -> true. --spec any(fun((T) -> boolean()), [T]) -> boolean(). +-spec any(Pred, List) -> boolean() when + Pred :: fun((Elem :: T) -> boolean()), + List :: [T], + T :: term(). any(Pred, [Hd|Tail]) -> case Pred(Hd) of @@ -946,31 +1157,59 @@ any(Pred, [Hd|Tail]) -> end; any(Pred, []) when is_function(Pred, 1) -> false. --spec map(fun((D) -> R), [D]) -> [R]. +-spec map(Fun, List1) -> List2 when + Fun :: fun((A) -> B), + List1 :: [A], + List2 :: [B], + A :: term(), + B :: term(). map(F, [H|T]) -> [F(H)|map(F, T)]; map(F, []) when is_function(F, 1) -> []. --spec flatmap(fun((D) -> [R]), [D]) -> [R]. +-spec flatmap(Fun, List1) -> List2 when + Fun :: fun((A) -> [B]), + List1 :: [A], + List2 :: [B], + A :: term(), + B :: term(). flatmap(F, [Hd|Tail]) -> F(Hd) ++ flatmap(F, Tail); flatmap(F, []) when is_function(F, 1) -> []. --spec foldl(fun((T, _) -> _), _, [T]) -> _. +-spec foldl(Fun, Acc0, List) -> Acc1 when + Fun :: fun((Elem :: T, AccIn) -> AccOut), + Acc0 :: term(), + Acc1 :: term(), + AccIn :: term(), + AccOut :: term(), + List :: [T], + T :: term(). foldl(F, Accu, [Hd|Tail]) -> foldl(F, F(Hd, Accu), Tail); foldl(F, Accu, []) when is_function(F, 2) -> Accu. --spec foldr(fun((T, _) -> _), _, [T]) -> _. +-spec foldr(Fun, Acc0, List) -> Acc1 when + Fun :: fun((Elem :: T, AccIn) -> AccOut), + Acc0 :: term(), + Acc1 :: term(), + AccIn :: term(), + AccOut :: term(), + List :: [T], + T :: term(). foldr(F, Accu, [Hd|Tail]) -> F(Hd, foldr(F, Accu, Tail)); foldr(F, Accu, []) when is_function(F, 2) -> Accu. --spec filter(Pred :: fun((T) -> boolean()), List :: [T]) -> [T]. +-spec filter(Pred, List1) -> List2 when + Pred :: fun((Elem :: T) -> boolean()), + List1 :: [T], + List2 :: [T], + T :: term(). filter(Pred, List) when is_function(Pred, 1) -> [ E || E <- List, Pred(E) ]. @@ -978,7 +1217,12 @@ filter(Pred, List) when is_function(Pred, 1) -> %% Equivalent to {filter(F, L), filter(NotF, L)}, if NotF = 'fun(X) -> %% not F(X) end'. --spec partition(Pred :: fun((T) -> boolean()), List :: [T]) -> {[T], [T]}. +-spec partition(Pred, List) -> {Satisfying, NotSatisfying} when + Pred :: fun((Elem :: T) -> boolean()), + List :: [T], + Satisfying :: [T], + NotSatisfying :: [T], + T :: term(). partition(Pred, L) -> partition(Pred, L, [], []). @@ -1004,14 +1248,26 @@ zf(F, [Hd|Tail]) -> end; zf(F, []) when is_function(F, 1) -> []. --spec foreach(F :: fun((T) -> _), List :: [T]) -> 'ok'. +-spec foreach(Fun, List) -> ok when + Fun :: fun((Elem :: T) -> term()), + List :: [T], + T :: term(). foreach(F, [Hd|Tail]) -> F(Hd), foreach(F, Tail); foreach(F, []) when is_function(F, 1) -> ok. --spec mapfoldl(fun((T, _) -> {_, _}), _, [T]) -> {[_], _}. +-spec mapfoldl(Fun, Acc0, List1) -> {List2, Acc1} when + Fun :: fun((A, AccIn) -> {B, AccOut}), + Acc0 :: term(), + Acc1 :: term(), + AccIn :: term(), + AccOut :: term(), + List1 :: [A], + List2 :: [B], + A :: term(), + B :: term(). mapfoldl(F, Accu0, [Hd|Tail]) -> {R,Accu1} = F(Hd, Accu0), @@ -1019,7 +1275,16 @@ mapfoldl(F, Accu0, [Hd|Tail]) -> {[R|Rs],Accu2}; mapfoldl(F, Accu, []) when is_function(F, 2) -> {[],Accu}. --spec mapfoldr(fun((T, _) -> {_, _}), _, [T]) -> {[_], _}. +-spec mapfoldr(Fun, Acc0, List1) -> {List2, Acc1} when + Fun :: fun((A, AccIn) -> {B, AccOut}), + Acc0 :: term(), + Acc1 :: term(), + AccIn :: term(), + AccOut :: term(), + List1 :: [A], + List2 :: [B], + A :: term(), + B :: term(). mapfoldr(F, Accu0, [Hd|Tail]) -> {Rs,Accu1} = mapfoldr(F, Accu0, Tail), @@ -1027,7 +1292,11 @@ mapfoldr(F, Accu0, [Hd|Tail]) -> {[R|Rs],Accu2}; mapfoldr(F, Accu, []) when is_function(F, 2) -> {[],Accu}. --spec takewhile(fun((T) -> boolean()), [T]) -> [T]. +-spec takewhile(Pred, List1) -> List2 when + Pred :: fun((Elem :: T) -> boolean()), + List1 :: [T], + List2 :: [T], + T :: term(). takewhile(Pred, [Hd|Tail]) -> case Pred(Hd) of @@ -1036,7 +1305,11 @@ takewhile(Pred, [Hd|Tail]) -> end; takewhile(Pred, []) when is_function(Pred, 1) -> []. --spec dropwhile(fun((T) -> boolean()), [T]) -> [T]. +-spec dropwhile(Pred, List1) -> List2 when + Pred :: fun((Elem :: T) -> boolean()), + List1 :: [T], + List2 :: [T], + T :: term(). dropwhile(Pred, [Hd|Tail]=Rest) -> case Pred(Hd) of @@ -1045,7 +1318,12 @@ dropwhile(Pred, [Hd|Tail]=Rest) -> end; dropwhile(Pred, []) when is_function(Pred, 1) -> []. --spec splitwith(fun((T) -> boolean()), [T]) -> {[T], [T]}. +-spec splitwith(Pred, List) -> {List1, List2} when + Pred :: fun((T) -> boolean()), + List :: [T], + List1 :: [T], + List2 :: [T], + T :: term(). splitwith(Pred, List) when is_function(Pred, 1) -> splitwith(Pred, List, []). @@ -1058,7 +1336,12 @@ splitwith(Pred, [Hd|Tail], Taken) -> splitwith(Pred, [], Taken) when is_function(Pred, 1) -> {reverse(Taken),[]}. --spec split(non_neg_integer(), [T]) -> {[T], [T]}. +-spec split(N, List1) -> {List2, List3} when + N :: non_neg_integer(), + List1 :: [T], + List2 :: [T], + List3 :: [T], + T :: term(). split(N, List) when is_integer(N), N >= 0, is_list(List) -> case split(N, List, []) of diff --git a/lib/stdlib/src/log_mf_h.erl b/lib/stdlib/src/log_mf_h.erl index 2729f27e51..f7f128dac7 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-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. 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,14 +27,13 @@ %%----------------------------------------------------------------- --type dir() :: file:filename(). -type b() :: non_neg_integer(). -type f() :: 1..255. -type pred() :: fun((term()) -> boolean()). %%----------------------------------------------------------------- --record(state, {dir :: dir(), +-record(state, {dir :: file:filename(), maxB :: b(), maxF :: f(), curB :: b(), @@ -67,11 +66,23 @@ %% EventMgr = pid() | atom(). %%----------------------------------------------------------------- --spec init(dir(), b(), f()) -> {dir(), b(), f(), pred()}. +-opaque args() :: {file:filename(), b(), f(), pred()}. + + +-spec init(Dir, MaxBytes, MaxFiles) -> Args when + Dir :: file:filename(), + MaxBytes :: non_neg_integer(), % b() + MaxFiles :: 1..255, % f() + Args :: args(). init(Dir, MaxB, MaxF) -> init(Dir, MaxB, MaxF, fun(_) -> true end). --spec init(dir(), b(), f(), pred()) -> {dir(), b(), f(), pred()}. +-spec init(Dir, MaxBytes, MaxFiles, Pred) -> Args when + Dir :: file:filename(), + MaxBytes :: non_neg_integer(), % b() + MaxFiles :: 1..255, % f() + Pred :: fun((Event :: term()) -> boolean()), % pred() + Args :: args(). init(Dir, MaxB, MaxF, Pred) -> {Dir, MaxB, MaxF, Pred}. @@ -79,7 +90,7 @@ init(Dir, MaxB, MaxF, Pred) -> {Dir, MaxB, MaxF, Pred}. %% Call-back functions from gen_event %%----------------------------------------------------------------- --spec init({dir(), b(), f(), pred()}) -> {'ok', #state{}} | {'error', term()}. +-spec init({file:filename(), non_neg_integer(), f(), pred()}) -> {'ok', #state{}} | {'error', term()}. init({Dir, MaxB, MaxF, Pred}) when is_integer(MaxF), MaxF > 0, MaxF < 256 -> First = @@ -185,13 +196,19 @@ read_index_file(Dir) -> %%----------------------------------------------------------------- %% Write the index file. This file contains one binary with %% the last used filename (an integer). +%% Write a temporary file and rename it in order to make the update +%% atomic. %%----------------------------------------------------------------- write_index_file(Dir, Index) -> - case file:open(Dir ++ "/index", [raw, write]) of + File = Dir ++ "/index", + TmpFile = File ++ ".tmp", + case file:open(TmpFile, [raw, write]) of {ok, Fd} -> - file:write(Fd, [Index]), - ok = file:close(Fd); + ok = file:write(Fd, [Index]), + ok = file:close(Fd), + ok = file:rename(TmpFile,File), + ok; _ -> exit(open_index_file) end. diff --git a/lib/stdlib/src/ms_transform.erl b/lib/stdlib/src/ms_transform.erl index 78b1de6e16..48e22e53fa 100644 --- a/lib/stdlib/src/ms_transform.erl +++ b/lib/stdlib/src/ms_transform.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2009. All Rights Reserved. +%% Copyright Ericsson AB 2002-2011. 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 @@ -43,6 +43,7 @@ -define(ERR_GENREMOTECALL,22). -define(ERR_GENBINCONSTRUCT,23). -define(ERR_GENDISALLOWEDOP,24). +-define(WARN_SHADOW_VAR,50). -define(ERR_GUARDMATCH,?ERR_GENMATCH+?ERROR_BASE_GUARD). -define(ERR_BODYMATCH,?ERR_GENMATCH+?ERROR_BASE_BODY). -define(ERR_GUARDLOCALCALL,?ERR_GENLOCALCALL+?ERROR_BASE_GUARD). @@ -63,8 +64,18 @@ -define(ERR_BODYDISALLOWEDOP,?ERR_GENDISALLOWEDOP+?ERROR_BASE_BODY). %% -%% Called by compiler or ets/dbg:fun2ms when errors occur +%% Called by compiler or ets/dbg:fun2ms when errors/warnings occur %% + +-spec(format_error(Error) -> Chars when + Error :: {error, module(), term()}, + Chars :: io_lib:chars()). + +format_error({?WARN_SHADOW_VAR,Name}) -> + lists:flatten( + io_lib:format("variable ~p shadowed in ms_transform fun head", + [Name])); + format_error(?ERR_NOFUN) -> "Parameter of ets/dbg:fun2ms/1 is not a literal fun"; format_error(?ERR_ETS_HEAD) -> @@ -180,9 +191,15 @@ format_error(Else) -> %% %% Called when translating in shell %% + +-spec transform_from_shell(Dialect, Clauses, BoundEnvironment) -> term() when + Dialect :: ets | dbg, + Clauses :: [erl_parse:abstract_clause()], + BoundEnvironment :: erl_eval:binding_struct(). + transform_from_shell(Dialect, Clauses, BoundEnvironment) -> SaveFilename = setup_filename(), - case catch ms_clause_list(1,Clauses,Dialect) of + case catch ms_clause_list(1,Clauses,Dialect,gb_sets:new()) of {'EXIT',Reason} -> cleanup_filename(SaveFilename), exit(Reason); @@ -205,8 +222,14 @@ transform_from_shell(Dialect, Clauses, BoundEnvironment) -> %% %% Called when translating during compiling %% + +-spec parse_transform(Forms, Options) -> Forms when + Forms :: [erl_parse:abstract_form()], + Options :: term(). + parse_transform(Forms, _Options) -> SaveFilename = setup_filename(), + %io:format("Forms: ~p~n",[Forms]), case catch forms(Forms) of {'EXIT',Reason} -> cleanup_filename(SaveFilename), @@ -215,12 +238,31 @@ parse_transform(Forms, _Options) -> {error, [{cleanup_filename(SaveFilename), [{Line, ?MODULE, R}]}], []}; Else -> - cleanup_filename(SaveFilename), + %io:format("Transformed into: ~p~n",[Else]), + case get_warnings() of + [] -> + cleanup_filename(SaveFilename), + Else; + WL -> + FName = cleanup_filename(SaveFilename) , + WList = [ {FName, [{L, ?MODULE, R}]} || {L,R} <- WL ], + {warning, Else, WList} + end + end. + +get_warnings() -> + case get(warnings) of + undefined -> + []; + Else -> Else end. +add_warning(Line,R) -> + put(warnings,[{Line,R}| get_warnings()]). + setup_filename() -> - {erase(filename),erase(records)}. + {erase(filename),erase(records),erase(warnings)}. put_filename(Name) -> put(filename,Name). @@ -235,7 +277,7 @@ get_records() -> Else -> Else end. -cleanup_filename({Old,OldRec}) -> +cleanup_filename({Old,OldRec,OldWarnings}) -> Ret = case erase(filename) of undefined -> "TOP_LEVEL"; @@ -248,6 +290,12 @@ cleanup_filename({Old,OldRec}) -> Rec -> put(records,Rec) end, + case OldWarnings of + undefined -> + erase(warnings); + Warn -> + put(warnings,Warn) + end, case Old of undefined -> Ret; @@ -285,42 +333,77 @@ form({function,Line,Name0,Arity0,Clauses0}) -> form(AnyOther) -> AnyOther. function(Name, Arity, Clauses0) -> - Clauses1 = clauses(Clauses0), + {Clauses1,_} = clauses(Clauses0,gb_sets:new()), {Name,Arity,Clauses1}. -clauses([C0|Cs]) -> - C1 = clause(C0), - [C1|clauses(Cs)]; -clauses([]) -> []. -clause({clause,Line,H0,G0,B0}) -> - B1 = copy(B0), - {clause,Line,H0,G0,B1}. +clauses([C0|Cs],Bound) -> + {C1,Bound1} = clause(C0,Bound), + {C2,Bound2} = clauses(Cs,Bound1), + {[C1|C2],Bound2}; +clauses([],Bound) -> {[],Bound}. +clause({clause,Line,H0,G0,B0},Bound) -> + {H1,Bound1} = copy(H0,Bound), + {B1,Bound2} = copy(B0,Bound1), + {{clause,Line,H1,G0,B1},Bound2}. copy({call,Line,{remote,_Line2,{atom,_Line3,ets},{atom,_Line4,fun2ms}}, - As0}) -> - transform_call(ets,Line,As0); + As0},Bound) -> + {transform_call(ets,Line,As0,Bound),Bound}; copy({call,Line,{remote,_Line2,{record_field,_Line3, {atom,_Line4,''},{atom,_Line5,ets}}, - {atom,_Line6,fun2ms}}, As0}) -> + {atom,_Line6,fun2ms}}, As0},Bound) -> %% Packages... - transform_call(ets,Line,As0); + {transform_call(ets,Line,As0,Bound),Bound}; copy({call,Line,{remote,_Line2,{atom,_Line3,dbg},{atom,_Line4,fun2ms}}, - As0}) -> - transform_call(dbg,Line,As0); -copy(T) when is_tuple(T) -> - list_to_tuple(copy_list(tuple_to_list(T))); -copy(L) when is_list(L) -> - copy_list(L); -copy(AnyOther) -> - AnyOther. + As0},Bound) -> + {transform_call(dbg,Line,As0,Bound),Bound}; +copy({match,Line,A,B},Bound) -> + {B1,Bound1} = copy(B,Bound), + {A1,Bound2} = copy(A,Bound), + {{match,Line,A1,B1},gb_sets:union(Bound1,Bound2)}; +copy({var,_Line,'_'} = VarDef,Bound) -> + {VarDef,Bound}; +copy({var,_Line,Name} = VarDef,Bound) -> + Bound1 = gb_sets:add(Name,Bound), + {VarDef,Bound1}; +copy({'fun',Line,{clauses,Clauses}},Bound) -> % Dont export bindings from funs + {NewClauses,_IgnoredBindings} = copy_list(Clauses,Bound), + {{'fun',Line,{clauses,NewClauses}},Bound}; +copy({'case',Line,Of,ClausesList},Bound) -> % Dont export bindings from funs + {NewOf,NewBind0} = copy(Of,Bound), + {NewClausesList,NewBindings} = copy_case_clauses(ClausesList,NewBind0,[]), + {{'case',Line,NewOf,NewClausesList},NewBindings}; +copy(T,Bound) when is_tuple(T) -> + {L,Bound1} = copy_list(tuple_to_list(T),Bound), + {list_to_tuple(L),Bound1}; +copy(L,Bound) when is_list(L) -> + copy_list(L,Bound); +copy(AnyOther,Bound) -> + {AnyOther,Bound}. -copy_list([H|T]) -> - [copy(H)|copy_list(T)]; -copy_list([]) -> - []. +copy_case_clauses([],Bound,AddSets) -> + ReallyAdded = gb_sets:intersection(AddSets), + {[],gb_sets:union(Bound,ReallyAdded)}; +copy_case_clauses([{clause,Line,Match,Guard,Clauses}|T],Bound,AddSets) -> + {NewMatch,MatchBinds} = copy(Match,Bound), + {NewGuard,GuardBinds} = copy(Guard,MatchBinds), %% Really no new binds + {NewClauses,AllBinds} = copy(Clauses,GuardBinds), + %% To limit the setsizes, I subtract what I had before the case clause + %% and add it in the end + AddedBinds = gb_sets:subtract(AllBinds,Bound), + {NewTail,ExportedBindings} = + copy_case_clauses(T,Bound,[AddedBinds | AddSets]), + {[{clause,Line,NewMatch,NewGuard,NewClauses}|NewTail],ExportedBindings}. -transform_call(Type,_Line,[{'fun',Line2,{clauses, ClauseList}}]) -> - ms_clause_list(Line2, ClauseList,Type); -transform_call(_Type,Line,_NoAbstractFun) -> +copy_list([H|T],Bound) -> + {C1,Bound1} = copy(H,Bound), + {C2,Bound2} = copy_list(T,Bound1), + {[C1|C2],Bound2}; +copy_list([],Bound) -> + {[],Bound}. + +transform_call(Type,_Line,[{'fun',Line2,{clauses, ClauseList}}],Bound) -> + ms_clause_list(Line2, ClauseList,Type,Bound); +transform_call(_Type,Line,_NoAbstractFun,_) -> throw({error,Line,?ERR_NOFUN}). % Fixup semicolons in guards @@ -329,18 +412,19 @@ ms_clause_expand({clause, Line, Parameters, Guard = [_,_|_], Body}) -> ms_clause_expand(_Other) -> false. -ms_clause_list(Line,[H|T],Type) -> +ms_clause_list(Line,[H|T],Type,Bound) -> case ms_clause_expand(H) of NewHead when is_list(NewHead) -> - ms_clause_list(Line,NewHead ++ T, Type); + ms_clause_list(Line,NewHead ++ T, Type, Bound); false -> - {cons, Line, ms_clause(H,Type), ms_clause_list(Line, T,Type)} + {cons, Line, ms_clause(H, Type, Bound), + ms_clause_list(Line, T, Type, Bound)} end; -ms_clause_list(Line,[],_) -> +ms_clause_list(Line,[],_,_) -> {nil,Line}. -ms_clause({clause, Line, Parameters, Guards, Body},Type) -> +ms_clause({clause, Line, Parameters, Guards, Body},Type,Bound) -> check_type(Line,Parameters,Type), - {MSHead,Bindings} = transform_head(Parameters), + {MSHead,Bindings} = transform_head(Parameters,Bound), MSGuards = transform_guards(Line, Guards, Bindings), MSBody = transform_body(Line,Body,Bindings), {tuple, Line, [MSHead,MSGuards,MSBody]}. @@ -627,29 +711,31 @@ tg(Other,B) -> Element = io_lib:format("unknown element ~w", [Other]), throw({error,unknown,{?ERR_GENELEMENT+B#tgd.eb,Element}}). -transform_head([V]) -> +transform_head([V],OuterBound) -> Bind = cre_bind(), - {NewV,NewBind} = toplevel_head_match(V,Bind), - th(NewV,NewBind). + {NewV,NewBind} = toplevel_head_match(V,Bind,OuterBound), + th(NewV,NewBind,OuterBound). -toplevel_head_match({match,_,{var,_,VName},Expr},B) -> +toplevel_head_match({match,Line,{var,_,VName},Expr},B,OB) -> + warn_var_clash(Line,VName,OB), {Expr,new_bind({VName,'$_'},B)}; -toplevel_head_match({match,_,Expr,{var,_,VName}},B) -> +toplevel_head_match({match,Line,Expr,{var,_,VName}},B,OB) -> + warn_var_clash(Line,VName,OB), {Expr,new_bind({VName,'$_'},B)}; -toplevel_head_match(Other,B) -> +toplevel_head_match(Other,B,_OB) -> {Other,B}. -th({record,Line,RName,RFields},B) -> +th({record,Line,RName,RFields},B,OB) -> % youch... RDefs = get_records(), {KeyList0,NewB} = lists:foldl(fun({record_field,_,{atom,_,Key},Value}, {L,B0}) -> - {NV,B1} = th(Value,B0), + {NV,B1} = th(Value,B0,OB), {[{Key,NV}|L],B1}; ({record_field,_,{var,_,'_'},Value}, {L,B0}) -> - {NV,B1} = th(Value,B0), + {NV,B1} = th(Value,B0,OB), {[{{default},NV}|L],B1}; (_,_) -> throw({error,Line,{?ERR_HEADBADREC, @@ -692,9 +778,9 @@ th({record,Line,RName,RFields},B) -> _ -> throw({error,Line,{?ERR_HEADBADREC,RName}}) end; -th({match,Line,_,_},_) -> +th({match,Line,_,_},_,_) -> throw({error,Line,?ERR_HEADMATCH}); -th({atom,Line,A},B) -> +th({atom,Line,A},B,_OB) -> case atom_to_list(A) of [$$|NL] -> case (catch list_to_integer(NL)) of @@ -706,10 +792,11 @@ th({atom,Line,A},B) -> _ -> {{atom,Line,A},B} end; -th({bin_element,_Line0,{var, Line, A},_,_},_) -> +th({bin_element,_Line0,{var, Line, A},_,_},_,_) -> throw({error,Line,{?ERR_HEADBINMATCH,A}}); -th({var,Line,Name},B) -> +th({var,Line,Name},B,OB) -> + warn_var_clash(Line,Name,OB), case lkup_bind(Name,B) of undefined -> NewB = new_bind(Name,B), @@ -717,16 +804,24 @@ th({var,Line,Name},B) -> Trans -> {{atom,Line,Trans},B} end; -th([H|T],B) -> - {NH,NB} = th(H,B), - {NT,NNB} = th(T,NB), +th([H|T],B,OB) -> + {NH,NB} = th(H,B,OB), + {NT,NNB} = th(T,NB,OB), {[NH|NT],NNB}; -th(T,B) when is_tuple(T) -> - {L,NB} = th(tuple_to_list(T),B), +th(T,B,OB) when is_tuple(T) -> + {L,NB} = th(tuple_to_list(T),B,OB), {list_to_tuple(L),NB}; -th(Nonstruct,B) -> +th(Nonstruct,B,_OB) -> {Nonstruct,B}. +warn_var_clash(Line,Name,OuterBound) -> + case gb_sets:is_member(Name,OuterBound) of + true -> + add_warning(Line,{?WARN_SHADOW_VAR,Name}); + _ -> + ok + end. + %% Could be more efficient... check_multi_field(_, _, [], _) -> ok; diff --git a/lib/stdlib/src/orddict.erl b/lib/stdlib/src/orddict.erl index c7b52b933e..45d3c84b3e 100644 --- a/lib/stdlib/src/orddict.erl +++ b/lib/stdlib/src/orddict.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. 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,9 +25,11 @@ -export([store/3,append/3,append_list/3,update/3,update/4,update_counter/3]). -export([fold/3,map/2,filter/2,merge/3]). +-export_type([orddict/0]). + %%--------------------------------------------------------------------------- --type orddict() :: [{term(), term()}]. +-type orddict() :: [{Key :: term(), Value :: term()}]. %%--------------------------------------------------------------------------- @@ -35,45 +37,63 @@ new() -> []. --spec is_key(Key::term(), Dictionary::orddict()) -> boolean(). +-spec is_key(Key, Orddict) -> boolean() when + Key :: term(), + Orddict :: orddict(). is_key(Key, [{K,_}|_]) when Key < K -> false; is_key(Key, [{K,_}|Dict]) when Key > K -> is_key(Key, Dict); is_key(_Key, [{_K,_Val}|_]) -> true; %Key == K is_key(_, []) -> false. --spec to_list(orddict()) -> [{term(), term()}]. +-spec to_list(Orddict) -> List when + Orddict :: orddict(), + List :: [{Key :: term(), Value :: term()}]. to_list(Dict) -> Dict. --spec from_list([{term(), term()}]) -> orddict(). +-spec from_list(List) -> Orddict when + List :: [{Key :: term(), Value :: term()}], + Orddict :: orddict(). from_list(Pairs) -> lists:foldl(fun ({K,V}, D) -> store(K, V, D) end, [], Pairs). --spec size(orddict()) -> non_neg_integer(). +-spec size(Orddict) -> non_neg_integer() when + Orddict :: orddict(). size(D) -> length(D). --spec fetch(Key::term(), Dictionary::orddict()) -> term(). +-spec fetch(Key, Orddict) -> Value when + Key :: term(), + Value :: term(), + Orddict :: orddict(). fetch(Key, [{K,_}|D]) when Key > K -> fetch(Key, D); fetch(Key, [{K,Value}|_]) when Key == K -> Value. --spec find(Key::term(), Dictionary::orddict()) -> {'ok', term()} | 'error'. +-spec find(Key, Orddict) -> {'ok', Value} | 'error' when + Key :: term(), + Orddict :: orddict(), + Value :: term(). find(Key, [{K,_}|_]) when Key < K -> error; find(Key, [{K,_}|D]) when Key > K -> find(Key, D); find(_Key, [{_K,Value}|_]) -> {ok,Value}; %Key == K find(_, []) -> error. --spec fetch_keys(Dictionary::orddict()) -> [term()]. +-spec fetch_keys(Orddict) -> Keys when + Orddict :: orddict(), + Keys :: [term()]. fetch_keys([{Key,_}|Dict]) -> [Key|fetch_keys(Dict)]; fetch_keys([]) -> []. --spec erase(Key::term(), Dictionary::orddict()) -> orddict(). +-spec erase(Key, Orddict1) -> Orddict2 when + Key :: term(), + Orddict1 :: orddict(), + Orddict2 :: orddict(). erase(Key, [{K,_}=E|Dict]) when Key < K -> [E|Dict]; erase(Key, [{K,_}=E|Dict]) when Key > K -> @@ -81,7 +101,11 @@ erase(Key, [{K,_}=E|Dict]) when Key > K -> erase(_Key, [{_K,_Val}|Dict]) -> Dict; %Key == K erase(_, []) -> []. --spec store(Key::term(), Value::term(), Dictionary::orddict()) -> orddict(). +-spec store(Key, Value, Orddict1) -> Orddict2 when + Key :: term(), + Value :: term(), + Orddict1 :: orddict(), + Orddict2 :: orddict(). store(Key, New, [{K,_}=E|Dict]) when Key < K -> [{Key,New},E|Dict]; @@ -91,7 +115,11 @@ store(Key, New, [{_K,_Old}|Dict]) -> %Key == K [{Key,New}|Dict]; store(Key, New, []) -> [{Key,New}]. --spec append(Key::term(), Value::term(), Dictionary::orddict()) -> orddict(). +-spec append(Key, Value, Orddict1) -> Orddict2 when + Key :: term(), + Value :: term(), + Orddict1 :: orddict(), + Orddict2 :: orddict(). append(Key, New, [{K,_}=E|Dict]) when Key < K -> [{Key,[New]},E|Dict]; @@ -101,7 +129,11 @@ append(Key, New, [{_K,Old}|Dict]) -> %Key == K [{Key,Old ++ [New]}|Dict]; append(Key, New, []) -> [{Key,[New]}]. --spec append_list(Key::term(), ValueList::[term()], orddict()) -> orddict(). +-spec append_list(Key, ValList, Orddict1) -> Orddict2 when + Key :: term(), + ValList :: [Value :: term()], + Orddict1 :: orddict(), + Orddict2 :: orddict(). append_list(Key, NewList, [{K,_}=E|Dict]) when Key < K -> [{Key,NewList},E|Dict]; @@ -112,14 +144,23 @@ append_list(Key, NewList, [{_K,Old}|Dict]) -> %Key == K append_list(Key, NewList, []) -> [{Key,NewList}]. --spec update(Key::term(), Fun::fun((term()) -> term()), orddict()) -> orddict(). +-spec update(Key, Fun, Orddict1) -> Orddict2 when + Key :: term(), + Fun :: fun((Value1 :: term()) -> Value2 :: term()), + Orddict1 :: orddict(), + Orddict2 :: orddict(). update(Key, Fun, [{K,_}=E|Dict]) when Key > K -> [E|update(Key, Fun, Dict)]; update(Key, Fun, [{K,Val}|Dict]) when Key == K -> [{Key,Fun(Val)}|Dict]. --spec update(term(), fun((term()) -> term()), term(), orddict()) -> orddict(). +-spec update(Key, Fun, Initial, Orddict1) -> Orddict2 when + Key :: term(), + Initial :: term(), + Fun :: fun((Value1 :: term()) -> Value2 :: term()), + Orddict1 :: orddict(), + Orddict2 :: orddict(). update(Key, _, Init, [{K,_}=E|Dict]) when Key < K -> [{Key,Init},E|Dict]; @@ -129,7 +170,11 @@ update(Key, Fun, _Init, [{_K,Val}|Dict]) -> %Key == K [{Key,Fun(Val)}|Dict]; update(Key, _, Init, []) -> [{Key,Init}]. --spec update_counter(Key::term(), Incr::number(), orddict()) -> orddict(). +-spec update_counter(Key, Increment, Orddict1) -> Orddict2 when + Key :: term(), + Increment :: number(), + Orddict1 :: orddict(), + Orddict2 :: orddict(). update_counter(Key, Incr, [{K,_}=E|Dict]) when Key < K -> [{Key,Incr},E|Dict]; @@ -139,19 +184,29 @@ update_counter(Key, Incr, [{_K,Val}|Dict]) -> %Key == K [{Key,Val+Incr}|Dict]; update_counter(Key, Incr, []) -> [{Key,Incr}]. --spec fold(fun((term(),term(),term()) -> term()), term(), orddict()) -> term(). +-spec fold(Fun, Acc0, Orddict) -> Acc1 when + Fun :: fun((Key :: term(), Value :: term(), AccIn :: term()) -> AccOut :: term()), + Acc0 :: term(), + Acc1 :: term(), + Orddict :: orddict(). fold(F, Acc, [{Key,Val}|D]) -> fold(F, F(Key, Val, Acc), D); fold(F, Acc, []) when is_function(F, 3) -> Acc. --spec map(fun((term(), term()) -> term()), orddict()) -> orddict(). +-spec map(Fun, Orddict1) -> Orddict2 when + Fun :: fun((Key :: term(), Value1 :: term()) -> Value2 :: term()), + Orddict1 :: orddict(), + Orddict2 :: orddict(). map(F, [{Key,Val}|D]) -> [{Key,F(Key, Val)}|map(F, D)]; map(F, []) when is_function(F, 2) -> []. --spec filter(fun((term(), term()) -> term()), orddict()) -> orddict(). +-spec filter(Pred, Orddict1) -> Orddict2 when + Pred :: fun((Key :: term(), Value :: term()) -> boolean()), + Orddict1 :: orddict(), + Orddict2 :: orddict(). filter(F, [{Key,Val}=E|D]) -> case F(Key, Val) of @@ -160,8 +215,11 @@ filter(F, [{Key,Val}=E|D]) -> end; filter(F, []) when is_function(F, 2) -> []. --spec merge(fun((term(), term(), term()) -> term()), orddict(), orddict()) -> - orddict(). +-spec merge(Fun, Orddict1, Orddict2) -> Orddict3 when + Fun :: fun((Key :: term(), Value1 :: term(), Value2 :: term()) -> Value :: term()), + Orddict1 :: orddict(), + Orddict2 :: orddict(), + Orddict3 :: orddict(). merge(F, [{K1,_}=E1|D1], [{K2,_}=E2|D2]) when K1 < K2 -> [E1|merge(F, D1, [E2|D2])]; diff --git a/lib/stdlib/src/ordsets.erl b/lib/stdlib/src/ordsets.erl index 05041c15f1..4a8b1275b2 100644 --- a/lib/stdlib/src/ordsets.erl +++ b/lib/stdlib/src/ordsets.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. 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,19 +26,22 @@ -export([subtract/2,is_subset/2]). -export([fold/3,filter/2]). +-export_type([ordset/1]). + -type ordset(T) :: [T]. %% new() -> Set. %% Return a new empty ordered set. --spec new() -> ordset(term()). +-spec new() -> []. new() -> []. %% is_set(Term) -> boolean(). %% Return 'true' if Set is an ordered set of elements, else 'false'. --spec is_set(term()) -> boolean(). +-spec is_set(Ordset) -> boolean() when + Ordset :: term(). is_set([E|Es]) -> is_set(Es, E); is_set([]) -> true; @@ -52,21 +55,26 @@ is_set([], _) -> true. %% size(OrdSet) -> int(). %% Return the number of elements in OrdSet. --spec size(ordset(_)) -> non_neg_integer(). +-spec size(Ordset) -> non_neg_integer() when + Ordset :: ordset(_). size(S) -> length(S). %% to_list(OrdSet) -> [Elem]. %% Return the elements in OrdSet as a list. --spec to_list(ordset(T)) -> [T]. +-spec to_list(Ordset) -> List when + Ordset :: ordset(T), + List :: [T]. to_list(S) -> S. %% from_list([Elem]) -> Set. %% Build an ordered set from the elements in List. --spec from_list([T]) -> ordset(T). +-spec from_list(List) -> Ordset when + List :: [T], + Ordset :: ordset(T). from_list(L) -> lists:usort(L). @@ -74,7 +82,9 @@ from_list(L) -> %% is_element(Element, OrdSet) -> boolean(). %% Return 'true' if Element is an element of OrdSet, else 'false'. --spec is_element(term(), ordset(_)) -> boolean(). +-spec is_element(Element, Ordset) -> boolean() when + Element :: term(), + Ordset :: ordset(_). is_element(E, [H|Es]) when E > H -> is_element(E, Es); is_element(E, [H|_]) when E < H -> false; @@ -84,7 +94,12 @@ is_element(_, []) -> false. %% add_element(Element, OrdSet) -> OrdSet. %% Return OrdSet with Element inserted in it. --spec add_element(term(), ordset(_)) -> ordset(_). +-spec add_element(Element, Ordset1) -> Ordset2 when + Element :: E, + Ordset1 :: ordset(T), + Ordset2 :: ordset(T | E). + +%-spec add_element(E, ordset(T)) -> [T | E,...]. add_element(E, [H|Es]) when E > H -> [H|add_element(E, Es)]; add_element(E, [H|_]=Set) when E < H -> [E|Set]; @@ -94,7 +109,10 @@ add_element(E, []) -> [E]. %% del_element(Element, OrdSet) -> OrdSet. %% Return OrdSet but with Element removed. --spec del_element(term(), ordset(_)) -> ordset(_). +-spec del_element(Element, Ordset1) -> Ordset2 when + Element :: term(), + Ordset1 :: ordset(T), + Ordset2 :: ordset(T). del_element(E, [H|Es]) when E > H -> [H|del_element(E, Es)]; del_element(E, [H|_]=Set) when E < H -> Set; @@ -104,7 +122,10 @@ del_element(_, []) -> []. %% union(OrdSet1, OrdSet2) -> OrdSet %% Return the union of OrdSet1 and OrdSet2. --spec union(ordset(_), ordset(_)) -> ordset(_). +-spec union(Ordset1, Ordset2) -> Ordset3 when + Ordset1 :: ordset(T1), + Ordset2 :: ordset(T2), + Ordset3 :: ordset(T1 | T2). union([E1|Es1], [E2|_]=Set2) when E1 < E2 -> [E1|union(Es1, Set2)]; @@ -118,7 +139,9 @@ union(Es1, []) -> Es1. %% union([OrdSet]) -> OrdSet %% Return the union of the list of ordered sets. --spec union([ordset(_)]) -> ordset(_). +-spec union(OrdsetList) -> Ordset when + OrdsetList :: [ordset(T)], + Ordset :: ordset(T). union([S1,S2|Ss]) -> union1(union(S1, S2), Ss); @@ -131,7 +154,10 @@ union1(S1, []) -> S1. %% intersection(OrdSet1, OrdSet2) -> OrdSet. %% Return the intersection of OrdSet1 and OrdSet2. --spec intersection(ordset(_), ordset(_)) -> ordset(_). +-spec intersection(Ordset1, Ordset2) -> Ordset3 when + Ordset1 :: ordset(_), + Ordset2 :: ordset(_), + Ordset3 :: ordset(_). intersection([E1|Es1], [E2|_]=Set2) when E1 < E2 -> intersection(Es1, Set2); @@ -147,7 +173,9 @@ intersection(_, []) -> %% intersection([OrdSet]) -> OrdSet. %% Return the intersection of the list of ordered sets. --spec intersection([ordset(_)]) -> ordset(_). +-spec intersection(OrdsetList) -> Ordset when + OrdsetList :: [ordset(_),...], + Ordset :: ordset(_). intersection([S1,S2|Ss]) -> intersection1(intersection(S1, S2), Ss); @@ -160,7 +188,9 @@ intersection1(S1, []) -> S1. %% is_disjoint(OrdSet1, OrdSet2) -> boolean(). %% Check whether OrdSet1 and OrdSet2 are disjoint. --spec is_disjoint(ordset(_), ordset(_)) -> boolean(). +-spec is_disjoint(Ordset1, Ordset2) -> boolean() when + Ordset1 :: ordset(_), + Ordset2 :: ordset(_). is_disjoint([E1|Es1], [E2|_]=Set2) when E1 < E2 -> is_disjoint(Es1, Set2); @@ -177,7 +207,10 @@ is_disjoint(_, []) -> %% Return all and only the elements of OrdSet1 which are not also in %% OrdSet2. --spec subtract(ordset(_), ordset(_)) -> ordset(_). +-spec subtract(Ordset1, Ordset2) -> Ordset3 when + Ordset1 :: ordset(_), + Ordset2 :: ordset(_), + Ordset3 :: ordset(_). subtract([E1|Es1], [E2|_]=Set2) when E1 < E2 -> [E1|subtract(Es1, Set2)]; @@ -192,7 +225,9 @@ subtract(Es1, []) -> Es1. %% Return 'true' when every element of OrdSet1 is also a member of %% OrdSet2, else 'false'. --spec is_subset(ordset(_), ordset(_)) -> boolean(). +-spec is_subset(Ordset1, Ordset2) -> boolean() when + Ordset1 :: ordset(_), + Ordset2 :: ordset(_). is_subset([E1|_], [E2|_]) when E1 < E2 -> %E1 not in Set2 false; @@ -206,7 +241,11 @@ is_subset(_, []) -> false. %% fold(Fun, Accumulator, OrdSet) -> Accumulator. %% Fold function Fun over all elements in OrdSet and return Accumulator. --spec fold(fun((_, _) -> _), _, ordset(_)) -> _. +-spec fold(Function, Acc0, Ordset) -> Acc1 when + Function :: fun((Element :: T, AccIn :: term()) -> AccOut :: term()), + Ordset :: ordset(T), + Acc0 :: term(), + Acc1 :: term(). fold(F, Acc, Set) -> lists:foldl(F, Acc, Set). @@ -214,7 +253,10 @@ fold(F, Acc, Set) -> %% filter(Fun, OrdSet) -> OrdSet. %% Filter OrdSet with Fun. --spec filter(fun((_) -> boolean()), ordset(_)) -> ordset(_). +-spec filter(Pred, Ordset1) -> Ordset2 when + Pred :: fun((Element :: T) -> boolean()), + Ordset1 :: ordset(T), + Ordset2 :: ordset(T). filter(F, Set) -> lists:filter(F, Set). diff --git a/lib/stdlib/src/otp_internal.erl b/lib/stdlib/src/otp_internal.erl index 7ea7de8d58..39d017d430 100644 --- a/lib/stdlib/src/otp_internal.erl +++ b/lib/stdlib/src/otp_internal.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1999-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1999-2011. 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(otp_internal). @@ -180,6 +180,9 @@ obsolete_1(calendar, local_time_to_universal_time, 1) -> obsolete_1(rpc, safe_multi_server_call, A) when A =:= 2; A =:= 3 -> {deprecated, {rpc, multi_server_call, A}}; + +%% *** SNMP *** + obsolete_1(snmp, N, A) -> case is_snmp_agent_function(N, A) of false -> @@ -189,9 +192,100 @@ obsolete_1(snmp, N, A) -> integer_to_list(A)++" instead"} end; +obsolete_1(snmpm, agent_info, 3) -> + {deprecated, {snmpm, agent_info, 2}, "R16B"}; +obsolete_1(snmpm, update_agent_info, 5) -> + {deprecated, {snmpm, update_agent_info, 4}, "R16B"}; +obsolete_1(snmpm, g, 3) -> + {deprecated, {snmpm, sync_get, 3}, "R16B"}; +obsolete_1(snmpm, g, 4) -> + {deprecated, {snmpm, sync_get, [3,4]}, "R16B"}; +obsolete_1(snmpm, g, 5) -> + {deprecated, {snmpm, sync_get, [4,5]}, "R16B"}; +obsolete_1(snmpm, g, 6) -> + {deprecated, {snmpm, sync_get, [5,6]}, "R16B"}; +obsolete_1(snmpm, g, 7) -> + {deprecated, {snmpm, sync_get, 6}, "R16B"}; +obsolete_1(snmpm, ag, 3) -> + {deprecated, {snmpm, async_get, 3}, "R16B"}; +obsolete_1(snmpm, ag, 4) -> + {deprecated, {snmpm, async_get, [3,4]}, "R16B"}; +obsolete_1(snmpm, ag, 5) -> + {deprecated, {snmpm, async_get, [4,5]}, "R16B"}; +obsolete_1(snmpm, ag, 6) -> + {deprecated, {snmpm, async_get, [5,6]}, "R16B"}; +obsolete_1(snmpm, ag, 7) -> + {deprecated, {snmpm, async_get, 6}, "R16B"}; +obsolete_1(snmpm, gn, 3) -> + {deprecated, {snmpm, sync_get_next, 3}, "R16B"}; +obsolete_1(snmpm, gn, 4) -> + {deprecated, {snmpm, sync_get_next, [3,4]}, "R16B"}; +obsolete_1(snmpm, gn, 5) -> + {deprecated, {snmpm, sync_get_next, [4,5]}, "R16B"}; +obsolete_1(snmpm, gn, 6) -> + {deprecated, {snmpm, sync_get_next, [5,6]}, "R16B"}; +obsolete_1(snmpm, gn, 7) -> + {deprecated, {snmpm, sync_get_next, 6}, "R16B"}; +obsolete_1(snmpm, agn, 3) -> + {deprecated, {snmpm, async_get_next, 3}, "R16B"}; +obsolete_1(snmpm, agn, 4) -> + {deprecated, {snmpm, async_get_next, [3,4]}, "R16B"}; +obsolete_1(snmpm, agn, 5) -> + {deprecated, {snmpm, async_get_next, [4,5]}, "R16B"}; +obsolete_1(snmpm, agn, 6) -> + {deprecated, {snmpm, async_get_next, [5,6]}, "R16B"}; +obsolete_1(snmpm, agn, 7) -> + {deprecated, {snmpm, async_get_next, 6}, "R16B"}; +obsolete_1(snmpm, s, 3) -> + {deprecated, {snmpm, sync_set, 3}, "R16B"}; +obsolete_1(snmpm, s, 4) -> + {deprecated, {snmpm, sync_set, [3,4]}, "R16B"}; +obsolete_1(snmpm, s, 5) -> + {deprecated, {snmpm, sync_set, [4,5]}, "R16B"}; +obsolete_1(snmpm, s, 6) -> + {deprecated, {snmpm, sync_set, [5,6]}, "R16B"}; +obsolete_1(snmpm, s, 7) -> + {deprecated, {snmpm, sync_set, 6}, "R16B"}; +obsolete_1(snmpm, as, 3) -> + {deprecated, {snmpm, async_set, 3}, "R16B"}; +obsolete_1(snmpm, as, 4) -> + {deprecated, {snmpm, async_set, [3,4]}, "R16B"}; +obsolete_1(snmpm, as, 5) -> + {deprecated, {snmpm, async_set, [4,5]}, "R16B"}; +obsolete_1(snmpm, as, 6) -> + {deprecated, {snmpm, async_set, [5,6]}, "R16B"}; +obsolete_1(snmpm, as, 7) -> + {deprecated, {snmpm, async_set, 6}, "R16B"}; +obsolete_1(snmpm, gb, 5) -> + {deprecated, {snmpm, sync_get_bulk, 5}, "R16B"}; +obsolete_1(snmpm, gb, 6) -> + {deprecated, {snmpm, sync_get_bulk, [5,6]}, "R16B"}; +obsolete_1(snmpm, gb, 7) -> + {deprecated, {snmpm, sync_get_bulk, [6,7]}, "R16B"}; +obsolete_1(snmpm, gb, 8) -> + {deprecated, {snmpm, sync_get_bulk, [7,8]}, "R16B"}; +obsolete_1(snmpm, gb, 9) -> + {deprecated, {snmpm, sync_get_bulk, 8}, "R16B"}; +obsolete_1(snmpm, agb, 5) -> + {deprecated, {snmpm, async_get_bulk, 5}, "R16B"}; +obsolete_1(snmpm, agb, 6) -> + {deprecated, {snmpm, async_get_bulk, [5,6]}, "R16B"}; +obsolete_1(snmpm, agb, 7) -> + {deprecated, {snmpm, async_get_bulk, [6,7]}, "R16B"}; +obsolete_1(snmpm, agb, 8) -> + {deprecated, {snmpm, async_get_bulk, [7,8]}, "R16B"}; +obsolete_1(snmpm, agb, 9) -> + {deprecated, {snmpm, async_get_bulk, 8}, "R16B"}; + + +%% *** MEGACO *** + obsolete_1(megaco, format_versions, 1) -> {deprecated, "Deprecated; use megaco:print_version_info/0,1 instead"}; + +%% *** OS-MON-MIB *** + obsolete_1(os_mon_mib, init, 1) -> {deprecated, {os_mon_mib, load, 1}}; obsolete_1(os_mon_mib, stop, 1) -> @@ -236,109 +330,137 @@ obsolete_1(erlang, fault, 2) -> obsolete_1(file, rawopen, 2) -> {removed, "deprecated (will be removed in R13B); use file:open/2 with the raw option"}; -obsolete_1(httpd, start, 0) -> {deprecated,{inets,start,[2,3]},"R14B"}; -obsolete_1(httpd, start, 1) -> {deprecated,{inets,start,[2,3]},"R14B"}; -obsolete_1(httpd, start_link, 1) -> {deprecated,{inets,start,[2,3]},"R14B"}; -obsolete_1(httpd, start_child, 0) -> {deprecated,{inets,start,[2,3]},"R14B"}; -obsolete_1(httpd, start_child, 1) -> {deprecated,{inets,start,[2,3]},"R14B"}; -obsolete_1(httpd, stop, 0) -> {deprecated,{inets,stop,2},"R14B"}; -obsolete_1(httpd, stop, 1) -> {deprecated,{inets,stop,2},"R14B"}; -obsolete_1(httpd, stop, 2) -> {deprecated,{inets,stop,2},"R14B"}; -obsolete_1(httpd, stop_child, 0) -> {deprecated,{inets,stop,2},"R14B"}; -obsolete_1(httpd, stop_child, 1) -> {deprecated,{inets,stop,2},"R14B"}; -obsolete_1(httpd, stop_child, 2) -> {deprecated,{inets,stop,2},"R14B"}; -obsolete_1(httpd, restart, 0) -> {deprecated,{httpd,reload_config,2},"R14B"}; -obsolete_1(httpd, restart, 1) -> {deprecated,{httpd,reload_config,2},"R14B"}; -obsolete_1(httpd, restart, 2) -> {deprecated,{httpd,reload_config,2},"R14B"}; -obsolete_1(httpd, block, 0) -> {deprecated,{httpd,reload_config,2},"R14B"}; -obsolete_1(httpd, block, 1) -> {deprecated,{httpd,reload_config,2},"R14B"}; -obsolete_1(httpd, block, 2) -> {deprecated,{httpd,reload_config,2},"R14B"}; -obsolete_1(httpd, block, 3) -> {deprecated,{httpd,reload_config,2},"R14B"}; -obsolete_1(httpd, block, 4) -> {deprecated,{httpd,reload_config,2},"R14B"}; -obsolete_1(httpd, unblock, 0) -> {deprecated,{httpd,reload_config,2},"R14B"}; -obsolete_1(httpd, unblock, 1) -> {deprecated,{httpd,reload_config,2},"R14B"}; -obsolete_1(httpd, unblock, 2) -> {deprecated,{httpd,reload_config,2},"R14B"}; +obsolete_1(http, request, 1) -> {deprecated,{httpc,request,1},"R15B"}; +obsolete_1(http, request, 2) -> {deprecated,{httpc,request,2},"R15B"}; +obsolete_1(http, request, 4) -> {deprecated,{httpc,request,4},"R15B"}; +obsolete_1(http, request, 5) -> {deprecated,{httpc,request,5},"R15B"}; +obsolete_1(http, cancel_request, 1) -> {deprecated,{httpc,cancel_request,1},"R15B"}; +obsolete_1(http, cancel_request, 2) -> {deprecated,{httpc,cancel_request,2},"R15B"}; +obsolete_1(http, set_option, 2) -> {deprecated,{httpc,set_option,2},"R15B"}; +obsolete_1(http, set_option, 3) -> {deprecated,{httpc,set_option,3},"R15B"}; +obsolete_1(http, set_options, 1) -> {deprecated,{httpc,set_options,1},"R15B"}; +obsolete_1(http, set_options, 2) -> {deprecated,{httpc,set_options,2},"R15B"}; +obsolete_1(http, verify_cookies, 2) -> {deprecated,{httpc,verify_cookies,2},"R15B"}; +obsolete_1(http, verify_cookies, 3) -> {deprecated,{httpc,verify_cookies,3},"R15B"}; +obsolete_1(http, cookie_header, 1) -> {deprecated,{httpc,cookie_header,1},"R15B"}; +obsolete_1(http, cookie_header, 2) -> {deprecated,{httpc,cookie_header,2},"R15B"}; +obsolete_1(http, stream_next, 1) -> {deprecated,{httpc,stream_next,1},"R15B"}; +obsolete_1(http, default_profile, 0) -> {deprecated,{httpc,default_profile,0},"R15B"}; + +obsolete_1(httpd, start, 0) -> {removed,{inets,start,[2,3]},"R14B"}; +obsolete_1(httpd, start, 1) -> {removed,{inets,start,[2,3]},"R14B"}; +obsolete_1(httpd, start_link, 0) -> {removed,{inets,start,[2,3]},"R14B"}; +obsolete_1(httpd, start_link, 1) -> {removed,{inets,start,[2,3]},"R14B"}; +obsolete_1(httpd, start_child, 0) -> {removed,{inets,start,[2,3]},"R14B"}; +obsolete_1(httpd, start_child, 1) -> {removed,{inets,start,[2,3]},"R14B"}; +obsolete_1(httpd, stop, 0) -> {removed,{inets,stop,2},"R14B"}; +obsolete_1(httpd, stop, 1) -> {removed,{inets,stop,2},"R14B"}; +obsolete_1(httpd, stop, 2) -> {removed,{inets,stop,2},"R14B"}; +obsolete_1(httpd, stop_child, 0) -> {removed,{inets,stop,2},"R14B"}; +obsolete_1(httpd, stop_child, 1) -> {removed,{inets,stop,2},"R14B"}; +obsolete_1(httpd, stop_child, 2) -> {removed,{inets,stop,2},"R14B"}; +obsolete_1(httpd, restart, 0) -> {removed,{httpd,reload_config,2},"R14B"}; +obsolete_1(httpd, restart, 1) -> {removed,{httpd,reload_config,2},"R14B"}; +obsolete_1(httpd, restart, 2) -> {removed,{httpd,reload_config,2},"R14B"}; +obsolete_1(httpd, block, 0) -> {removed,{httpd,reload_config,2},"R14B"}; +obsolete_1(httpd, block, 1) -> {removed,{httpd,reload_config,2},"R14B"}; +obsolete_1(httpd, block, 2) -> {removed,{httpd,reload_config,2},"R14B"}; +obsolete_1(httpd, block, 3) -> {removed,{httpd,reload_config,2},"R14B"}; +obsolete_1(httpd, block, 4) -> {removed,{httpd,reload_config,2},"R14B"}; +obsolete_1(httpd, unblock, 0) -> {removed,{httpd,reload_config,2},"R14B"}; +obsolete_1(httpd, unblock, 1) -> {removed,{httpd,reload_config,2},"R14B"}; +obsolete_1(httpd, unblock, 2) -> {removed,{httpd,reload_config,2},"R14B"}; obsolete_1(httpd_util, key1search, 2) -> {removed,{proplists,get_value,2},"R13B"}; obsolete_1(httpd_util, key1search, 3) -> {removed,{proplists,get_value,3},"R13B"}; -obsolete_1(ftp, open, 3) -> {deprecated,{inets,start,[2,3]},"R14B"}; -obsolete_1(ftp, force_active, 1) -> {deprecated,{inets,start,[2,3]},"R14B"}; +obsolete_1(ftp, open, 3) -> {removed,{inets,start,[2,3]},"R14B"}; +obsolete_1(ftp, force_active, 1) -> {removed,{inets,start,[2,3]},"R14B"}; %% Added in R12B-4. obsolete_1(ssh_cm, connect, A) when 1 =< A, A =< 3 -> - {deprecated,{ssh,connect,A},"R14B"}; + {removed,{ssh,connect,A},"R14B"}; obsolete_1(ssh_cm, listen, A) when 2 =< A, A =< 4 -> - {deprecated,{ssh,daemon,A},"R14B"}; + {removed,{ssh,daemon,A},"R14B"}; obsolete_1(ssh_cm, stop_listener, 1) -> - {deprecated,{ssh,stop_listener,[1,2]},"R14B"}; + {removed,{ssh,stop_listener,[1,2]},"R14B"}; obsolete_1(ssh_cm, session_open, A) when A =:= 2; A =:= 4 -> - {deprecated,{ssh_connection,session_channel,A},"R14B"}; + {removed,{ssh_connection,session_channel,A},"R14B"}; obsolete_1(ssh_cm, direct_tcpip, A) when A =:= 6; A =:= 8 -> - {deprecated,{ssh_connection,direct_tcpip,A}}; + {removed,{ssh_connection,direct_tcpip,A}}; obsolete_1(ssh_cm, tcpip_forward, 3) -> - {deprecated,{ssh_connection,tcpip_forward,3},"R14B"}; + {removed,{ssh_connection,tcpip_forward,3},"R14B"}; obsolete_1(ssh_cm, cancel_tcpip_forward, 3) -> - {deprecated,{ssh_connection,cancel_tcpip_forward,3},"R14B"}; + {removed,{ssh_connection,cancel_tcpip_forward,3},"R14B"}; obsolete_1(ssh_cm, open_pty, A) when A =:= 3; A =:= 7; A =:= 9 -> - {deprecated,{ssh_connection,open_pty,A},"R14"}; + {removed,{ssh_connection,open_pty,A},"R14"}; obsolete_1(ssh_cm, setenv, 5) -> - {deprecated,{ssh_connection,setenv,5},"R14B"}; + {removed,{ssh_connection,setenv,5},"R14B"}; obsolete_1(ssh_cm, shell, 2) -> - {deprecated,{ssh_connection,shell,2},"R14B"}; + {removed,{ssh_connection,shell,2},"R14B"}; obsolete_1(ssh_cm, exec, 4) -> - {deprecated,{ssh_connection,exec,4},"R14B"}; + {removed,{ssh_connection,exec,4},"R14B"}; obsolete_1(ssh_cm, subsystem, 4) -> - {deprecated,{ssh_connection,subsystem,4},"R14B"}; + {removed,{ssh_connection,subsystem,4},"R14B"}; obsolete_1(ssh_cm, winch, A) when A =:= 4; A =:= 6 -> - {deprecated,{ssh_connection,window_change,A},"R14B"}; + {removed,{ssh_connection,window_change,A},"R14B"}; obsolete_1(ssh_cm, signal, 3) -> - {deprecated,{ssh_connection,signal,3},"R14B"}; + {removed,{ssh_connection,signal,3},"R14B"}; obsolete_1(ssh_cm, attach, A) when A =:= 2; A =:= 3 -> - {deprecated,{ssh,attach,A}}; + {removed,{ssh,attach,A}}; obsolete_1(ssh_cm, detach, 2) -> - {deprecated,"no longer useful; will be removed in R14B"}; + {removed,"no longer useful; will be removed in R14B"}; obsolete_1(ssh_cm, set_user_ack, 4) -> - {deprecated,"no longer useful; will be removed in R14B"}; + {removed,"no longer useful; will be removed in R14B"}; obsolete_1(ssh_cm, adjust_window, 3) -> - {deprecated,{ssh_connection,adjust_window,3},"R14B"}; + {removed,{ssh_connection,adjust_window,3},"R14B"}; obsolete_1(ssh_cm, close, 2) -> - {deprecated,{ssh_connection,close,2},"R14B"}; + {removed,{ssh_connection,close,2},"R14B"}; obsolete_1(ssh_cm, stop, 1) -> - {deprecated,{ssh,close,1},"R14B"}; + {removed,{ssh,close,1},"R14B"}; obsolete_1(ssh_cm, send_eof, 2) -> - {deprecated,{ssh_connection,send_eof,2},"R14B"}; + {removed,{ssh_connection,send_eof,2},"R14B"}; obsolete_1(ssh_cm, send, A) when A =:= 3; A =:= 4 -> - {deprecated,{ssh_connection,send,A},"R14B"}; + {removed,{ssh_connection,send,A},"R14B"}; obsolete_1(ssh_cm, send_ack, A) when 3 =< A, A =< 5 -> - {deprecated,{ssh_connection,send,[3,4]},"R14B"}; + {removed,{ssh_connection,send,[3,4]},"R14B"}; obsolete_1(ssh_ssh, connect, A) when 1 =< A, A =< 3 -> - {deprecated,{ssh,shell,A},"R14B"}; + {removed,{ssh,shell,A},"R14B"}; obsolete_1(ssh_sshd, listen, A) when 0 =< A, A =< 3 -> - {deprecated,{ssh,daemon,[1,2,3]},"R14"}; + {removed,{ssh,daemon,[1,2,3]},"R14"}; obsolete_1(ssh_sshd, stop, 1) -> - {deprecated,{ssh,stop_listener,1}}; + {removed,{ssh,stop_listener,1}}; %% Added in R13A. obsolete_1(regexp, _, _) -> {deprecated, "the regexp module is deprecated (will be removed in R15A); use the re module instead"}; obsolete_1(lists, flat_length, 1) -> - {deprecated,{lists,flatlength,1},"R14"}; + {removed,{lists,flatlength,1},"R14"}; obsolete_1(ssh_sftp, connect, A) when 1 =< A, A =< 3 -> - {deprecated,{ssh_sftp,start_channel,A},"R14B"}; + {removed,{ssh_sftp,start_channel,A},"R14B"}; obsolete_1(ssh_sftp, stop, 1) -> - {deprecated,{ssh_sftp,stop_channel,1},"R14B"}; + {removed,{ssh_sftp,stop_channel,1},"R14B"}; %% Added in R13B01. obsolete_1(ssl_pkix, decode_cert_file, A) when A =:= 1; A =:= 2 -> - {deprecated,"deprecated (will be removed in R14B); use public_key:pem_to_der/1 and public_key:pkix_decode_cert/2 instead"}; + {removed,"removed in R14A; use public_key:pem_to_der/1 and public_key:pkix_decode_cert/2 instead"}; obsolete_1(ssl_pkix, decode_cert, A) when A =:= 1; A =:= 2 -> - {deprecated,{public_key,pkix_decode_cert,2},"R14B"}; + {removed,{public_key,pkix_decode_cert,2},"R14A"}; %% Added in R13B04. obsolete_1(erlang, concat_binary, 1) -> - {deprecated,{erlang,list_to_binary,1},"R14B"}; - + {deprecated,{erlang,list_to_binary,1},"R15B"}; + +%% Added in R14A. +obsolete_1(ssl, peercert, 2) -> + {deprecated,"deprecated (will be removed in R15A); use ssl:peercert/1 and public_key:pkix_decode_cert/2 instead"}; + +%% Added in R14B. +obsolete_1(public_key, pem_to_der, 1) -> + {deprecated,"deprecated (will be removed in R15A); use file:read_file/1 and public_key:pem_decode/1"}; +obsolete_1(public_key, decode_private_key, A) when A =:= 1; A =:= 2 -> + {deprecated,{public_key,pem_entry_decode,1},"R15A"}; + obsolete_1(_, _, _) -> no. diff --git a/lib/stdlib/src/pg.erl b/lib/stdlib/src/pg.erl index 503654e706..ee177e4e0b 100644 --- a/lib/stdlib/src/pg.erl +++ b/lib/stdlib/src/pg.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. 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,9 @@ %% Create a brand new empty process group with the master residing %% at the local node --spec create(term()) -> 'ok' | {'error', term()}. +-spec create(PgName) -> 'ok' | {'error', Reason} when + PgName :: term(), + Reason :: 'already_created' | term(). create(PgName) -> catch begin check(PgName), @@ -46,7 +48,10 @@ create(PgName) -> %% Create a brand new empty process group with the master %% residing at Node --spec create(term(), node()) -> 'ok' | {'error', term()}. +-spec create(PgName, Node) -> 'ok' | {'error', Reason} when + PgName :: term(), + Node :: node(), + Reason :: 'already_created' | term(). create(PgName, Node) -> catch begin check(PgName), @@ -66,7 +71,10 @@ standby(_PgName, _Node) -> %% Tell process group PgName that Pid is a new member of the group %% synchronously return a list of all old members in the group --spec join(atom(), pid()) -> [pid()]. +-spec join(PgName, Pid) -> Members when + PgName :: term(), + Pid :: pid(), + Members :: [pid()]. join(PgName, Pid) when is_atom(PgName) -> global:send(PgName, {join,self(),Pid}), @@ -77,7 +85,9 @@ join(PgName, Pid) when is_atom(PgName) -> %% Multi cast Mess to all members in the group --spec send(atom() | pid(), term()) -> 'ok'. +-spec send(PgName, Msg) -> 'ok' when + PgName :: term(), + Msg :: term(). send(PgName, Mess) when is_atom(PgName) -> global:send(PgName, {send, self(), Mess}), @@ -89,7 +99,9 @@ send(Pg, Mess) when is_pid(Pg) -> %% multi cast a message to all members in the group but ourselves %% If we are a member --spec esend(atom() | pid(), term()) -> 'ok'. +-spec esend(PgName, Msg) -> 'ok' when + PgName :: term(), + Msg :: term(). esend(PgName, Mess) when is_atom(PgName) -> global:send(PgName, {esend,self(),Mess}), @@ -100,7 +112,9 @@ esend(Pg, Mess) when is_pid(Pg) -> %% Return the members of the group --spec members(atom() | pid()) -> [pid()]. +-spec members(PgName) -> Members when + PgName :: term(), + Members :: [pid()]. members(PgName) when is_atom(PgName) -> global:send(PgName, {self() ,members}), diff --git a/lib/stdlib/src/pool.erl b/lib/stdlib/src/pool.erl index 7f5f23e26d..a5eb191ab2 100644 --- a/lib/stdlib/src/pool.erl +++ b/lib/stdlib/src/pool.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. 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 @@ -52,11 +52,16 @@ %% Start up using the .hosts.erlang file --spec start(atom()) -> [node()]. +-spec start(Name) -> Nodes when + Name :: atom(), + Nodes :: [node()]. start(Name) -> start(Name,[]). --spec start(atom(), string()) -> [node()]. +-spec start(Name, Args) -> Nodes when + Name :: atom(), + Args :: string(), + Nodes :: [node()]. start(Name, Args) when is_atom(Name) -> gen_server:start({global, pool_master}, pool, [], []), Hosts = net_adm:host_file(), @@ -71,7 +76,8 @@ start(Name, Args) when is_atom(Name) -> get_nodes() -> get_elements(2, get_nodes_and_load()). --spec attach(node()) -> 'already_attached' | 'attached'. +-spec attach(Node) -> 'already_attached' | 'attached' when + Node :: node(). attach(Node) -> gen_server:call({global, pool_master}, {attach, Node}). @@ -82,11 +88,17 @@ get_nodes_and_load() -> get_node() -> gen_server:call({global, pool_master}, get_node). --spec pspawn(module(), atom(), [term()]) -> pid(). +-spec pspawn(Mod, Fun, Args) -> pid() when + Mod :: module(), + Fun :: atom(), + Args :: [term()]. pspawn(M, F, A) -> gen_server:call({global, pool_master}, {spawn, group_leader(), M, F, A}). --spec pspawn_link(module(), atom(), [term()]) -> pid(). +-spec pspawn_link(Mod, Fun, Args) -> pid() when + Mod :: module(), + Fun :: atom(), + Args :: [term()]. pspawn_link(M, F, A) -> P = pspawn(M, F, A), link(P), @@ -95,6 +107,9 @@ pspawn_link(M, F, A) -> start_nodes([], _, _) -> []; start_nodes([Host|Tail], Name, Args) -> case slave:start(Host, Name, Args) of + {error, {already_running, Node}} -> + io:format("Can't start node on host ~w due to ~w~n",[Host, {already_running, Node}]), + [Node | start_nodes(Tail, Name, Args)]; {error, R} -> io:format("Can't start node on host ~w due to ~w~n",[Host, R]), start_nodes(Tail, Name, Args); diff --git a/lib/stdlib/src/proc_lib.erl b/lib/stdlib/src/proc_lib.erl index 9aa5e0a71e..02bcbb5a60 100644 --- a/lib/stdlib/src/proc_lib.erl +++ b/lib/stdlib/src/proc_lib.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1996-2011. 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(proc_lib). @@ -34,81 +34,114 @@ %% Internal exports. -export([wake_up/3]). +-export_type([spawn_option/0]). + %%----------------------------------------------------------------------------- -type priority_level() :: 'high' | 'low' | 'max' | 'normal'. -type spawn_option() :: 'link' + | 'monitor' | {'priority', priority_level()} | {'min_heap_size', non_neg_integer()} + | {'min_bin_vheap_size', non_neg_integer()} | {'fullsweep_after', non_neg_integer()}. --type dict_or_pid() :: pid() | [_] | {integer(), integer(), integer()}. +-type dict_or_pid() :: pid() + | (ProcInfo :: [_]) + | {X :: integer(), Y :: integer(), Z :: integer()}. %%----------------------------------------------------------------------------- --spec spawn(function()) -> pid(). +-spec spawn(Fun) -> pid() when + Fun :: function(). spawn(F) when is_function(F) -> Parent = get_my_name(), Ancestors = get_ancestors(), erlang:spawn(?MODULE, init_p, [Parent,Ancestors,F]). --spec spawn(atom(), atom(), [term()]) -> pid(). +-spec spawn(Module, Function, Args) -> pid() when + Module :: module(), + Function :: atom(), + Args :: [term()]. spawn(M,F,A) when is_atom(M), is_atom(F), is_list(A) -> Parent = get_my_name(), Ancestors = get_ancestors(), erlang:spawn(?MODULE, init_p, [Parent,Ancestors,M,F,A]). --spec spawn_link(function()) -> pid(). +-spec spawn_link(Fun) -> pid() when + Fun :: function(). spawn_link(F) when is_function(F) -> Parent = get_my_name(), Ancestors = get_ancestors(), erlang:spawn_link(?MODULE, init_p, [Parent,Ancestors,F]). --spec spawn_link(atom(), atom(), [term()]) -> pid(). +-spec spawn_link(Module, Function, Args) -> pid() when + Module :: module(), + Function :: atom(), + Args :: [term()]. spawn_link(M,F,A) when is_atom(M), is_atom(F), is_list(A) -> Parent = get_my_name(), Ancestors = get_ancestors(), erlang:spawn_link(?MODULE, init_p, [Parent,Ancestors,M,F,A]). --spec spawn(node(), function()) -> pid(). +-spec spawn(Node, Fun) -> pid() when + Node :: node(), + Fun :: function(). spawn(Node, F) when is_function(F) -> Parent = get_my_name(), Ancestors = get_ancestors(), erlang:spawn(Node, ?MODULE, init_p, [Parent,Ancestors,F]). --spec spawn(node(), atom(), atom(), [term()]) -> pid(). +-spec spawn(Node, Module, Function, Args) -> pid() when + Node :: node(), + Module :: module(), + Function :: atom(), + Args :: [term()]. + spawn(Node, M, F, A) when is_atom(M), is_atom(F), is_list(A) -> Parent = get_my_name(), Ancestors = get_ancestors(), erlang:spawn(Node, ?MODULE, init_p, [Parent,Ancestors,M,F,A]). --spec spawn_link(node(), function()) -> pid(). +-spec spawn_link(Node, Fun) -> pid() when + Node :: node(), + Fun :: function(). spawn_link(Node, F) when is_function(F) -> Parent = get_my_name(), Ancestors = get_ancestors(), erlang:spawn_link(Node, ?MODULE, init_p, [Parent,Ancestors,F]). --spec spawn_link(node(), atom(), atom(), [term()]) -> pid(). +-spec spawn_link(Node, Module, Function, Args) -> pid() when + Node :: node(), + Module :: module(), + Function :: atom(), + Args :: [term()]. spawn_link(Node, M, F, A) when is_atom(M), is_atom(F), is_list(A) -> Parent = get_my_name(), Ancestors = get_ancestors(), erlang:spawn_link(Node, ?MODULE, init_p, [Parent,Ancestors,M,F,A]). --spec spawn_opt(function(), [spawn_option()]) -> pid(). +-spec spawn_opt(Fun, SpawnOpts) -> pid() when + Fun :: function(), + SpawnOpts :: [spawn_option()]. + spawn_opt(F, Opts) when is_function(F) -> Parent = get_my_name(), Ancestors = get_ancestors(), check_for_monitor(Opts), erlang:spawn_opt(?MODULE, init_p, [Parent,Ancestors,F],Opts). --spec spawn_opt(node(), function(), [spawn_option()]) -> pid(). +-spec spawn_opt(Node, Function, SpawnOpts) -> pid() when + Node :: node(), + Function :: function(), + SpawnOpts :: [spawn_option()]. spawn_opt(Node, F, Opts) when is_function(F) -> Parent = get_my_name(), @@ -116,7 +149,11 @@ spawn_opt(Node, F, Opts) when is_function(F) -> check_for_monitor(Opts), erlang:spawn_opt(Node, ?MODULE, init_p, [Parent,Ancestors,F], Opts). --spec spawn_opt(atom(), atom(), [term()], [spawn_option()]) -> pid(). +-spec spawn_opt(Module, Function, Args, SpawnOpts) -> pid() when + Module :: module(), + Function :: atom(), + Args :: [term()], + SpawnOpts :: [spawn_option()]. spawn_opt(M, F, A, Opts) when is_atom(M), is_atom(F), is_list(A) -> Parent = get_my_name(), @@ -124,7 +161,12 @@ spawn_opt(M, F, A, Opts) when is_atom(M), is_atom(F), is_list(A) -> check_for_monitor(Opts), erlang:spawn_opt(?MODULE, init_p, [Parent,Ancestors,M,F,A], Opts). --spec spawn_opt(node(), atom(), atom(), [term()], [spawn_option()]) -> pid(). +-spec spawn_opt(Node, Module, Function, Args, SpawnOpts) -> pid() when + Node :: node(), + Module :: module(), + Function :: atom(), + Args :: [term()], + SpawnOpts :: [spawn_option()]. spawn_opt(Node, M, F, A, Opts) when is_atom(M), is_atom(F), is_list(A) -> Parent = get_my_name(), @@ -142,7 +184,10 @@ check_for_monitor(SpawnOpts) -> false end. --spec hibernate(module(), atom(), [term()]) -> no_return(). +-spec hibernate(Module, Function, Args) -> no_return() when + Module :: module(), + Function :: atom(), + Args :: [term()]. hibernate(M, F, A) when is_atom(M), is_atom(F), is_list(A) -> erlang:hibernate(?MODULE, wake_up, [M, F, A]). @@ -208,35 +253,65 @@ exit_p(Class, Reason) -> exit(Reason) end. --spec start(atom(), atom(), [term()]) -> term(). +-spec start(Module, Function, Args) -> Ret when + Module :: module(), + Function :: atom(), + Args :: [term()], + Ret :: term() | {error, Reason :: term()}. start(M, F, A) when is_atom(M), is_atom(F), is_list(A) -> start(M, F, A, infinity). --spec start(atom(), atom(), [term()], timeout()) -> term(). +-spec start(Module, Function, Args, Time) -> Ret when + Module :: module(), + Function :: atom(), + Args :: [term()], + Time :: timeout(), + 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). --spec start(atom(), atom(), [term()], timeout(), [spawn_option()]) -> term(). +-spec start(Module, Function, Args, Time, SpawnOpts) -> Ret when + Module :: module(), + Function :: atom(), + Args :: [term()], + Time :: timeout(), + SpawnOpts :: [spawn_option()], + 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). --spec start_link(atom(), atom(), [term()]) -> term(). +-spec start_link(Module, Function, Args) -> Ret when + Module :: module(), + Function :: atom(), + Args :: [term()], + Ret :: term() | {error, Reason :: term()}. start_link(M, F, A) when is_atom(M), is_atom(F), is_list(A) -> start_link(M, F, A, infinity). --spec start_link(atom(), atom(), [term()], timeout()) -> term(). +-spec start_link(Module, Function, Args, Time) -> Ret when + Module :: module(), + Function :: atom(), + Args :: [term()], + Time :: timeout(), + Ret :: term() | {error, Reason :: term()}. start_link(M, F, A, Timeout) when is_atom(M), is_atom(F), is_list(A) -> Pid = ?MODULE:spawn_link(M, F, A), sync_wait(Pid, Timeout). --spec start_link(atom(),atom(),[term()],timeout(),[spawn_option()]) -> term(). +-spec start_link(Module, Function, Args, Time, SpawnOpts) -> Ret when + Module :: module(), + Function :: atom(), + Args :: [term()], + Time :: timeout(), + SpawnOpts :: [spawn_option()], + Ret :: term() | {error, Reason :: term()}. start_link(M,F,A,Timeout,SpawnOpts) when is_atom(M), is_atom(F), is_list(A) -> Pid = ?MODULE:spawn_opt(M, F, A, ensure_link(SpawnOpts)), @@ -265,13 +340,17 @@ flush(Pid) -> true end. --spec init_ack(pid(), term()) -> 'ok'. +-spec init_ack(Parent, Ret) -> 'ok' when + Parent :: pid(), + Ret :: term(). init_ack(Parent, Return) -> Parent ! {ack, self(), Return}, ok. --spec init_ack(term()) -> 'ok'. +-spec init_ack(Ret) -> 'ok' when + Ret :: term(). + init_ack(Return) -> [Parent|_] = get('$ancestors'), init_ack(Parent, Return). @@ -280,7 +359,11 @@ init_ack(Return) -> %% Fetch the initial call of a proc_lib spawned process. %% ----------------------------------------------------- --spec initial_call(dict_or_pid()) -> {atom(), atom(), [atom()]} | 'false'. +-spec initial_call(Process) -> {Module, Function, Args} | 'false' when + Process :: dict_or_pid(), + Module :: module(), + Function :: atom(), + Args :: [atom()]. initial_call(DictOrPid) -> case raw_initial_call(DictOrPid) of @@ -303,7 +386,11 @@ make_dummy_args(N, Acc) -> %% This function is typically called from c:i() and c:regs(). %% ----------------------------------------------------- --spec translate_initial_call(dict_or_pid()) -> mfa(). +-spec translate_initial_call(Process) -> {Module, Function, Arity} when + Process :: dict_or_pid(), + Module :: module(), + Function :: atom(), + Arity :: byte(). translate_initial_call(DictOrPid) -> case raw_initial_call(DictOrPid) of @@ -575,7 +662,8 @@ check(Res) -> Res. %%% Format (and write) a generated crash info structure. %%% ----------------------------------------------------------- --spec format([term()]) -> string(). +-spec format(CrashReport) -> string() when + CrashReport :: [term()]. format([OwnReport,LinkReport]) -> OwnFormat = format_report(OwnReport), diff --git a/lib/stdlib/src/proplists.erl b/lib/stdlib/src/proplists.erl index 35d14891f1..68697d0da2 100644 --- a/lib/stdlib/src/proplists.erl +++ b/lib/stdlib/src/proplists.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2001-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2001-2011. 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% %% %% ===================================================================== @@ -37,7 +37,7 @@ %% overriding the default settings, object properties, annotations, %% etc.</p> %% -%% @type property() = atom() | tuple() +%% % @type property() = atom() | tuple() -module(proplists). @@ -49,16 +49,12 @@ %% --------------------------------------------------------------------- --type property() :: atom() | tuple(). +-export_type([property/0]). --type aliases() :: [{any(), any()}]. --type negations() :: [{any(), any()}]. --type expansions() :: [{property(), [any()]}]. +-type property() :: atom() | tuple(). %% --------------------------------------------------------------------- -%% @spec property(P::property()) -> 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 @@ -66,7 +62,8 @@ %% %% @see property/2 --spec property(property()) -> property(). +-spec property(Property) -> Property when + Property :: property(). property({Key, true}) when is_atom(Key) -> Key; @@ -74,8 +71,6 @@ property(Property) -> Property. -%% @spec property(Key::term(), Value::term()) -> property() -%% %% @doc Creates a normal form (minimal) representation of a simple %% key/value property. Returns <code>Key</code> if <code>Value</code> is %% <code>true</code> and <code>Key</code> is an atom, otherwise a tuple @@ -83,7 +78,10 @@ property(Property) -> %% %% @see property/1 --spec property(Key::term(), Value::term()) -> atom() | {term(), term()}. +-spec property(Key, Value) -> Property when + Key :: term(), + Value :: term(), + Property :: atom() | {term(), term()}. property(Key, true) when is_atom(Key) -> Key; @@ -93,14 +91,13 @@ property(Key, Value) -> %% --------------------------------------------------------------------- -%% @spec unfold(List::[term()]) -> [term()] -%% %% @doc Unfolds all occurences of atoms in <code>List</code> to tuples %% <code>{Atom, true}</code>. %% %% @see compact/1 --spec unfold(List::[term()]) -> [term()]. +-spec unfold(List) -> List when + List :: [term()]. unfold([P | Ps]) -> if is_atom(P) -> @@ -111,15 +108,14 @@ unfold([P | Ps]) -> unfold([]) -> []. -%% @spec compact(List::[term()]) -> [term()] -%% %% @doc Minimizes the representation of all entries in the list. This is %% equivalent to <code>[property(P) || P <- List]</code>. %% %% @see unfold/1 %% @see property/1 --spec compact(List::[property()]) -> [property()]. +-spec compact(List) -> List when + List :: [property()]. compact(List) -> [property(P) || P <- List]. @@ -127,8 +123,6 @@ compact(List) -> %% --------------------------------------------------------------------- -%% @spec lookup(Key::term(), List::[term()]) -> none | tuple() -%% %% @doc Returns the first entry associated with <code>Key</code> in %% <code>List</code>, if one exists, otherwise returns %% <code>none</code>. For an atom <code>A</code> in the list, the tuple @@ -138,7 +132,9 @@ compact(List) -> %% @see get_value/2 %% @see get_bool/2 --spec lookup(Key::term(), List::[term()]) -> 'none' | tuple(). +-spec lookup(Key, List) -> 'none' | tuple() when + Key :: term(), + List :: [term()]. lookup(Key, [P | Ps]) -> if is_atom(P), P =:= Key -> @@ -152,15 +148,15 @@ lookup(Key, [P | Ps]) -> lookup(_Key, []) -> none. -%% @spec lookup_all(Key::term(), List::[term()]) -> [tuple()] -%% %% @doc Returns the list of all entries associated with <code>Key</code> %% in <code>List</code>. If no such entry exists, the result is the %% empty list. %% %% @see lookup/2 --spec lookup_all(Key::term(), List::[term()]) -> [tuple()]. +-spec lookup_all(Key, List) -> [tuple()] when + Key :: term(), + List :: [term()]. lookup_all(Key, [P | Ps]) -> if is_atom(P), P =:= Key -> @@ -176,13 +172,13 @@ lookup_all(_Key, []) -> %% --------------------------------------------------------------------- -%% @spec is_defined(Key::term(), List::[term()]) -> boolean() -%% %% @doc Returns <code>true</code> if <code>List</code> contains at least %% one entry associated with <code>Key</code>, otherwise %% <code>false</code> is returned. --spec is_defined(Key::term(), List::[term()]) -> boolean(). +-spec is_defined(Key, List) -> boolean() when + Key :: term(), + List :: [term()]. is_defined(Key, [P | Ps]) -> if is_atom(P), P =:= Key -> @@ -198,17 +194,15 @@ is_defined(_Key, []) -> %% --------------------------------------------------------------------- -%% @spec get_value(Key::term(), List::[term()]) -> term() %% @equiv get_value(Key, List, undefined) --spec get_value(Key::term(), List::[term()]) -> term(). +-spec get_value(Key, List) -> term() when + Key :: term(), + List :: List::[term()]. get_value(Key, List) -> get_value(Key, List, undefined). -%% @spec get_value(Key::term(), List::[term()], Default::term()) -> -%% term() -%% %% @doc Returns the value of a simple key/value property in %% <code>List</code>. If <code>lookup(Key, List)</code> would yield %% <code>{Key, Value}</code>, this function returns the corresponding @@ -219,7 +213,10 @@ get_value(Key, List) -> %% @see get_all_values/2 %% @see get_bool/2 --spec get_value(Key::term(), List::[term()], Default::term()) -> term(). +-spec get_value(Key, List, Default) -> term() when + Key :: term(), + List :: [term()], + Default :: term(). get_value(Key, [P | Ps], Default) -> if is_atom(P), P =:= Key -> @@ -238,8 +235,6 @@ get_value(Key, [P | Ps], Default) -> get_value(_Key, [], Default) -> Default. -%% @spec get_all_values(Key, List) -> [term()] -%% %% @doc Similar to <code>get_value/2</code>, but returns the list of %% values for <em>all</em> entries <code>{Key, Value}</code> in %% <code>List</code>. If no such entry exists, the result is the empty @@ -247,7 +242,9 @@ get_value(_Key, [], Default) -> %% %% @see get_value/2 --spec get_all_values(Key::term(), List::[term()]) -> [term()]. +-spec get_all_values(Key, List) -> [term()] when + Key :: term(), + List :: [term()]. get_all_values(Key, [P | Ps]) -> if is_atom(P), P =:= Key -> @@ -265,8 +262,6 @@ get_all_values(Key, [P | Ps]) -> get_all_values(_Key, []) -> []. -%% @spec append_values(Key::term(), List::[term()]) -> [term()] -%% %% @doc Similar to <code>get_all_values/2</code>, but each value is %% wrapped in a list unless it is already itself a list, and the %% resulting list of lists is concatenated. This is often useful for @@ -276,7 +271,9 @@ get_all_values(_Key, []) -> %% %% @see get_all_values/2 --spec append_values(Key::term(), List::[term()]) -> [term()]. +-spec append_values(Key, List) -> List when + Key :: term(), + List :: [term()]. append_values(Key, [P | Ps]) -> if is_atom(P), P =:= Key -> @@ -299,8 +296,6 @@ append_values(_Key, []) -> %% --------------------------------------------------------------------- -%% @spec get_bool(Key::term(), List::[term()]) -> boolean() -%% %% @doc Returns the value of a boolean key/value option. If %% <code>lookup(Key, List)</code> would yield <code>{Key, true}</code>, %% this function returns <code>true</code>; otherwise <code>false</code> @@ -309,7 +304,9 @@ append_values(_Key, []) -> %% @see lookup/2 %% @see get_value/2 --spec get_bool(Key::term(), List::[term()]) -> boolean(). +-spec get_bool(Key, List) -> boolean() when + Key :: term(), + List :: [term()]. get_bool(Key, [P | Ps]) -> if is_atom(P), P =:= Key -> @@ -331,12 +328,11 @@ get_bool(_Key, []) -> %% --------------------------------------------------------------------- -%% @spec get_keys(List::[term()]) -> [term()] -%% %% @doc Returns an unordered list of the keys used in <code>List</code>, %% not containing duplicates. --spec get_keys(List::[term()]) -> [term()]. +-spec get_keys(List) -> [term()] when + List :: [term()]. get_keys(Ps) -> sets:to_list(get_keys(Ps, sets:new())). @@ -355,12 +351,12 @@ get_keys([], Keys) -> %% --------------------------------------------------------------------- -%% @spec delete(Key::term(), List::[term()]) -> [term()] -%% %% @doc Deletes all entries associated with <code>Key</code> from %% <code>List</code>. --spec delete(Key::term(), List::[term()]) -> [term()]. +-spec delete(Key, List) -> List when + Key :: term(), + List::[term()]. delete(Key, [P | Ps]) -> if is_atom(P), P =:= Key -> @@ -376,11 +372,6 @@ delete(_, []) -> %% --------------------------------------------------------------------- -%% @spec substitute_aliases(Aliases, List::[term()]) -> [term()] -%% -%% Aliases = [{Key, Key}] -%% Key = term() -%% %% @doc Substitutes keys of properties. For each entry in %% <code>List</code>, if it is associated with some key <code>K1</code> %% such that <code>{K1, K2}</code> occurs in <code>Aliases</code>, the @@ -396,7 +387,10 @@ delete(_, []) -> %% @see substitute_negations/2 %% @see normalize/2 --spec substitute_aliases(aliases(), List::[term()]) -> [term()]. +-spec substitute_aliases(Aliases, List) -> List when + Aliases :: [{Key, Key}], + Key :: term(), + List::[term()]. substitute_aliases(As, Props) -> [substitute_aliases_1(As, P) || P <- Props]. @@ -415,11 +409,6 @@ substitute_aliases_1([], P) -> %% --------------------------------------------------------------------- -%% @spec substitute_negations(Negations, List::[term()]) -> [term()] -%% -%% Negations = [{Key, Key}] -%% Key = term() -%% %% @doc Substitutes keys of boolean-valued properties and simultaneously %% negates their values. For each entry in <code>List</code>, if it is %% associated with some key <code>K1</code> such that <code>{K1, @@ -441,7 +430,10 @@ substitute_aliases_1([], P) -> %% @see substitute_aliases/2 %% @see normalize/2 --spec substitute_negations(negations(), List::[term()]) -> [term()]. +-spec substitute_negations(Negations, List) -> List when + Negations :: [{Key, Key}], + Key :: term(), + List :: [term()]. substitute_negations(As, Props) -> [substitute_negations_1(As, P) || P <- Props]. @@ -470,10 +462,6 @@ substitute_negations_1([], P) -> %% --------------------------------------------------------------------- -%% @spec expand(Expansions, List::[term()]) -> [term()] -%% -%% Expansions = [{property(), [term()]}] -%% %% @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 @@ -508,7 +496,9 @@ substitute_negations_1([], P) -> %% %% @see normalize/2 --spec expand(Expansions::expansions(), [term()]) -> [term()]. +-spec expand(Expansions, List) -> List when + Expansions :: [{Property :: property(), Expansion :: [term()]}], + List :: [term()]. expand(Es, Ps) when is_list(Ps) -> Es1 = [{property(P), V} || {P, V} <- Es], @@ -587,15 +577,6 @@ flatten([]) -> %% --------------------------------------------------------------------- -%% @spec normalize(List::[term()], Stages::[Operation]) -> [term()] -%% -%% Operation = {aliases, Aliases} | {negations, Negations} -%% | {expand, Expansions} -%% Aliases = [{Key, Key}] -%% Negations = [{Key, Key}] -%% Key = term() -%% Expansions = [{property(), [term()]}] -%% %% @doc Passes <code>List</code> through a sequence of %% substitution/expansion stages. For an <code>aliases</code> operation, %% the function <code>substitute_aliases/2</code> is applied using the @@ -617,11 +598,15 @@ flatten([]) -> %% @see expand/2 %% @see compact/1 --type operation() :: {'aliases', aliases()} - | {'negations', negations()} - | {'expand', expansions()}. - --spec normalize(List::[term()], Stages::[operation()]) -> [term()]. +-spec normalize(List, Stages) -> List when + List :: [term()], + Stages :: [Operation], + Operation :: {'aliases', Aliases} + | {'negations', Negations} + | {'expand', Expansions}, + Aliases :: [{Key, Key}], + Negations :: [{Key, Key}], + Expansions :: [{Property :: property(), Expansion :: [term()]}]. normalize(L, [{aliases, As} | Xs]) -> normalize(substitute_aliases(As, L), Xs); @@ -634,10 +619,6 @@ normalize(L, []) -> %% --------------------------------------------------------------------- -%% @spec split(List::[term()], Keys::[term()]) -> {Lists, Rest} -%% Lists = [[term()]] -%% Rest = [term()] -%% %% @doc Partitions <code>List</code> into a list of sublists and a %% remainder. <code>Lists</code> contains one sublist for each key in %% <code>Keys</code>, in the corresponding order. The relative order of @@ -652,7 +633,11 @@ normalize(L, []) -> %% {[[a], [{b, 5}, b],[{c, 2}, {c, 3, 4}]], [{e, 1}, d]}</pre> %% </p> --spec split(List::[term()], Keys::[term()]) -> {[[term()]], [term()]}. +-spec split(List, Keys) -> {Lists, Rest} when + List :: [term()], + Keys :: [term()], + Lists :: [[term()]], + Rest :: [term()]. split(List, Keys) -> {Store, Rest} = split(List, dict:from_list([{K, []} || K <- Keys]), []), diff --git a/lib/stdlib/src/qlc.erl b/lib/stdlib/src/qlc.erl index 6e48d95973..5ca04ff023 100644 --- a/lib/stdlib/src/qlc.erl +++ b/lib/stdlib/src/qlc.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2010. All Rights Reserved. +%% Copyright Ericsson AB 2004-2011. 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 @@ -24,6 +24,8 @@ %% External exports +%% Avoid warning for local function error/1 clashing with autoimported BIF. +-compile({no_auto_import,[error/1]}). -export([parse_transform/2, transform_from_evaluator/2]). -export([q/1, q/2]). @@ -58,7 +60,7 @@ -record(qlc_table, % qlc:table/2 {trav_fun, % traverse fun - trav_MS, % bool(); true iff traverse fun takes a match spec + trav_MS, % boolean(); true iff traverse fun takes a match spec pre_fun, post_fun, info_fun, @@ -108,12 +110,12 @@ -record(qlc_cursor, {c}). -record(qlc_opt, - {unique = false, % bool() - cache = false, % bool() | list (true~ets, false~no) + {unique = false, % boolean() + cache = false, % boolean() | list (true~ets, false~no) max_lookup = -1, % int() >= 0 | -1 (represents infinity) join = any, % any | nested_loop | merge | lookup tmpdir = "", % global tmpdir - lookup = any, % any | bool() + lookup = any, % any | boolean() max_list = ?MAX_LIST_SIZE, % int() >= 0 tmpdir_usage = allowed % allowed | not_allowed % | warning_msg | error_msg | info_msg @@ -123,6 +125,8 @@ -define(THROWN_ERROR, {?MODULE, throw_error, _}). +-export_type([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. @@ -142,6 +146,35 @@ get_handle(_) -> %%% Exported functions %%% +-type(query_list_comprehension() :: term()). +-opaque(query_cursor() :: {qlc_cursor, term()}). +-opaque(query_handle() :: {qlc_handle, term()}). +-type(query_handle_or_list() :: query_handle() | list()). +-type(answers() :: [answer()]). +-type(answer() :: term()). +-type(abstract_expr() :: erl_parse:abstract_expr()). +-type(match_expression() :: ets:match_spec()). +-type(spawn_options() :: default | [proc_lib:spawn_option()]). +-type(sort_options() :: [sort_option()] | sort_option()). +-type(sort_option() :: {compressed, boolean()} + | {no_files, no_files()} + | {order, order()} + | {size, pos_integer()} + | {tmpdir, tmp_directory()} + | {unique, boolean()}). +-type(order() :: ascending | descending | order_fun()). +-type(order_fun() :: fun((term(), term()) -> boolean())). +-type(tmp_directory() :: [] | file:name()). +-type(no_files() :: pos_integer()). % > 1 +-type(key_pos() :: pos_integer() | [pos_integer()]). +-type(max_list_size() :: non_neg_integer()). +-type(cache() :: ets | list | no). +-type(tmp_file_usage() :: allowed | not_allowed | info_msg + | warning_msg | error_msg). + +-spec(append(QHL) -> QH when + QHL :: [query_handle_or_list()], + QH :: query_handle()). append(QHs) -> Hs = [case get_handle(QH) of badarg -> erlang:error(badarg, [QHs]); @@ -149,6 +182,10 @@ append(QHs) -> end || QH <- QHs], #qlc_handle{h = #qlc_append{hl = Hs}}. +-spec(append(QH1, QH2) -> QH3 when + QH1 :: query_handle_or_list(), + QH2 :: query_handle_or_list(), + QH3 :: query_handle()). append(QH1, QH2) -> Hs = [case get_handle(QH) of badarg -> erlang:error(badarg, [QH1, QH2]); @@ -156,9 +193,22 @@ append(QH1, QH2) -> end || QH <- [QH1, QH2]], #qlc_handle{h = #qlc_append{hl = Hs}}. +-spec(cursor(QH) -> Cursor when + QH :: query_handle_or_list(), + Cursor :: query_cursor()). cursor(QH) -> cursor(QH, []). +-spec(cursor(QH, Options) -> Cursor when + QH :: query_handle_or_list(), + Options :: [Option] | Option, + Option :: {cache_all, cache()} | cache_all + | {max_list_size, max_list_size()} + | {spawn_options, spawn_options()} + | {tmpdir_usage, tmp_file_usage()} + | {tmpdir, tmp_directory()} + | {unique_all, boolean()} | unique_all, + Cursor :: query_cursor()). cursor(QH, Options) -> case {options(Options, [unique_all, cache_all, tmpdir, spawn_options, max_list_size, @@ -177,6 +227,8 @@ cursor(QH, Options) -> end end. +-spec(delete_cursor(QueryCursor) -> ok when + QueryCursor :: query_cursor()). delete_cursor(#qlc_cursor{c = {_, Owner}}=C) when Owner =/= self() -> erlang:error(not_cursor_owner, [C]); delete_cursor(#qlc_cursor{c = {Pid, _}}) -> @@ -184,15 +236,47 @@ delete_cursor(#qlc_cursor{c = {Pid, _}}) -> delete_cursor(T) -> erlang:error(badarg, [T]). +-spec(e(QH) -> Answers | Error when + QH :: query_handle_or_list(), + Answers :: answers(), + Error :: {error, module(), Reason}, + Reason :: file_sorter:reason()). e(QH) -> eval(QH, []). +-spec(e(QH, Options) -> Answers | Error when + QH :: query_handle_or_list(), + Options :: [Option] | Option, + Option :: {cache_all, cache()} | cache_all + | {max_list_size, max_list_size()} + | {tmpdir_usage, tmp_file_usage()} + | {tmpdir, tmp_directory()} + | {unique_all, boolean()} | unique_all, + Answers :: answers(), + Error :: {error, module(), Reason}, + Reason :: file_sorter:reason()). e(QH, Options) -> eval(QH, Options). +-spec(eval(QH) -> Answers | Error when + QH :: query_handle_or_list(), + Answers :: answers(), + Error :: {error, module(), Reason}, + Reason :: file_sorter:reason()). eval(QH) -> eval(QH, []). +-spec(eval(QH, Options) -> Answers | Error when + QH :: query_handle_or_list(), + Answers :: answers(), + Options :: [Option] | Option, + Option :: {cache_all, cache()} | cache_all + | {max_list_size, max_list_size()} + | {tmpdir_usage, tmp_file_usage()} + | {tmpdir, tmp_directory()} + | {unique_all, boolean()} | unique_all, + Error :: {error, module(), Reason}, + Reason :: file_sorter:reason()). eval(QH, Options) -> case {options(Options, [unique_all, cache_all, tmpdir, max_list_size, tmpdir_usage]), @@ -224,9 +308,35 @@ eval(QH, Options) -> end end. +-spec(fold(Function, Acc0, QH) -> + Acc1 | Error when + QH :: query_handle_or_list(), + Function :: fun((answer(), AccIn) -> AccOut), + Acc0 :: term(), + Acc1 :: term(), + AccIn :: term(), + AccOut :: term(), + Error :: {error, module(), Reason}, + Reason :: file_sorter:reason()). fold(Fun, Acc0, QH) -> fold(Fun, Acc0, QH, []). +-spec(fold(Function, Acc0, QH, Options) -> + Acc1 | Error when + QH :: query_handle_or_list(), + Function :: fun((answer(), AccIn) -> AccOut), + Acc0 :: term(), + Acc1 :: term(), + AccIn :: term(), + AccOut :: term(), + Options :: [Option] | Option, + Option :: {cache_all, cache()} | cache_all + | {max_list_size, max_list_size()} + | {tmpdir_usage, tmp_file_usage()} + | {tmpdir, tmp_directory()} + | {unique_all, boolean()} | unique_all, + Error :: {error, module(), Reason}, + Reason :: file_sorter:reason()). fold(Fun, Acc0, QH, Options) -> case {options(Options, [unique_all, cache_all, tmpdir, max_list_size, tmpdir_usage]), @@ -256,6 +366,9 @@ fold(Fun, Acc0, QH, Options) -> end end. +-spec(format_error(Error) -> Chars when + Error :: {error, module(), term()}, + Chars :: io_lib:chars()). format_error(not_a_query_list_comprehension) -> io_lib:format("argument is not a query list comprehension", []); format_error({used_generator_variable, V}) -> @@ -293,9 +406,29 @@ format_error({error, Module, Reason}) -> format_error(E) -> io_lib:format("~p~n", [E]). +-spec(info(QH) -> Info when + QH :: query_handle_or_list(), + Info :: abstract_expr() | string()). info(QH) -> info(QH, []). +-spec(info(QH, Options) -> Info when + QH :: query_handle_or_list(), + Options :: [Option] | Option, + Option :: EvalOption | ReturnOption, + EvalOption :: {cache_all, cache()} | cache_all + | {max_list_size, max_list_size()} + | {tmpdir_usage, tmp_file_usage()} + | {tmpdir, tmp_directory()} + | {unique_all, boolean()} | unique_all, + ReturnOption :: {depth, Depth} + | {flat, boolean()} + | {format, Format} + | {n_elements, NElements}, + Depth :: infinity | non_neg_integer(), + Format :: abstract_code | string, + NElements :: infinity | pos_integer(), + Info :: abstract_expr() | string()). info(QH, Options) -> case {options(Options, [unique_all, cache_all, flat, format, n_elements, depth, tmpdir, max_list_size, tmpdir_usage]), @@ -331,9 +464,18 @@ info(QH, Options) -> end end. +-spec(keysort(KeyPos, QH1) -> QH2 when + KeyPos :: key_pos(), + QH1 :: query_handle_or_list(), + QH2 :: query_handle()). keysort(KeyPos, QH) -> keysort(KeyPos, QH, []). +-spec(keysort(KeyPos, QH1, SortOptions) -> QH2 when + KeyPos :: key_pos(), + SortOptions :: sort_options(), + QH1 :: query_handle_or_list(), + QH2 :: query_handle()). keysort(KeyPos, QH, Options) -> case {is_keypos(KeyPos), options(Options, [tmpdir, order, unique, compressed, @@ -352,9 +494,22 @@ keysort(KeyPos, QH, Options) -> -define(DEFAULT_NUM_OF_ANSWERS, 10). +-spec(next_answers(QueryCursor) -> + Answers | Error when + QueryCursor :: query_cursor(), + Answers :: answers(), + Error :: {error, module(), Reason}, + Reason :: file_sorter:reason()). next_answers(C) -> next_answers(C, ?DEFAULT_NUM_OF_ANSWERS). +-spec(next_answers(QueryCursor, NumberOfAnswers) -> + Answers | Error when + QueryCursor :: query_cursor(), + Answers :: answers(), + NumberOfAnswers :: all_remaining | pos_integer(), + Error :: {error, module(), Reason}, + Reason :: file_sorter:reason()). next_answers(#qlc_cursor{c = {_, Owner}}=C, NumOfAnswers) when Owner =/= self() -> erlang:error(not_cursor_owner, [C, NumOfAnswers]); @@ -368,14 +523,35 @@ next_answers(#qlc_cursor{c = {Pid, _}}=C, NumOfAnswers) -> next_answers(T1, T2) -> erlang:error(badarg, [T1, T2]). +-spec(parse_transform(Forms, Options) -> Forms2 when + Forms :: [erl_parse:abstract_form()], + Forms2 :: [erl_parse:abstract_form()], + Options :: [Option], + Option :: type_checker | compile:option()). + parse_transform(Forms, Options) -> qlc_pt:parse_transform(Forms, Options). %% The funcspecs qlc:q/1 and qlc:q/2 are known by erl_eval.erl and %% erl_lint.erl. +-spec(q(QLC) -> QH when + QLC :: query_list_comprehension(), + QH :: query_handle()). q(QLC_lc) -> q(QLC_lc, []). +-spec(q(QLC, Options) -> QH when + QH :: query_handle(), + Options :: [Option] | Option, + Option :: {max_lookup, MaxLookup} + | {cache, cache()} | cache + | {join, Join} + | {lookup, Lookup} + | {unique, boolean()} | unique, + MaxLookup :: non_neg_integer() | infinity, + Join :: any | lookup | merge | nested_loop, + Lookup :: boolean() | any, + QLC :: query_list_comprehension()). q(#qlc_lc{}=QLC_lc, Options) -> case options(Options, [unique, cache, max_lookup, join, lookup]) of [Unique, Cache, Max, Join, Lookup] -> @@ -388,9 +564,16 @@ q(#qlc_lc{}=QLC_lc, Options) -> q(T1, T2) -> erlang:error(badarg, [T1, T2]). +-spec(sort(QH1) -> QH2 when + QH1 :: query_handle_or_list(), + QH2 :: query_handle()). sort(QH) -> sort(QH, []). +-spec(sort(QH1, SortOptions) -> QH2 when + SortOptions :: sort_options(), + QH1 :: query_handle_or_list(), + QH2 :: query_handle()). sort(QH, Options) -> case {options(Options, [tmpdir, order, unique, compressed, size, no_files]), get_handle(QH)} of @@ -404,14 +587,47 @@ sort(QH, Options) -> end. %% Note that the generated code is evaluated by (the slow) erl_eval. +-spec(string_to_handle(QueryString) -> QH | Error when + QueryString :: string(), + QH :: query_handle(), + Error :: {error, module(), Reason}, + Reason :: erl_parse:error_info() | erl_scan:error_info()). string_to_handle(Str) -> string_to_handle(Str, []). +-spec(string_to_handle(QueryString, Options) -> QH | Error when + QueryString :: string(), + Options :: [Option] | Option, + Option :: {max_lookup, MaxLookup} + | {cache, cache()} | cache + | {join, Join} + | {lookup, Lookup} + | {unique, boolean()} | unique, + MaxLookup :: non_neg_integer() | infinity, + Join :: any | lookup | merge | nested_loop, + Lookup :: boolean() | any, + QH :: query_handle(), + Error :: {error, module(), Reason}, + Reason :: erl_parse:error_info() | erl_scan:error_info()). string_to_handle(Str, Options) -> - string_to_handle(Str, Options, []). - -string_to_handle(Str, Options, Bindings) when is_list(Str), - is_list(Bindings) -> + string_to_handle(Str, Options, erl_eval:new_bindings()). + +-spec(string_to_handle(QueryString, Options, Bindings) -> QH | Error when + QueryString :: string(), + Options :: [Option] | Option, + Option :: {max_lookup, MaxLookup} + | {cache, cache()} | cache + | {join, Join} + | {lookup, Lookup} + | {unique, boolean()} | unique, + MaxLookup :: non_neg_integer() | infinity, + Join :: any | lookup | merge | nested_loop, + Lookup :: boolean() | any, + Bindings :: erl_eval:binding_struct(), + QH :: query_handle(), + Error :: {error, module(), Reason}, + Reason :: erl_parse:error_info() | erl_scan:error_info()). +string_to_handle(Str, Options, Bindings) when is_list(Str) -> case options(Options, [unique, cache, max_lookup, join, lookup]) of badarg -> erlang:error(badarg, [Str, Options, Bindings]); @@ -445,6 +661,51 @@ string_to_handle(Str, Options, Bindings) when is_list(Str), string_to_handle(T1, T2, T3) -> erlang:error(badarg, [T1, T2, T3]). +-spec(table(TraverseFun, Options) -> QH when + TraverseFun :: TraverseFun0 | TraverseFun1, + TraverseFun0 :: fun(() -> TraverseResult), + TraverseFun1 :: fun((match_expression()) -> TraverseResult), + TraverseResult :: Objects | term(), + Objects :: [] | [term() | ObjectList], + ObjectList :: TraverseFun0 | Objects, + Options :: [Option] | Option, + Option :: {format_fun, FormatFun} + | {info_fun, InfoFun} + | {lookup_fun, LookupFun} + | {parent_fun, ParentFun} + | {post_fun, PostFun} + | {pre_fun, PreFun} + | {key_equality, KeyComparison}, + FormatFun :: undefined | fun((SelectedObjects) -> FormatedTable), + SelectedObjects :: all + | {all, NElements, DepthFun} + | {match_spec, match_expression()} + | {lookup, Position, Keys} + | {lookup, Position, Keys, NElements, DepthFun}, + NElements :: infinity | pos_integer(), + DepthFun :: fun((term()) -> term()), + FormatedTable :: {Mod, Fun, Args} + | abstract_expr() + | string(), + InfoFun :: undefined | fun((InfoTag) -> InfoValue), + InfoTag :: indices | is_unique_objects | keypos | num_of_objects, + InfoValue :: undefined | term(), + LookupFun :: undefined | fun((Position, Keys) -> LookupResult), + LookupResult :: [term()] | term(), + ParentFun :: undefined | fun(() -> ParentFunValue), + PostFun :: undefined | fun(() -> term()), + PreFun :: undefined | fun((PreArgs) -> term()), + PreArgs :: [PreArg], + PreArg :: {parent_value, ParentFunValue} | {stop_fun, StopFun}, + ParentFunValue :: undefined | term(), + StopFun :: undefined | fun(() -> term()), + KeyComparison :: '=:=' | '==', + Position :: pos_integer(), + Keys :: [term()], + Mod :: atom(), + Fun :: atom(), + Args :: [term()], + QH :: query_handle()). table(TraverseFun, Options) when is_function(TraverseFun) -> case {is_function(TraverseFun, 0), IsFun1 = is_function(TraverseFun, 1)} of @@ -470,6 +731,11 @@ table(TraverseFun, Options) when is_function(TraverseFun) -> table(T1, T2) -> erlang:error(badarg, [T1, T2]). +-spec(transform_from_evaluator(LC, Bs) -> Expr when + LC :: abstract_expr(), + Expr :: abstract_expr(), + Bs :: erl_eval:binding_struct()). + transform_from_evaluator(LC, Bs0) -> qlc_pt:transform_from_evaluator(LC, Bs0). @@ -720,8 +986,8 @@ listify(T) -> %% Optimizations to be carried out. -record(optz, - {unique = false, % bool() - cache = false, % bool() | list + {unique = false, % boolean() + cache = false, % boolean() | list join_option = any, % constraint set by the 'join' option fast_join = no, % no | #qlc_join. 'no' means nested loop. opt % #qlc_opt @@ -754,8 +1020,8 @@ listify(T) -> lu_skip_quals = [], % qualifiers to skip due to lookup join = {[],[]}, % {Lookup, Merge} n_objs = undefined, % for join (not used yet) - is_unique_objects = false, % bool() - is_cached = false % bool() (true means 'ets' or 'list') + is_unique_objects = false, % boolean() + is_cached = false % boolean() (true means 'ets' or 'list') }). %%% Cursor process functions. @@ -1141,20 +1407,20 @@ monitor_request(Pid, Req) -> %% QueryDesc = {qlc, TemplateDesc, [QualDesc], [QueryOpt]} %% | {table, TableDesc} %% | {append, [QueryDesc]} -%% | {sort, QueryDesc, [SortOption]} -%% | {keysort, KeyPos, QueryDesc, [SortOption]} +%% | {sort, QueryDesc, [sort_option()]} +%% | {keysort, key_pos(), QueryDesc, [sort_option()]} %% | {list, list()} -%% | {list, QueryDesc, MatchExpression} +%% | {list, QueryDesc, match_expression()} %% TableDesc = {Mod, Fun, Args} -%% | AbstractExpression -%% | character_list() +%% | erl_parse:abstract_expr() +%% | string() %% Mod = module() %% Fun = atom() %% Args = [term()] %% QualDesc = FilterDesc %% | {generate, PatternDesc, QueryDesc} -%% QueryOpt = {cache, bool()} | cache -%% | {unique, bool()} | unique +%% QueryOpt = {cache, boolean()} | cache +%% | {unique, boolean()} | unique %% FilterDesc = PatternDesc = TemplateDesc = binary() le_info(#prepared{qh = #simple_qlc{le = LE, p = P, line = L, optz = Optz}}, diff --git a/lib/stdlib/src/qlc_pt.erl b/lib/stdlib/src/qlc_pt.erl index 24378a0698..21504d707b 100644 --- a/lib/stdlib/src/qlc_pt.erl +++ b/lib/stdlib/src/qlc_pt.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2010. All Rights Reserved. +%% Copyright Ericsson AB 2004-2011. 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 @@ -63,6 +63,12 @@ %%% Exported functions %%% +-spec(parse_transform(Forms, Options) -> Forms2 when + Forms :: [erl_parse:abstract_form()], + Forms2 :: [erl_parse:abstract_form()], + Options :: [Option], + Option :: type_checker | compile:option()). + parse_transform(Forms, Options) -> ?DEBUG("qlc Parse Transform~n", []), State = #state{imp = is_qlc_q_imported(Forms), @@ -96,10 +102,20 @@ parse_transform(Forms, Options) -> end end. +-spec(transform_from_evaluator(LC, Bs) -> Expr when + LC :: erl_parse:abstract_expr(), + Expr :: erl_parse:abstract_expr(), + Bs :: erl_eval:binding_struct()). + transform_from_evaluator(LC, Bindings) -> ?DEBUG("qlc Parse Transform (Evaluator Version)~n", []), transform_expression(LC, Bindings, false). +-spec(transform_expression(LC, Bs) -> Expr when + LC :: erl_parse:abstract_expr(), + Expr :: erl_parse:abstract_expr(), + Bs :: erl_eval:binding_struct()). + transform_expression(LC, Bindings) -> transform_expression(LC, Bindings, true). diff --git a/lib/stdlib/src/queue.erl b/lib/stdlib/src/queue.erl index c09079e8d2..4c6b4d710b 100644 --- a/lib/stdlib/src/queue.erl +++ b/lib/stdlib/src/queue.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. 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,14 +56,16 @@ new() -> {[],[]}. %{RearList,FrontList} %% O(1) --spec is_queue(term()) -> boolean(). +-spec is_queue(Term) -> boolean() when + Term :: term(). is_queue({R,F}) when is_list(R), is_list(F) -> true; is_queue(_) -> false. %% O(1) --spec is_empty(queue()) -> boolean(). +-spec is_empty(Q) -> boolean() when + Q :: queue(). is_empty({[],[]}) -> true; is_empty({In,Out}) when is_list(In), is_list(Out) -> @@ -72,14 +74,16 @@ is_empty(Q) -> erlang:error(badarg, [Q]). %% O(len(Q)) --spec len(queue()) -> non_neg_integer(). +-spec len(Q) -> non_neg_integer() when + Q :: queue(). len({R,F}) when is_list(R), is_list(F) -> length(R)+length(F); len(Q) -> erlang:error(badarg, [Q]). %% O(len(Q)) --spec to_list(queue()) -> list(). +-spec to_list(Q) -> list() when + Q :: queue(). to_list({In,Out}) when is_list(In), is_list(Out) -> Out++lists:reverse(In, []); to_list(Q) -> @@ -88,7 +92,8 @@ to_list(Q) -> %% Create queue from list %% %% O(length(L)) --spec from_list(list()) -> queue(). +-spec from_list(L) -> queue() when + L :: list(). from_list(L) when is_list(L) -> f2r(L); from_list(L) -> @@ -97,7 +102,9 @@ from_list(L) -> %% Return true or false depending on if element is in queue %% %% O(length(Q)) worst case --spec member(term(), queue()) -> boolean(). +-spec member(Item, Q) -> boolean() when + Item :: term(), + Q :: queue(). member(X, {R,F}) when is_list(R), is_list(F) -> lists:member(X, R) orelse lists:member(X, F); member(X, Q) -> @@ -110,7 +117,10 @@ member(X, Q) -> %% Put at least one element in each list, if it is cheap %% %% O(1) --spec in(term(), queue()) -> queue(). +-spec in(Item, Q1) -> Q2 when + Item :: term(), + Q1 :: queue(), + Q2 :: queue(). in(X, {[_]=In,[]}) -> {[X], In}; in(X, {In,Out}) when is_list(In), is_list(Out) -> @@ -122,7 +132,10 @@ in(X, Q) -> %% Put at least one element in each list, if it is cheap %% %% O(1) --spec in_r(term(), queue()) -> queue(). +-spec in_r(Item, Q1) -> Q2 when + Item :: term(), + Q1 :: queue(), + Q2 :: queue(). in_r(X, {[],[_]=F}) -> {F,[X]}; in_r(X, {R,F}) when is_list(R), is_list(F) -> @@ -133,7 +146,10 @@ in_r(X, Q) -> %% Take from head/front %% %% O(1) amortized, O(len(Q)) worst case --spec out(queue()) -> {'empty' | {'value',term()}, queue()}. +-spec out(Q1) -> Result when + Q1 :: queue(), + Q2 :: queue(), + Result :: {{value, Item :: term()}, Q2} | {empty, Q1}. out({[],[]}=Q) -> {empty,Q}; out({[V],[]}) -> @@ -151,7 +167,10 @@ out(Q) -> %% Take from tail/rear %% %% O(1) amortized, O(len(Q)) worst case --spec out_r(queue()) -> {'empty' | {'value',term()}, queue()}. +-spec out_r(Q1) -> Result when + Q1 :: queue(), + Q2 :: queue(), + Result :: {{value, Item :: term()}, Q2} | {empty, Q1}. out_r({[],[]}=Q) -> {empty,Q}; out_r({[],[V]}) -> @@ -172,7 +191,9 @@ out_r(Q) -> %% Return the first element in the queue %% %% O(1) since the queue is supposed to be well formed --spec get(queue()) -> term(). +-spec get(Q) -> Item when + Q :: queue(), + Item :: term(). get({[],[]}=Q) -> erlang:error(empty, [Q]); get({R,F}) when is_list(R), is_list(F) -> @@ -191,7 +212,9 @@ get([_|R], []) -> % malformed queue -> O(len(Q)) %% Return the last element in the queue %% %% O(1) since the queue is supposed to be well formed --spec get_r(queue()) -> term(). +-spec get_r(Q) -> Item when + Q :: queue(), + Item :: term(). get_r({[],[]}=Q) -> erlang:error(empty, [Q]); get_r({[H|_],F}) when is_list(F) -> @@ -206,7 +229,9 @@ get_r(Q) -> %% Return the first element in the queue %% %% O(1) since the queue is supposed to be well formed --spec peek(queue()) -> 'empty' | {'value',term()}. +-spec peek(Q) -> 'empty' | {'value',Item} when + Q :: queue(), + Item :: term(). peek({[],[]}) -> empty; peek({R,[H|_]}) when is_list(R) -> @@ -221,7 +246,9 @@ peek(Q) -> %% Return the last element in the queue %% %% O(1) since the queue is supposed to be well formed --spec peek_r(queue()) -> 'empty' | {'value',term()}. +-spec peek_r(Q) -> 'empty' | {'value',Item} when + Q :: queue(), + Item :: term(). peek_r({[],[]}) -> empty; peek_r({[H|_],F}) when is_list(F) -> @@ -236,7 +263,9 @@ peek_r(Q) -> %% Remove the first element and return resulting queue %% %% O(1) amortized --spec drop(queue()) -> queue(). +-spec drop(Q1) -> Q2 when + Q1 :: queue(), + Q2 :: queue(). drop({[],[]}=Q) -> erlang:error(empty, [Q]); drop({[_],[]}) -> @@ -254,7 +283,9 @@ drop(Q) -> %% Remove the last element and return resulting queue %% %% O(1) amortized --spec drop_r(queue()) -> queue(). +-spec drop_r(Q1) -> Q2 when + Q1 :: queue(), + Q2 :: queue(). drop_r({[],[]}=Q) -> erlang:error(empty, [Q]); drop_r({[],[_]}) -> @@ -275,7 +306,9 @@ drop_r(Q) -> %% Return reversed queue %% %% O(1) --spec reverse(queue()) -> queue(). +-spec reverse(Q1) -> Q2 when + Q1 :: queue(), + Q2 :: queue(). reverse({R,F}) when is_list(R), is_list(F) -> {F,R}; reverse(Q) -> @@ -285,7 +318,10 @@ reverse(Q) -> %% %% Q2 empty: O(1) %% else: O(len(Q1)) --spec join(queue(), queue()) -> queue(). +-spec join(Q1, Q2) -> Q3 when + Q1 :: queue(), + Q2 :: queue(), + Q3 :: queue(). join({R,F}=Q, {[],[]}) when is_list(R), is_list(F) -> Q; join({[],[]}, {R,F}=Q) when is_list(R), is_list(F) -> @@ -299,7 +335,11 @@ join(Q1, Q2) -> %% %% N = 0..len(Q) %% O(max(N, len(Q))) --spec split(non_neg_integer(), queue()) -> {queue(),queue()}. +-spec split(N, Q1) -> {Q2,Q3} when + N :: non_neg_integer(), + Q1 :: queue(), + Q2 :: queue(), + Q3 :: queue(). split(0, {R,F}=Q) when is_list(R), is_list(F) -> {{[],[]},Q}; split(N, {R,F}=Q) when is_integer(N), N >= 1, is_list(R), is_list(F) -> @@ -340,7 +380,10 @@ split_r1_to_f2(N, [X|R1], F1, R2, F2) -> %% %% Fun(_) -> List: O(length(List) * len(Q)) %% else: O(len(Q) --spec filter(fun((term()) -> boolean() | list()), queue()) -> queue(). +-spec filter(Fun, Q1) -> Q2 when + Fun :: fun((Item :: term()) -> boolean() | list()), + Q1 :: queue(), + Q2 :: queue(). filter(Fun, {R0,F0}) when is_function(Fun, 1), is_list(R0), is_list(F0) -> F = filter_f(Fun, F0), R = filter_r(Fun, R0), @@ -416,7 +459,10 @@ filter_r(Fun, [X|R0]) -> %% Cons to head %% --spec cons(term(), queue()) -> queue(). +-spec cons(Item, Q1) -> Q2 when + Item :: term(), + Q1 :: queue(), + Q2 :: queue(). cons(X, Q) -> in_r(X, Q). @@ -425,7 +471,9 @@ cons(X, Q) -> %% Return the first element in the queue %% %% O(1) since the queue is supposed to be well formed --spec head(queue()) -> term(). +-spec head(Q) -> Item when + Q :: queue(), + Item :: term(). head({[],[]}=Q) -> erlang:error(empty, [Q]); head({R,F}) when is_list(R), is_list(F) -> @@ -435,7 +483,9 @@ head(Q) -> %% Remove head element and return resulting queue %% --spec tail(queue()) -> queue(). +-spec tail(Q1) -> Q2 when + Q1 :: queue(), + Q2 :: queue(). tail(Q) -> drop(Q). @@ -443,22 +493,35 @@ tail(Q) -> %% Cons to tail %% --spec snoc(queue(), term()) -> queue(). +-spec snoc(Q1, Item) -> Q2 when + Q1 :: queue(), + Q2 :: queue(), + Item :: term(). snoc(Q, X) -> in(X, Q). %% Return last element --spec daeh(queue()) -> term(). +-spec daeh(Q) -> Item when + Q :: queue(), + Item :: term(). daeh(Q) -> get_r(Q). --spec last(queue()) -> term(). +-spec last(Q) -> Item when + Q :: queue(), + Item :: term(). last(Q) -> get_r(Q). %% Remove last element and return resulting queue --spec liat(queue()) -> queue(). +-spec liat(Q1) -> Q2 when + Q1 :: queue(), + Q2 :: queue(). liat(Q) -> drop_r(Q). --spec lait(queue()) -> queue(). +-spec lait(Q1) -> Q2 when + Q1 :: queue(), + Q2 :: queue(). lait(Q) -> drop_r(Q). %% Oops, mis-spelled 'tail' reversed. Forget this one. --spec init(queue()) -> queue(). +-spec init(Q1) -> Q2 when + Q1 :: queue(), + Q2 :: queue(). init(Q) -> drop_r(Q). %%-------------------------------------------------------------------------- diff --git a/lib/stdlib/src/random.erl b/lib/stdlib/src/random.erl index 01227c29b4..dbb524cc74 100644 --- a/lib/stdlib/src/random.erl +++ b/lib/stdlib/src/random.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -49,7 +49,10 @@ seed() -> %% seed({A1, A2, A3}) %% Seed random number generation --spec seed({integer(), integer(), integer()}) -> 'undefined' | ran(). +-spec seed({A1, A2, A3}) -> 'undefined' | ran() when + A1 :: integer(), + A2 :: integer(), + A3 :: integer(). seed({A1, A2, A3}) -> seed(A1, A2, A3). @@ -57,7 +60,10 @@ seed({A1, A2, A3}) -> %% seed(A1, A2, A3) %% Seed random number generation --spec seed(integer(), integer(), integer()) -> 'undefined' | ran(). +-spec seed(A1, A2, A3) -> 'undefined' | ran() when + A1 :: integer(), + A2 :: integer(), + A3 :: integer(). seed(A1, A2, A3) -> put(random_seed, @@ -93,7 +99,8 @@ uniform() -> %% Given an integer N >= 1, uniform(N) returns a random integer %% between 1 and N. --spec uniform(pos_integer()) -> pos_integer(). +-spec uniform(N) -> pos_integer() when + N :: pos_integer(). uniform(N) when is_integer(N), N >= 1 -> trunc(uniform() * N) + 1. @@ -104,7 +111,9 @@ uniform(N) when is_integer(N), N >= 1 -> %% uniform_s(State) -> {F, NewState} %% Returns a random float between 0 and 1. --spec uniform_s(ran()) -> {float(), ran()}. +-spec uniform_s(State0) -> {float(), State1} when + State0 :: ran(), + State1 :: ran(). uniform_s({A1, A2, A3}) -> B1 = (A1*171) rem 30269, @@ -117,7 +126,10 @@ uniform_s({A1, A2, A3}) -> %% Given an integer N >= 1, uniform(N) returns a random integer %% between 1 and N. --spec uniform_s(pos_integer(), ran()) -> {integer(), ran()}. +-spec uniform_s(N, State0) -> {integer(), State1} when + N :: pos_integer(), + State0 :: ran(), + State1 :: ran(). uniform_s(N, State0) when is_integer(N), N >= 1 -> {F, State1} = uniform_s(State0), diff --git a/lib/stdlib/src/re.erl b/lib/stdlib/src/re.erl index 296a6b3d23..e08258a535 100644 --- a/lib/stdlib/src/re.erl +++ b/lib/stdlib/src/re.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2010. All Rights Reserved. +%% Copyright Ericsson AB 2008-2011. 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,15 +19,46 @@ -module(re). -export([grun/3,urun/3,ucompile/2,replace/3,replace/4,split/2,split/3]). +%-opaque mp() :: {re_pattern, _, _, _}. +-type mp() :: {re_pattern, _, _, _}. + +-type nl_spec() :: cr | crlf | lf | anycrlf | any. + +-type compile_option() :: unicode | anchored | caseless | dollar_endonly + | dotall | extended | firstline | multiline + | no_auto_capture | dupnames | ungreedy + | {newline, nl_spec()}| bsr_anycrlf + | bsr_unicode. + %% Emulator builtins in this module: %% re:compile/1 %% re:compile/2 %% re:run/2 %% re:run/3 +-spec split(Subject, RE) -> SplitList when + Subject :: iodata() | unicode:charlist(), + RE :: mp() | iodata(), + SplitList :: [iodata() | unicode:charlist()]. + split(Subject,RE) -> split(Subject,RE,[]). +-spec split(Subject, RE, Options) -> SplitList when + Subject :: iodata() | unicode:charlist(), + RE :: mp() | iodata() | unicode:charlist(), + Options :: [ Option ], + Option :: anchored | global | notbol | noteol | notempty + | {offset, non_neg_integer()} | {newline, nl_spec()} + | bsr_anycrlf | bsr_unicode | {return, ReturnType} + | {parts, NumParts} | group | trim | CompileOpt, + NumParts :: non_neg_integer() | infinity, + ReturnType :: iodata | list | binary, + CompileOpt :: compile_option(), + SplitList :: [RetData] | [GroupedRetData], + GroupedRetData :: [RetData], + RetData :: iodata() | unicode:charlist() | binary() | list(). + split(Subject,RE,Options) -> try {NewOpt,Convert,Unicode,Limit,Strip,Group} = @@ -37,16 +68,16 @@ split(Subject,RE,Options) -> {error,_Err} -> throw(badre); {PreCompiled, NumSub, RunOpt} -> - % OK, lets run + %% OK, lets run case re:run(FlatSubject,PreCompiled,RunOpt ++ [global]) of nomatch -> case Group of true -> convert_any_split_result([[FlatSubject]], - Convert, Unicode,true); + Convert, Unicode, true); false -> convert_any_split_result([FlatSubject], - Convert, Unicode,false) + Convert, Unicode, false) end; {match, Matches} -> Res = do_split(FlatSubject, 0, Matches, NumSub, @@ -69,7 +100,7 @@ split(Subject,RE,Options) -> erlang:error(badarg,[Subject,RE,Options]) end. -backstrip_empty(List,false) -> +backstrip_empty(List, false) -> do_backstrip_empty(List); backstrip_empty(List, true) -> do_backstrip_empty_g(List). @@ -196,41 +227,52 @@ compile_split(Pat,Options0) when not is_tuple(Pat) -> end; compile_split(_,_) -> throw(badre). - - +-spec replace(Subject, RE, Replacement) -> iodata() | unicode:charlist() when + Subject :: iodata() | unicode:charlist(), + RE :: mp() | iodata(), + Replacement :: iodata() | unicode:charlist(). replace(Subject,RE,Replacement) -> replace(Subject,RE,Replacement,[]). + +-spec replace(Subject, RE, Replacement, Options) -> iodata() | unicode:charlist() when + Subject :: iodata() | unicode:charlist(), + RE :: mp() | iodata() | unicode:charlist(), + Replacement :: iodata() | unicode:charlist(), + Options :: [Option], + Option :: anchored | global | notbol | noteol | notempty + | {offset, non_neg_integer()} | {newline, NLSpec} | bsr_anycrlf + | bsr_unicode | {return, ReturnType} | CompileOpt, + ReturnType :: iodata | list | binary, + CompileOpt :: compile_option(), + NLSpec :: cr | crlf | lf | anycrlf | any. + replace(Subject,RE,Replacement,Options) -> try {NewOpt,Convert,Unicode} = process_repl_params(Options,iodata,false), FlatSubject = to_binary(Subject, Unicode), FlatReplacement = to_binary(Replacement, Unicode), - case do_replace(FlatSubject,Subject,RE,FlatReplacement,NewOpt) of - {error,_Err} -> - throw(badre); - IoList -> - case Convert of - iodata -> - IoList; - binary -> - case Unicode of - false -> - iolist_to_binary(IoList); - true -> - unicode:characters_to_binary(IoList,unicode) - end; - list -> - case Unicode of - false -> - binary_to_list(iolist_to_binary(IoList)); - true -> - unicode:characters_to_list(IoList,unicode) - end - end - end + IoList = do_replace(FlatSubject,Subject,RE,FlatReplacement,NewOpt), + case Convert of + iodata -> + IoList; + binary -> + case Unicode of + false -> + iolist_to_binary(IoList); + true -> + unicode:characters_to_binary(IoList,unicode) + end; + list -> + case Unicode of + false -> + binary_to_list(iolist_to_binary(IoList)); + true -> + unicode:characters_to_list(IoList,unicode) + end + end catch throw:badopt -> erlang:error(badarg,[Subject,RE,Replacement,Options]); @@ -239,7 +281,7 @@ replace(Subject,RE,Replacement,Options) -> error:badarg -> erlang:error(badarg,[Subject,RE,Replacement,Options]) end. - + do_replace(FlatSubject,Subject,RE,Replacement,Options) -> case re:run(FlatSubject,RE,Options) of @@ -314,7 +356,7 @@ apply_mlist(Subject,Replacement,Mlist) -> precomp_repl(<<>>) -> []; precomp_repl(<<$\\,X,Rest/binary>>) when X < $1 ; X > $9 -> - % Escaped character + %% Escaped character case precomp_repl(Rest) of [BHead | T0] when is_binary(BHead) -> [<<X,BHead/binary>> | T0]; @@ -524,7 +566,7 @@ process_uparams([H|T],Type) -> {[H|NL],NType}; process_uparams([],Type) -> {[],Type}. - + ucompile(RE,Options) -> try @@ -548,6 +590,7 @@ urun(Subject,RE,Options) -> [Subject,RE,Options])), erlang:raise(error,AnyError,[{Mod,run,L}|Rest]) end. + urun2(Subject0,RE0,Options0) -> {Options,RetType} = case (catch process_uparams(Options0,index)) of {A,B} -> @@ -573,7 +616,6 @@ urun2(Subject0,RE0,Options0) -> _ -> Ret end. - %% Might be called either with two-tuple (if regexp was already compiled) diff --git a/lib/stdlib/src/regexp.erl b/lib/stdlib/src/regexp.erl index 8f5994bbee..65f9ca247d 100644 --- a/lib/stdlib/src/regexp.erl +++ b/lib/stdlib/src/regexp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. 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,9 @@ -import(string, [substr/2,substr/3]). -import(lists, [reverse/1]). +-type errordesc() :: term(). +-opaque regexp() :: term(). + %% -type matchres() = {match,Start,Length} | nomatch | {error,E}. %% -type subres() = {ok,RepString,RepCount} | {error,E}. %% -type splitres() = {ok,[SubString]} | {error,E}. @@ -287,6 +290,10 @@ re_apply_or(R1, nomatch) -> R1. %% Convert a sh style regexp into a full AWK one. The main difficulty is %% getting character sets right as the conventions are different. +-spec sh_to_awk(ShRegExp) -> AwkRegExp when + ShRegExp :: string(), + AwkRegExp :: string(). + sh_to_awk(Sh) -> "^(" ++ sh_to_awk_1(Sh). %Fix the beginning sh_to_awk_1([$*|Sh]) -> %This matches any string @@ -336,6 +343,12 @@ special_char(_C) -> false. %% parse(RegExp) -> {ok,RE} | {error,E}. %% Parse the regexp described in the string RegExp. +-spec parse(RegExp) -> ParseRes when + RegExp :: string(), + ParseRes :: {ok, RE} | {error, Error}, + RE :: regexp(), + Error :: errordesc(). + parse(S) -> case catch reg(S) of {R,[]} -> {ok,R}; @@ -345,6 +358,10 @@ parse(S) -> %% format_error(Error) -> String. +-spec format_error(ErrorDescriptor) -> Chars when + ErrorDescriptor :: errordesc(), + Chars :: io_lib:chars(). + format_error({illegal,What}) -> ["illegal character `",What,"'"]; format_error({unterminated,What}) -> ["unterminated `",What,"'"]; format_error({char_class,What}) -> @@ -353,6 +370,14 @@ format_error({char_class,What}) -> %% -type match(String, RegExp) -> matchres(). %% Find the longest match of RegExp in String. +-spec match(String, RegExp) -> MatchRes when + String :: string(), + RegExp :: string() | regexp(), + MatchRes :: {match, Start, Length} | nomatch | {error, Error}, + Start :: pos_integer(), + Length :: pos_integer(), + Error :: errordesc(). + match(S, RegExp) when is_list(RegExp) -> case parse(RegExp) of {ok,RE} -> match(S, RE); @@ -378,6 +403,14 @@ match(RE, S, St, Pos, L) -> %% -type first_match(String, RegExp) -> matchres(). %% Find the first match of RegExp in String. +-spec first_match(String, RegExp) -> MatchRes when + String :: string(), + RegExp :: string() | regexp(), + MatchRes :: {match, Start, Length} | nomatch | {error, Error}, + Start :: pos_integer(), + Length :: pos_integer(), + Error :: errordesc(). + first_match(S, RegExp) when is_list(RegExp) -> case parse(RegExp) of {ok,RE} -> first_match(S, RE); @@ -400,6 +433,15 @@ first_match(_RE, [], _St) -> nomatch. %% -type matches(String, RegExp) -> {match,[{Start,Length}]} | {error,E}. %% Return the all the non-overlapping matches of RegExp in String. +-spec matches(String, RegExp) -> MatchRes when + String :: string(), + RegExp :: string() | regexp(), + MatchRes :: {match, Matches} | {error, Error}, + Matches :: [{Start, Length}], + Start :: pos_integer(), + Length :: pos_integer(), + Error :: errordesc(). + matches(S, RegExp) when is_list(RegExp) -> case parse(RegExp) of {ok,RE} -> matches(S, RE); @@ -420,6 +462,15 @@ matches(S, RE, St) -> %% the string Replace in String. Accept pre-parsed regular %% expressions. +-spec sub(String, RegExp, New) -> SubRes when + String :: string(), + RegExp :: string() | regexp(), + New :: string(), + NewString :: string(), + SubRes :: {ok, NewString, RepCount} | {error, Error}, + RepCount :: 0 | 1, + Error :: errordesc(). + sub(String, RegExp, Rep) when is_list(RegExp) -> case parse(RegExp) of {ok,RE} -> sub(String, RE, Rep); @@ -449,6 +500,15 @@ sub_repl([], _M, Rest) -> Rest. %% Substitute every match of the regular expression RegExp with the %% string New in String. Accept pre-parsed regular expressions. +-spec gsub(String, RegExp, New) -> SubRes when + String :: string(), + RegExp :: string() | regexp(), + New :: string(), + NewString :: string(), + SubRes :: {ok, NewString, RepCount} | {error, Error}, + RepCount :: non_neg_integer(), + Error :: errordesc(). + gsub(String, RegExp, Rep) when is_list(RegExp) -> case parse(RegExp) of {ok,RE} -> gsub(String, RE, Rep); @@ -462,6 +522,13 @@ gsub(String, RE, Rep) -> %% Split a string into substrings where the RegExp describes the %% field seperator. The RegExp " " is specially treated. +-spec split(String, RegExp) -> SplitRes when + String :: string(), + RegExp :: string() | regexp(), + SplitRes :: {ok, FieldList} | {error, Error}, + FieldList :: [string()], + Error :: errordesc(). + split(String, " ") -> %This is really special {ok,RE} = parse("[ \t]+"), case split_apply(String, RE, true) of diff --git a/lib/stdlib/src/sets.erl b/lib/stdlib/src/sets.erl index bcddca2567..3fd6c81e5f 100644 --- a/lib/stdlib/src/sets.erl +++ b/lib/stdlib/src/sets.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2009. All Rights Reserved. +%% Copyright Ericsson AB 2000-2011. 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 @@ -84,30 +84,38 @@ new() -> %% is_set(Set) -> boolean(). %% Return 'true' if Set is a set of elements, else 'false'. --spec is_set(term()) -> boolean(). +-spec is_set(Set) -> boolean() when + Set :: term(). is_set(#set{}) -> true; is_set(_) -> false. %% size(Set) -> int(). %% Return the number of elements in Set. --spec size(set()) -> non_neg_integer(). +-spec size(Set) -> non_neg_integer() when + Set :: set(). size(S) -> S#set.size. %% to_list(Set) -> [Elem]. %% Return the elements in Set as a list. --spec to_list(set()) -> [term()]. +-spec to_list(Set) -> List when + Set :: set(), + List :: [term()]. to_list(S) -> fold(fun (Elem, List) -> [Elem|List] end, [], S). %% from_list([Elem]) -> Set. %% Build a set from the elements in List. --spec from_list([term()]) -> set(). +-spec from_list(List) -> Set when + List :: [term()], + Set :: set(). from_list(L) -> lists:foldl(fun (E, S) -> add_element(E, S) end, new(), L). %% is_element(Element, Set) -> boolean(). %% Return 'true' if Element is an element of Set, else 'false'. --spec is_element(term(), set()) -> boolean(). +-spec is_element(Element, Set) -> boolean() when + Element :: term(), + Set :: set(). is_element(E, S) -> Slot = get_slot(S, E), Bkt = get_bucket(S, Slot), @@ -115,7 +123,10 @@ is_element(E, S) -> %% add_element(Element, Set) -> Set. %% Return Set with Element inserted in it. --spec add_element(term(), set()) -> set(). +-spec add_element(Element, Set1) -> Set2 when + Element :: term(), + Set1 :: set(), + Set2 :: set(). add_element(E, S0) -> Slot = get_slot(S0, E), {S1,Ic} = on_bucket(fun (B0) -> add_bkt_el(E, B0, B0) end, S0, Slot), @@ -129,7 +140,10 @@ add_bkt_el(E, [], Bkt) -> {[E|Bkt],1}. %% del_element(Element, Set) -> Set. %% Return Set but with Element removed. --spec del_element(term(), set()) -> set(). +-spec del_element(Element, Set1) -> Set2 when + Element :: term(), + Set1 :: set(), + Set2 :: set(). del_element(E, S0) -> Slot = get_slot(S0, E), {S1,Dc} = on_bucket(fun (B0) -> del_bkt_el(E, B0) end, S0, Slot), @@ -144,7 +158,10 @@ del_bkt_el(_, []) -> {[],0}. %% union(Set1, Set2) -> Set %% Return the union of Set1 and Set2. --spec union(set(), set()) -> set(). +-spec union(Set1, Set2) -> Set3 when + Set1 :: set(), + Set2 :: set(), + Set3 :: set(). union(S1, S2) when S1#set.size < S2#set.size -> fold(fun (E, S) -> add_element(E, S) end, S2, S1); union(S1, S2) -> @@ -152,7 +169,9 @@ union(S1, S2) -> %% union([Set]) -> Set %% Return the union of the list of sets. --spec union([set()]) -> set(). +-spec union(SetList) -> Set when + SetList :: [set()], + Set :: set(). union([S1,S2|Ss]) -> union1(union(S1, S2), Ss); union([S]) -> S; @@ -165,7 +184,10 @@ union1(S1, []) -> S1. %% intersection(Set1, Set2) -> Set. %% Return the intersection of Set1 and Set2. --spec intersection(set(), set()) -> set(). +-spec intersection(Set1, Set2) -> Set3 when + Set1 :: set(), + Set2 :: set(), + Set3 :: set(). intersection(S1, S2) when S1#set.size < S2#set.size -> filter(fun (E) -> is_element(E, S2) end, S1); intersection(S1, S2) -> @@ -173,7 +195,9 @@ intersection(S1, S2) -> %% intersection([Set]) -> Set. %% Return the intersection of the list of sets. --spec intersection([set(),...]) -> set(). +-spec intersection(SetList) -> Set when + SetList :: [set(),...], + Set :: set(). intersection([S1,S2|Ss]) -> intersection1(intersection(S1, S2), Ss); intersection([S]) -> S. @@ -185,7 +209,9 @@ intersection1(S1, []) -> S1. %% is_disjoint(Set1, Set2) -> boolean(). %% Check whether Set1 and Set2 are disjoint. --spec is_disjoint(set(), set()) -> boolean(). +-spec is_disjoint(Set1, Set2) -> boolean() when + Set1 :: set(), + Set2 :: set(). is_disjoint(S1, S2) when S1#set.size < S2#set.size -> fold(fun (_, false) -> false; (E, true) -> not is_element(E, S2) @@ -198,25 +224,39 @@ is_disjoint(S1, S2) -> %% subtract(Set1, Set2) -> Set. %% Return all and only the elements of Set1 which are not also in %% Set2. --spec subtract(set(), set()) -> set(). +-spec subtract(Set1, Set2) -> Set3 when + Set1 :: set(), + Set2 :: set(), + Set3 :: set(). subtract(S1, S2) -> filter(fun (E) -> not is_element(E, S2) end, S1). %% is_subset(Set1, Set2) -> boolean(). %% Return 'true' when every element of Set1 is also a member of %% Set2, else 'false'. --spec is_subset(set(), set()) -> boolean(). +-spec is_subset(Set1, Set2) -> boolean() when + Set1 :: set(), + Set2 :: set(). is_subset(S1, S2) -> fold(fun (E, Sub) -> Sub andalso is_element(E, S2) end, true, S1). %% fold(Fun, Accumulator, Set) -> Accumulator. %% Fold function Fun over all elements in Set and return Accumulator. --spec fold(fun((_,_) -> _), T, set()) -> T. +-spec fold(Function, Acc0, Set) -> Acc1 when + Function :: fun((E :: term(),AccIn) -> AccOut), + Set :: set(), + Acc0 :: T, + Acc1 :: T, + AccIn :: T, + AccOut :: T. fold(F, Acc, D) -> fold_set(F, Acc, D). %% filter(Fun, Set) -> Set. %% Filter Set with Fun. --spec filter(fun((_) -> boolean()), set()) -> set(). +-spec filter(Pred, Set1) -> Set2 when + Pred :: fun((E :: term()) -> boolean()), + Set1 :: set(), + Set2 :: set(). filter(F, D) -> filter_set(F, D). %% get_slot(Hashdb, Key) -> Slot. diff --git a/lib/stdlib/src/shell.erl b/lib/stdlib/src/shell.erl index ebb221c151..e3e23e09bc 100644 --- a/lib/stdlib/src/shell.erl +++ b/lib/stdlib/src/shell.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. 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 @@ -115,7 +115,9 @@ whereis_evaluator(Shell) -> %% Call this function to start a user restricted shell %% from a normal shell session. --spec start_restricted(module()) -> {'error', code:load_error_rsn()}. +-spec start_restricted(Module) -> {'error', Reason} when + Module :: module(), + Reason :: code:load_error_rsn(). start_restricted(RShMod) when is_atom(RShMod) -> case code:ensure_loaded(RShMod) of @@ -465,7 +467,7 @@ getc(N) -> {get({command,N}), get({result,N}), N}. get_cmd(Num, C) -> - case catch erl_eval:expr(Num, []) of + case catch erl_eval:expr(Num, erl_eval:new_bindings()) of {value,N,_} when N < 0 -> getc(C+N); {value,N,_} -> getc(N); _Other -> {undefined,undefined,undefined} @@ -1184,6 +1186,8 @@ 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]. @@ -1468,23 +1472,26 @@ set_env(App, Name, Val, Default) -> application_controller:set_env(App, Name, Val), Prev. --spec history(non_neg_integer()) -> non_neg_integer(). +-spec history(N) -> non_neg_integer() when + N :: non_neg_integer(). history(L) when is_integer(L), L >= 0 -> set_env(stdlib, shell_history_length, L, ?DEF_HISTORY). --spec results(non_neg_integer()) -> non_neg_integer(). +-spec results(N) -> non_neg_integer() when + N :: non_neg_integer(). results(L) when is_integer(L), L >= 0 -> set_env(stdlib, shell_saved_results, L, ?DEF_RESULTS). --spec catch_exception(boolean()) -> boolean(). +-spec catch_exception(Bool) -> Bool when + Bool :: boolean(). catch_exception(Bool) -> set_env(stdlib, shell_catch_exception, Bool, ?DEF_CATCH_EXCEPTION). --type prompt_func() :: 'default' | {module(),atom()}. --spec prompt_func(prompt_func()) -> prompt_func(). +-spec prompt_func(PromptFunc) -> PromptFunc when + PromptFunc :: 'default' | {module(),atom()}. prompt_func(String) -> set_env(stdlib, shell_prompt_func, String, ?DEF_PROMPT_FUNC). diff --git a/lib/stdlib/src/slave.erl b/lib/stdlib/src/slave.erl index 196b659938..de0179da59 100644 --- a/lib/stdlib/src/slave.erl +++ b/lib/stdlib/src/slave.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. 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 @@ -54,6 +54,10 @@ pseudo([Master | ServerList]) -> pseudo(_) -> error_msg("No master node given to slave:pseudo/1~n",[]). +-spec pseudo(Master, ServerList) -> ok when + Master :: node(), + ServerList :: [atom()]. + pseudo(_, []) -> ok; pseudo(Master, [S|Tail]) -> start_pseudo(S, whereis(S), Master), @@ -68,6 +72,9 @@ start_pseudo(_,_,_) -> ok. %% It's already there %% This relay can be used to relay all messages directed to a process. +-spec relay(Pid) -> no_return() when + Pid :: pid(). + relay({badrpc,Reason}) -> error_msg(" ** exiting relay server ~w :~w **~n", [self(),Reason]), exit(Reason); @@ -120,25 +127,61 @@ relay1(Pid) -> %% {error, no_rsh} | %% {error, {already_running, Name@Host}} +-spec start(Host) -> {ok, Node} | {error, Reason} when + Host :: atom(), + Node :: node(), + Reason :: timeout | no_rsh | {already_running, Node}. + start(Host) -> L = atom_to_list(node()), Name = upto($@, L), - start(Host, Name). + start(Host, Name, [], no_link). + +-spec start(Host, Name) -> {ok, Node} | {error, Reason} when + Host :: atom(), + Name :: atom(), + Node :: node(), + Reason :: timeout | no_rsh | {already_running, Node}. start(Host, Name) -> start(Host, Name, []). +-spec start(Host, Name, Args) -> {ok, Node} | {error, Reason} when + Host :: atom(), + Name :: atom(), + Args :: string(), + Node :: node(), + Reason :: timeout | no_rsh | {already_running, Node}. + start(Host, Name, Args) -> start(Host, Name, Args, no_link). +-spec start_link(Host) -> {ok, Node} | {error, Reason} when + Host :: atom(), + Node :: node(), + Reason :: timeout | no_rsh | {already_running, Node}. + start_link(Host) -> L = atom_to_list(node()), Name = upto($@, L), - start_link(Host, Name). + start(Host, Name, [], self()). + +-spec start_link(Host, Name) -> {ok, Node} | {error, Reason} when + Host :: atom(), + Name :: atom(), + Node :: node(), + Reason :: timeout | no_rsh | {already_running, Node}. start_link(Host, Name) -> start_link(Host, Name, []). +-spec start_link(Host, Name, Args) -> {ok, Node} | {error, Reason} when + Host :: atom(), + Name :: atom(), + Args :: string(), + Node :: node(), + Reason :: timeout | no_rsh | {already_running, Node}. + start_link(Host, Name, Args) -> start(Host, Name, Args, self()). @@ -163,6 +206,9 @@ start(Host0, Name, Args, LinkTo, Prog) -> %% Stops a running node. +-spec stop(Node) -> ok when + Node :: node(). + stop(Node) -> % io:format("stop(~p)~n", [Node]), rpc:call(Node, erlang, halt, []), diff --git a/lib/stdlib/src/sofs.erl b/lib/stdlib/src/sofs.erl index a83f803330..d38b8ab37a 100644 --- a/lib/stdlib/src/sofs.erl +++ b/lib/stdlib/src/sofs.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2001-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2001-2011. 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(sofs). @@ -40,11 +40,11 @@ substitution/2, projection/2, partition/1, partition/2, partition/3, multiple_relative_product/2, join/4]). --export([family_to_relation/1, family_specification/2, +-export([family_to_relation/1, family_specification/2, union_of_family/1, intersection_of_family/1, family_union/1, family_intersection/1, family_domain/1, family_range/1, family_field/1, - family_union/2, family_intersection/2, family_difference/2, + family_union/2, family_intersection/2, family_difference/2, partition_family/2, family_projection/2]). -export([family_to_digraph/1, family_to_digraph/2, @@ -64,9 +64,9 @@ -compile({inline, [{external_fun,1},{element_type,1}]}). --compile({inline, +-compile({inline, [{unify_types,2}, {match_types,2}, - {test_rel,3}, {symdiff,3}, + {test_rel,3}, {symdiff,3}, {subst,3}]}). -compile({inline, [{fam_binop,3}]}). @@ -80,13 +80,13 @@ -define(TAG, 'Set'). -define(ORDTAG, 'OrdSet'). --record(?TAG, {data = [], type = type}). --record(?ORDTAG, {orddata = {}, ordtype = type}). +-record(?TAG, {data = [] :: list(), type = type :: term()}). +-record(?ORDTAG, {orddata = {} :: tuple(), ordtype = type :: term()}). -define(LIST(S), (S)#?TAG.data). -define(TYPE(S), (S)#?TAG.type). -%%-define(SET(L, T), -%% case is_type(T) of +%%-define(SET(L, T), +%% case is_type(T) of %% true -> #?TAG{data = L, type = T}; %% false -> erlang:error(badtype, [T]) %% end @@ -113,14 +113,40 @@ -define(IS_SET_OF(X), is_list(X)). -define(FAMILY(X, Y), ?BINREL(X, ?SET_OF(Y))). +-export_type([anyset/0, binary_relation/0, external_set/0, a_function/0, + family/0, relation/0, set_of_sets/0, set_fun/0, spec_fun/0, + type/0]). +-export_type([ordset/0, a_set/0]). + +-type(anyset() :: ordset() | a_set()). +-type(binary_relation() :: relation()). +-type(external_set() :: term()). +-type(a_function() :: relation()). +-type(family() :: a_function()). +-opaque(ordset() :: #?ORDTAG{}). +-type(relation() :: a_set()). +-opaque(a_set() :: #?TAG{}). +-type(set_of_sets() :: a_set()). +-type(set_fun() :: pos_integer() + | {external, fun((external_set()) -> external_set())} + | fun((anyset()) -> anyset())). +-type(spec_fun() :: {external, fun((external_set()) -> boolean())} + | fun((anyset()) -> boolean())). +-type(type() :: term()). + +-type(tuple_of(_T) :: tuple()). + %% %% Exported functions %% -%%% +%%% %%% Create sets -%%% +%%% +-spec(from_term(Term) -> AnySet when + AnySet :: anyset(), + Term :: term()). from_term(T) -> Type = case T of _ when is_list(T) -> [?ANYTYPE]; @@ -133,6 +159,10 @@ from_term(T) -> Set end. +-spec(from_term(Term, Type) -> AnySet when + AnySet :: anyset(), + Term :: term(), + Type :: type()). from_term(L, T) -> case is_type(T) of true -> @@ -146,14 +176,23 @@ from_term(L, T) -> erlang:error(badarg, [L, T]) end. +-spec(from_external(ExternalSet, Type) -> AnySet when + ExternalSet :: external_set(), + AnySet :: anyset(), + Type :: type()). from_external(L, ?SET_OF(Type)) -> ?SET(L, Type); from_external(T, Type) -> ?ORDSET(T, Type). +-spec(empty_set() -> Set when + Set :: a_set()). empty_set() -> ?SET([], ?ANYTYPE). +-spec(is_type(Term) -> Bool when + Bool :: boolean(), + Term :: term()). is_type(Atom) when ?IS_ATOM_TYPE(Atom), Atom =/= ?ANYTYPE -> true; is_type(?SET_OF(T)) -> @@ -163,19 +202,26 @@ is_type(T) when tuple_size(T) > 0 -> is_type(_T) -> false. +-spec(set(Terms) -> Set when + Set :: a_set(), + Terms :: [term()]). set(L) -> case catch usort(L) of {'EXIT', _} -> erlang:error(badarg, [L]); - SL -> + SL -> ?SET(SL, ?ATOM_TYPE) end. +-spec(set(Terms, Type) -> Set when + Set :: a_set(), + Terms :: [term()], + Type :: type()). set(L, ?SET_OF(Type) = T) when ?IS_ATOM_TYPE(Type), Type =/= ?ANYTYPE -> case catch usort(L) of {'EXIT', _} -> erlang:error(badarg, [L, T]); - SL -> + SL -> ?SET(SL, Type) end; set(L, ?SET_OF(_) = T) -> @@ -188,6 +234,12 @@ set(L, ?SET_OF(_) = T) -> set(L, T) -> erlang:error(badarg, [L, T]). +-spec(from_sets(ListOfSets) -> Set when + Set :: a_set(), + ListOfSets :: [anyset()]; + (TupleOfSets) -> Ordset when + Ordset :: ordset(), + TupleOfSets :: tuple_of(anyset())). from_sets(Ss) when is_list(Ss) -> case set_of_sets(Ss, [], ?ANYTYPE) of {error, Error} -> @@ -205,6 +257,9 @@ from_sets(Tuple) when is_tuple(Tuple) -> from_sets(T) -> erlang:error(badarg, [T]). +-spec(relation(Tuples) -> Relation when + Relation :: relation(), + Tuples :: [tuple()]). relation([]) -> ?SET([], ?BINREL(?ATOM_TYPE, ?ATOM_TYPE)); relation(Ts = [T | _]) when is_tuple(T) -> @@ -217,6 +272,11 @@ relation(Ts = [T | _]) when is_tuple(T) -> relation(E) -> erlang:error(badarg, [E]). +-spec(relation(Tuples, Type) -> Relation when + N :: integer(), + Type :: N | type(), + Relation :: relation(), + Tuples :: [tuple()]). relation(Ts, TS) -> case catch rel(Ts, TS) of {'EXIT', _} -> @@ -225,6 +285,9 @@ relation(Ts, TS) -> Set end. +-spec(a_function(Tuples) -> Function when + Function :: a_function(), + Tuples :: [tuple()]). a_function(Ts) -> case catch func(Ts, ?BINREL(?ATOM_TYPE, ?ATOM_TYPE)) of {'EXIT', _} -> @@ -235,6 +298,10 @@ a_function(Ts) -> Set end. +-spec(a_function(Tuples, Type) -> Function when + Function :: a_function(), + Tuples :: [tuple()], + Type :: type()). a_function(Ts, T) -> case catch a_func(Ts, T) of {'EXIT', _} -> @@ -245,6 +312,9 @@ a_function(Ts, T) -> Set end. +-spec(family(Tuples) -> Family when + Family :: family(), + Tuples :: [tuple()]). family(Ts) -> case catch fam2(Ts, ?FAMILY(?ATOM_TYPE, ?ATOM_TYPE)) of {'EXIT', _} -> @@ -255,6 +325,10 @@ family(Ts) -> Set end. +-spec(family(Tuples, Type) -> Family when + Family :: family(), + Tuples :: [tuple()], + Type :: type()). family(Ts, T) -> case catch fam(Ts, T) of {'EXIT', _} -> @@ -265,20 +339,30 @@ family(Ts, T) -> Set end. -%%% +%%% %%% Functions on sets. -%%% +%%% +-spec(to_external(AnySet) -> ExternalSet when + ExternalSet :: external_set(), + AnySet :: anyset()). to_external(S) when ?IS_SET(S) -> ?LIST(S); to_external(S) when ?IS_ORDSET(S) -> ?ORDDATA(S). +-spec(type(AnySet) -> Type when + AnySet :: anyset(), + Type :: type()). type(S) when ?IS_SET(S) -> ?SET_OF(?TYPE(S)); type(S) when ?IS_ORDSET(S) -> ?ORDTYPE(S). +-spec(to_sets(ASet) -> Sets when + ASet :: a_set() | ordset(), + Sets :: tuple_of(AnySet) | [AnySet], + AnySet :: anyset()). to_sets(S) when ?IS_SET(S) -> case ?TYPE(S) of ?SET_OF(Type) -> list_of_sets(?LIST(S), Type, []); @@ -289,6 +373,9 @@ to_sets(S) when ?IS_ORDSET(S), is_tuple(?ORDTYPE(S)) -> to_sets(S) when ?IS_ORDSET(S) -> erlang:error(badarg, [S]). +-spec(no_elements(ASet) -> NoElements when + ASet :: a_set() | ordset(), + NoElements :: pos_integer()). no_elements(S) when ?IS_SET(S) -> length(?LIST(S)); no_elements(S) when ?IS_ORDSET(S), is_tuple(?ORDTYPE(S)) -> @@ -296,6 +383,10 @@ no_elements(S) when ?IS_ORDSET(S), is_tuple(?ORDTYPE(S)) -> no_elements(S) when ?IS_ORDSET(S) -> erlang:error(badarg, [S]). +-spec(specification(Fun, Set1) -> Set2 when + Fun :: spec_fun(), + Set1 :: a_set(), + Set2 :: a_set()). specification(Fun, S) when ?IS_SET(S) -> Type = ?TYPE(S), R = case external_fun(Fun) of @@ -311,36 +402,62 @@ specification(Fun, S) when ?IS_SET(S) -> erlang:error(Bad, [Fun, S]) end. +-spec(union(Set1, Set2) -> Set3 when + Set1 :: a_set(), + Set2 :: a_set(), + Set3 :: a_set()). union(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> case unify_types(?TYPE(S1), ?TYPE(S2)) of [] -> erlang:error(type_mismatch, [S1, S2]); Type -> ?SET(umerge(?LIST(S1), ?LIST(S2)), Type) end. +-spec(intersection(Set1, Set2) -> Set3 when + Set1 :: a_set(), + Set2 :: a_set(), + Set3 :: a_set()). intersection(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> case unify_types(?TYPE(S1), ?TYPE(S2)) of [] -> erlang:error(type_mismatch, [S1, S2]); Type -> ?SET(intersection(?LIST(S1), ?LIST(S2), []), Type) end. +-spec(difference(Set1, Set2) -> Set3 when + Set1 :: a_set(), + Set2 :: a_set(), + Set3 :: a_set()). difference(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> case unify_types(?TYPE(S1), ?TYPE(S2)) of [] -> erlang:error(type_mismatch, [S1, S2]); Type -> ?SET(difference(?LIST(S1), ?LIST(S2), []), Type) end. +-spec(symdiff(Set1, Set2) -> Set3 when + Set1 :: a_set(), + Set2 :: a_set(), + Set3 :: a_set()). symdiff(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> case unify_types(?TYPE(S1), ?TYPE(S2)) of [] -> erlang:error(type_mismatch, [S1, S2]); Type -> ?SET(symdiff(?LIST(S1), ?LIST(S2), []), Type) end. +-spec(symmetric_partition(Set1, Set2) -> {Set3, Set4, Set5} when + Set1 :: a_set(), + Set2 :: a_set(), + Set3 :: a_set(), + Set4 :: a_set(), + Set5 :: a_set()). symmetric_partition(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> case unify_types(?TYPE(S1), ?TYPE(S2)) of [] -> erlang:error(type_mismatch, [S1, S2]); Type -> sympart(?LIST(S1), ?LIST(S2), [], [], [], Type) end. +-spec(product(Set1, Set2) -> BinRel when + BinRel :: binary_relation(), + Set1 :: a_set(), + Set2 :: a_set()). product(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> if ?TYPE(S1) =:= ?ANYTYPE -> S1; @@ -351,6 +468,9 @@ product(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> ?SET(relprod(map(F, ?LIST(S1)), map(F, ?LIST(S2))), T) end. +-spec(product(TupleOfSets) -> Relation when + Relation :: relation(), + TupleOfSets :: tuple_of(a_set())). product({S1, S2}) -> product(S1, S2); product(T) when is_tuple(T) -> @@ -365,11 +485,15 @@ product(T) when is_tuple(T) -> case member([], L) of true -> empty_set(); - false -> + false -> ?SET(reverse(prod(L, [], [])), Type) end end. +-spec(constant_function(Set, AnySet) -> Function when + AnySet :: anyset(), + Function :: a_function(), + Set :: a_set()). constant_function(S, E) when ?IS_SET(S) -> case {?TYPE(S), is_sofs_set(E)} of {?ANYTYPE, true} -> S; @@ -381,6 +505,10 @@ constant_function(S, E) when ?IS_SET(S) -> constant_function(S, E) when ?IS_ORDSET(S) -> erlang:error(badarg, [S, E]). +-spec(is_equal(AnySet1, AnySet2) -> Bool when + AnySet1 :: anyset(), + AnySet2 :: anyset(), + Bool :: boolean()). is_equal(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> case match_types(?TYPE(S1), ?TYPE(S2)) of true -> ?LIST(S1) == ?LIST(S2); @@ -396,12 +524,19 @@ is_equal(S1, S2) when ?IS_SET(S1), ?IS_ORDSET(S2) -> is_equal(S1, S2) when ?IS_ORDSET(S1), ?IS_SET(S2) -> erlang:error(type_mismatch, [S1, S2]). +-spec(is_subset(Set1, Set2) -> Bool when + Bool :: boolean(), + Set1 :: a_set(), + Set2 :: a_set()). is_subset(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> case match_types(?TYPE(S1), ?TYPE(S2)) of true -> subset(?LIST(S1), ?LIST(S2)); false -> erlang:error(type_mismatch, [S1, S2]) end. +-spec(is_sofs_set(Term) -> Bool when + Bool :: boolean(), + Term :: term()). is_sofs_set(S) when ?IS_SET(S) -> true; is_sofs_set(S) when ?IS_ORDSET(S) -> @@ -409,16 +544,26 @@ is_sofs_set(S) when ?IS_ORDSET(S) -> is_sofs_set(_S) -> false. +-spec(is_set(AnySet) -> Bool when + AnySet :: anyset(), + Bool :: boolean()). is_set(S) when ?IS_SET(S) -> true; is_set(S) when ?IS_ORDSET(S) -> false. -is_empty_set(S) when ?IS_SET(S) -> +-spec(is_empty_set(AnySet) -> Bool when + AnySet :: anyset(), + Bool :: boolean()). +is_empty_set(S) when ?IS_SET(S) -> ?LIST(S) =:= []; is_empty_set(S) when ?IS_ORDSET(S) -> false. +-spec(is_disjoint(Set1, Set2) -> Bool when + Bool :: boolean(), + Set1 :: a_set(), + Set2 :: a_set()). is_disjoint(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> case match_types(?TYPE(S1), ?TYPE(S2)) of true -> @@ -433,6 +578,9 @@ is_disjoint(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> %%% Functions on set-of-sets. %%% +-spec(union(SetOfSets) -> Set when + Set :: a_set(), + SetOfSets :: set_of_sets()). union(Sets) when ?IS_SET(Sets) -> case ?TYPE(Sets) of ?SET_OF(Type) -> ?SET(lunion(?LIST(Sets)), Type); @@ -440,6 +588,9 @@ union(Sets) when ?IS_SET(Sets) -> _ -> erlang:error(badarg, [Sets]) end. +-spec(intersection(SetOfSets) -> Set when + Set :: a_set(), + SetOfSets :: set_of_sets()). intersection(Sets) when ?IS_SET(Sets) -> case ?LIST(Sets) of [] -> erlang:error(badarg, [Sets]); @@ -451,32 +602,41 @@ intersection(Sets) when ?IS_SET(Sets) -> end end. +-spec(canonical_relation(SetOfSets) -> BinRel when + BinRel :: binary_relation(), + SetOfSets :: set_of_sets()). canonical_relation(Sets) when ?IS_SET(Sets) -> ST = ?TYPE(Sets), case ST of ?SET_OF(?ANYTYPE) -> empty_set(); - ?SET_OF(Type) -> + ?SET_OF(Type) -> ?SET(can_rel(?LIST(Sets), []), ?BINREL(Type, ST)); ?ANYTYPE -> Sets; _ -> erlang:error(badarg, [Sets]) end. -%%% +%%% %%% Functions on binary relations only. -%%% +%%% rel2fam(R) -> relation_to_family(R). +-spec(relation_to_family(BinRel) -> Family when + Family :: family(), + BinRel :: binary_relation()). %% Inlined. relation_to_family(R) when ?IS_SET(R) -> case ?TYPE(R) of - ?BINREL(DT, RT) -> + ?BINREL(DT, RT) -> ?SET(rel2family(?LIST(R)), ?FAMILY(DT, RT)); ?ANYTYPE -> R; _Else -> erlang:error(badarg, [R]) end. +-spec(domain(BinRel) -> Set when + BinRel :: binary_relation(), + Set :: a_set()). domain(R) when ?IS_SET(R) -> case ?TYPE(R) of ?BINREL(DT, _) -> ?SET(dom(?LIST(R)), DT); @@ -484,6 +644,9 @@ domain(R) when ?IS_SET(R) -> _Else -> erlang:error(badarg, [R]) end. +-spec(range(BinRel) -> Set when + BinRel :: binary_relation(), + Set :: a_set()). range(R) when ?IS_SET(R) -> case ?TYPE(R) of ?BINREL(_, RT) -> ?SET(ran(?LIST(R), []), RT); @@ -491,35 +654,63 @@ range(R) when ?IS_SET(R) -> _ -> erlang:error(badarg, [R]) end. +-spec(field(BinRel) -> Set when + BinRel :: binary_relation(), + Set :: a_set()). %% In "Introduction to LOGIC", Suppes defines the field of a binary %% relation to be the union of the domain and the range (or %% counterdomain). field(R) -> union(domain(R), range(R)). +-spec(relative_product(ListOfBinRels) -> BinRel2 when + ListOfBinRels :: [BinRel, ...], + BinRel :: binary_relation(), + BinRel2 :: binary_relation()). +%% The following clause is kept for backward compatibility. +%% The list is due to Dialyzer's specs. relative_product(RT) when is_tuple(RT) -> - case relprod_n(RT, foo, false, false) of - {error, Reason} -> - erlang:error(Reason, [RT]); + relative_product(tuple_to_list(RT)); +relative_product(RL) when is_list(RL) -> + case relprod_n(RL, foo, false, false) of + {error, Reason} -> + erlang:error(Reason, [RL]); Reply -> Reply end. +-spec(relative_product(ListOfBinRels, BinRel1) -> BinRel2 when + ListOfBinRels :: [BinRel, ...], + BinRel :: binary_relation(), + BinRel1 :: binary_relation(), + BinRel2 :: binary_relation(); + (BinRel1, BinRel2) -> BinRel3 when + BinRel1 :: binary_relation(), + BinRel2 :: binary_relation(), + BinRel3 :: binary_relation()). relative_product(R1, R2) when ?IS_SET(R1), ?IS_SET(R2) -> relative_product1(converse(R1), R2); +%% The following clause is kept for backward compatibility. +%% The list is due to Dialyzer's specs. relative_product(RT, R) when is_tuple(RT), ?IS_SET(R) -> + relative_product(tuple_to_list(RT), R); +relative_product(RL, R) when is_list(RL), ?IS_SET(R) -> EmptyR = case ?TYPE(R) of ?BINREL(_, _) -> ?LIST(R) =:= []; ?ANYTYPE -> true; - _ -> erlang:error(badarg, [RT, R]) + _ -> erlang:error(badarg, [RL, R]) end, - case relprod_n(RT, R, EmptyR, true) of - {error, Reason} -> - erlang:error(Reason, [RT, R]); + case relprod_n(RL, R, EmptyR, true) of + {error, Reason} -> + erlang:error(Reason, [RL, R]); Reply -> Reply end. +-spec(relative_product1(BinRel1, BinRel2) -> BinRel3 when + BinRel1 :: binary_relation(), + BinRel2 :: binary_relation(), + BinRel3 :: binary_relation()). relative_product1(R1, R2) when ?IS_SET(R1), ?IS_SET(R2) -> {DTR1, RTR1} = case ?TYPE(R1) of ?BINREL(_, _) = R1T -> R1T; @@ -538,16 +729,23 @@ relative_product1(R1, R2) when ?IS_SET(R1), ?IS_SET(R2) -> false -> erlang:error(type_mismatch, [R1, R2]) end. +-spec(converse(BinRel1) -> BinRel2 when + BinRel1 :: binary_relation(), + BinRel2 :: binary_relation()). converse(R) when ?IS_SET(R) -> case ?TYPE(R) of ?BINREL(DT, RT) -> ?SET(converse(?LIST(R), []), ?BINREL(RT, DT)); ?ANYTYPE -> R; _ -> erlang:error(badarg, [R]) end. - + +-spec(image(BinRel, Set1) -> Set2 when + BinRel :: binary_relation(), + Set1 :: a_set(), + Set2 :: a_set()). image(R, S) when ?IS_SET(R), ?IS_SET(S) -> case ?TYPE(R) of - ?BINREL(DT, RT) -> + ?BINREL(DT, RT) -> case match_types(DT, ?TYPE(S)) of true -> ?SET(usort(restrict(?LIST(S), ?LIST(R))), RT); @@ -558,9 +756,13 @@ image(R, S) when ?IS_SET(R), ?IS_SET(S) -> _ -> erlang:error(badarg, [R, S]) end. +-spec(inverse_image(BinRel, Set1) -> Set2 when + BinRel :: binary_relation(), + Set1 :: a_set(), + Set2 :: a_set()). inverse_image(R, S) when ?IS_SET(R), ?IS_SET(S) -> case ?TYPE(R) of - ?BINREL(DT, RT) -> + ?BINREL(DT, RT) -> case match_types(RT, ?TYPE(S)) of true -> NL = restrict(?LIST(S), converse(?LIST(R), [])), @@ -572,17 +774,23 @@ inverse_image(R, S) when ?IS_SET(R), ?IS_SET(S) -> _ -> erlang:error(badarg, [R, S]) end. +-spec(strict_relation(BinRel1) -> BinRel2 when + BinRel1 :: binary_relation(), + BinRel2 :: binary_relation()). strict_relation(R) when ?IS_SET(R) -> case ?TYPE(R) of - Type = ?BINREL(_, _) -> + Type = ?BINREL(_, _) -> ?SET(strict(?LIST(R), []), Type); ?ANYTYPE -> R; _ -> erlang:error(badarg, [R]) end. - + +-spec(weak_relation(BinRel1) -> BinRel2 when + BinRel1 :: binary_relation(), + BinRel2 :: binary_relation()). weak_relation(R) when ?IS_SET(R) -> case ?TYPE(R) of - ?BINREL(DT, RT) -> + ?BINREL(DT, RT) -> case unify_types(DT, RT) of [] -> erlang:error(badarg, [R]); @@ -592,7 +800,12 @@ weak_relation(R) when ?IS_SET(R) -> ?ANYTYPE -> R; _ -> erlang:error(badarg, [R]) end. - + +-spec(extension(BinRel1, Set, AnySet) -> BinRel2 when + AnySet :: anyset(), + BinRel1 :: binary_relation(), + BinRel2 :: binary_relation(), + Set :: a_set()). extension(R, S, E) when ?IS_SET(R), ?IS_SET(S) -> case {?TYPE(R), ?TYPE(S), is_sofs_set(E)} of {T=?BINREL(DT, RT), ST, true} -> @@ -621,9 +834,12 @@ extension(R, S, E) when ?IS_SET(R), ?IS_SET(S) -> erlang:error(badarg, [R, S, E]) end. +-spec(is_a_function(BinRel) -> Bool when + Bool :: boolean(), + BinRel :: binary_relation()). is_a_function(R) when ?IS_SET(R) -> case ?TYPE(R) of - ?BINREL(_, _) -> + ?BINREL(_, _) -> case ?LIST(R) of [] -> true; [{V,_} | Es] -> is_a_func(Es, V) @@ -632,16 +848,28 @@ is_a_function(R) when ?IS_SET(R) -> _ -> erlang:error(badarg, [R]) end. +-spec(restriction(BinRel1, Set) -> BinRel2 when + BinRel1 :: binary_relation(), + BinRel2 :: binary_relation(), + Set :: a_set()). restriction(Relation, Set) -> restriction(1, Relation, Set). +-spec(drestriction(BinRel1, Set) -> BinRel2 when + BinRel1 :: binary_relation(), + BinRel2 :: binary_relation(), + Set :: a_set()). drestriction(Relation, Set) -> drestriction(1, Relation, Set). -%%% +%%% %%% Functions on functions only. -%%% +%%% +-spec(composite(Function1, Function2) -> Function3 when + Function1 :: a_function(), + Function2 :: a_function(), + Function3 :: a_function()). composite(Fn1, Fn2) when ?IS_SET(Fn1), ?IS_SET(Fn2) -> ?BINREL(DTF1, RTF1) = case ?TYPE(Fn1)of ?BINREL(_, _) = F1T -> F1T; @@ -656,7 +884,7 @@ composite(Fn1, Fn2) when ?IS_SET(Fn1), ?IS_SET(Fn2) -> case match_types(RTF1, DTF2) of true when DTF1 =:= ?ANYTYPE -> Fn1; true when DTF2 =:= ?ANYTYPE -> Fn2; - true -> + true -> case comp(?LIST(Fn1), ?LIST(Fn2)) of SL when is_list(SL) -> ?SET(sort(SL), ?BINREL(DTF1, RTF2)); @@ -666,9 +894,12 @@ composite(Fn1, Fn2) when ?IS_SET(Fn1), ?IS_SET(Fn2) -> false -> erlang:error(type_mismatch, [Fn1, Fn2]) end. +-spec(inverse(Function1) -> Function2 when + Function1 :: a_function(), + Function2 :: a_function()). inverse(Fn) when ?IS_SET(Fn) -> case ?TYPE(Fn) of - ?BINREL(DT, RT) -> + ?BINREL(DT, RT) -> case inverse1(?LIST(Fn)) of SL when is_list(SL) -> ?SET(SL, ?BINREL(RT, DT)); @@ -678,11 +909,16 @@ inverse(Fn) when ?IS_SET(Fn) -> ?ANYTYPE -> Fn; _ -> erlang:error(badarg, [Fn]) end. - -%%% + +%%% %%% Functions on relations (binary or other). -%%% +%%% +-spec(restriction(SetFun, Set1, Set2) -> Set3 when + SetFun :: set_fun(), + Set1 :: a_set(), + Set2 :: a_set(), + Set3 :: a_set()). %% Equivalent to range(restriction(inverse(substitution(Fun, S1)), S2)). restriction(I, R, S) when is_integer(I), ?IS_SET(R), ?IS_SET(S) -> RT = ?TYPE(R), @@ -747,6 +983,11 @@ restriction(SetFun, S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> end end. +-spec(drestriction(SetFun, Set1, Set2) -> Set3 when + SetFun :: set_fun(), + Set1 :: a_set(), + Set2 :: a_set(), + Set3 :: a_set()). drestriction(I, R, S) when is_integer(I), ?IS_SET(R), ?IS_SET(S) -> RT = ?TYPE(R), ST = ?TYPE(S), @@ -812,6 +1053,10 @@ drestriction(SetFun, S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> end end. +-spec(projection(SetFun, Set1) -> Set2 when + SetFun :: set_fun(), + Set1 :: a_set(), + Set2 :: a_set()). projection(I, Set) when is_integer(I), ?IS_SET(Set) -> Type = ?TYPE(Set), case check_for_sort(Type, I) of @@ -827,6 +1072,10 @@ projection(I, Set) when is_integer(I), ?IS_SET(Set) -> projection(Fun, Set) -> range(substitution(Fun, Set)). +-spec(substitution(SetFun, Set1) -> Set2 when + SetFun :: set_fun(), + Set1 :: a_set(), + Set2 :: a_set()). substitution(I, Set) when is_integer(I), ?IS_SET(Set) -> Type = ?TYPE(Set), case check_for_sort(Type, I) of @@ -867,11 +1116,18 @@ substitution(SetFun, Set) when ?IS_SET(Set) -> end end. +-spec(partition(SetOfSets) -> Partition when + SetOfSets :: set_of_sets(), + Partition :: a_set()). partition(Sets) -> F1 = relation_to_family(canonical_relation(Sets)), F2 = relation_to_family(converse(F1)), range(F2). +-spec(partition(SetFun, Set) -> Partition when + SetFun :: set_fun(), + Partition :: a_set(), + Set :: a_set()). partition(I, Set) when is_integer(I), ?IS_SET(Set) -> Type = ?TYPE(Set), case check_for_sort(Type, I) of @@ -887,6 +1143,12 @@ partition(I, Set) when is_integer(I), ?IS_SET(Set) -> partition(Fun, Set) -> range(partition_family(Fun, Set)). +-spec(partition(SetFun, Set1, Set2) -> {Set3, Set4} when + SetFun :: set_fun(), + Set1 :: a_set(), + Set2 :: a_set(), + Set3 :: a_set(), + Set4 :: a_set()). partition(I, R, S) when is_integer(I), ?IS_SET(R), ?IS_SET(S) -> RT = ?TYPE(R), ST = ?TYPE(S), @@ -954,21 +1216,32 @@ partition(SetFun, S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> end end. +-spec(multiple_relative_product(TupleOfBinRels, BinRel1) -> BinRel2 when + TupleOfBinRels :: tuple_of(BinRel), + BinRel :: binary_relation(), + BinRel1 :: binary_relation(), + BinRel2 :: binary_relation()). multiple_relative_product(T, R) when is_tuple(T), ?IS_SET(R) -> case test_rel(R, tuple_size(T), eq) of true when ?TYPE(R) =:= ?ANYTYPE -> empty_set(); - true -> + true -> MProd = mul_relprod(tuple_to_list(T), 1, R), - relative_product(list_to_tuple(MProd)); - false -> + relative_product(MProd); + false -> erlang:error(badarg, [T, R]) end. -join(R1, I1, R2, I2) +-spec(join(Relation1, I, Relation2, J) -> Relation3 when + Relation1 :: relation(), + Relation2 :: relation(), + Relation3 :: relation(), + I :: pos_integer(), + J :: pos_integer()). +join(R1, I1, R2, I2) when ?IS_SET(R1), ?IS_SET(R2), is_integer(I1), is_integer(I2) -> case test_rel(R1, I1, lte) and test_rel(R2, I2, lte) of - false -> + false -> erlang:error(badarg, [R1, I1, R2, I2]); true when ?TYPE(R1) =:= ?ANYTYPE -> R1; true when ?TYPE(R2) =:= ?ANYTYPE -> R2; @@ -980,8 +1253,8 @@ join(R1, I1, R2, I2) true -> fun({X,Y}) -> join_element(X, Y) end; false -> - fun({X,Y}) -> - list_to_tuple(join_element(X, Y, I2)) + fun({X,Y}) -> + list_to_tuple(join_element(X, Y, I2)) end end, ?SET(replace(T, F, []), F({?TYPE(R1), ?TYPE(R2)})) @@ -1001,9 +1274,15 @@ test_rel(R, I, C) -> %%% Family functions %%% +-spec(fam2rel(Family) -> BinRel when + Family :: family(), + BinRel :: binary_relation()). fam2rel(F) -> family_to_relation(F). +-spec(family_to_relation(Family) -> BinRel when + Family :: family(), + BinRel :: binary_relation()). %% Inlined. family_to_relation(F) when ?IS_SET(F) -> case ?TYPE(F) of @@ -1013,6 +1292,10 @@ family_to_relation(F) when ?IS_SET(F) -> _ -> erlang:error(badarg, [F]) end. +-spec(family_specification(Fun, Family1) -> Family2 when + Fun :: spec_fun(), + Family1 :: family(), + Family2 :: family()). family_specification(Fun, F) when ?IS_SET(F) -> case ?TYPE(F) of ?FAMILY(_DT, Type) = FType -> @@ -1032,6 +1315,9 @@ family_specification(Fun, F) when ?IS_SET(F) -> _ -> erlang:error(badarg, [Fun, F]) end. +-spec(union_of_family(Family) -> Set when + Family :: family(), + Set :: a_set()). union_of_family(F) when ?IS_SET(F) -> case ?TYPE(F) of ?FAMILY(_DT, Type) -> @@ -1040,6 +1326,9 @@ union_of_family(F) when ?IS_SET(F) -> _ -> erlang:error(badarg, [F]) end. +-spec(intersection_of_family(Family) -> Set when + Family :: family(), + Set :: a_set()). intersection_of_family(F) when ?IS_SET(F) -> case ?TYPE(F) of ?FAMILY(_DT, Type) -> @@ -1052,6 +1341,9 @@ intersection_of_family(F) when ?IS_SET(F) -> _ -> erlang:error(badarg, [F]) end. +-spec(family_union(Family1) -> Family2 when + Family1 :: family(), + Family2 :: family()). family_union(F) when ?IS_SET(F) -> case ?TYPE(F) of ?FAMILY(DT, ?SET_OF(Type)) -> @@ -1060,6 +1352,9 @@ family_union(F) when ?IS_SET(F) -> _ -> erlang:error(badarg, [F]) end. +-spec(family_intersection(Family1) -> Family2 when + Family1 :: family(), + Family2 :: family()). family_intersection(F) when ?IS_SET(F) -> case ?TYPE(F) of ?FAMILY(DT, ?SET_OF(Type)) -> @@ -1073,6 +1368,9 @@ family_intersection(F) when ?IS_SET(F) -> _ -> erlang:error(badarg, [F]) end. +-spec(family_domain(Family1) -> Family2 when + Family1 :: family(), + Family2 :: family()). family_domain(F) when ?IS_SET(F) -> case ?TYPE(F) of ?FAMILY(FDT, ?BINREL(DT, _)) -> @@ -1082,6 +1380,9 @@ family_domain(F) when ?IS_SET(F) -> _ -> erlang:error(badarg, [F]) end. +-spec(family_range(Family1) -> Family2 when + Family1 :: family(), + Family2 :: family()). family_range(F) when ?IS_SET(F) -> case ?TYPE(F) of ?FAMILY(DT, ?BINREL(_, RT)) -> @@ -1091,15 +1392,30 @@ family_range(F) when ?IS_SET(F) -> _ -> erlang:error(badarg, [F]) end. +-spec(family_field(Family1) -> Family2 when + Family1 :: family(), + Family2 :: family()). family_field(F) -> family_union(family_domain(F), family_range(F)). +-spec(family_union(Family1, Family2) -> Family3 when + Family1 :: family(), + Family2 :: family(), + Family3 :: family()). family_union(F1, F2) -> fam_binop(F1, F2, fun fam_union/3). +-spec(family_intersection(Family1, Family2) -> Family3 when + Family1 :: family(), + Family2 :: family(), + Family3 :: family()). family_intersection(F1, F2) -> fam_binop(F1, F2, fun fam_intersect/3). +-spec(family_difference(Family1, Family2) -> Family3 when + Family1 :: family(), + Family2 :: family(), + Family3 :: family()). family_difference(F1, F2) -> fam_binop(F1, F2, fun fam_difference/3). @@ -1108,13 +1424,17 @@ fam_binop(F1, F2, FF) when ?IS_SET(F1), ?IS_SET(F2) -> case unify_types(?TYPE(F1), ?TYPE(F2)) of [] -> erlang:error(type_mismatch, [F1, F2]); - ?ANYTYPE -> + ?ANYTYPE -> F1; - Type = ?FAMILY(_, _) -> + Type = ?FAMILY(_, _) -> ?SET(FF(?LIST(F1), ?LIST(F2), []), Type); _ -> erlang:error(badarg, [F1, F2]) end. +-spec(partition_family(SetFun, Set) -> Family when + Family :: family(), + SetFun :: set_fun(), + Set :: a_set()). partition_family(I, Set) when is_integer(I), ?IS_SET(Set) -> Type = ?TYPE(Set), case check_for_sort(Type, I) of @@ -1159,8 +1479,12 @@ partition_family(SetFun, Set) when ?IS_SET(Set) -> end end. +-spec(family_projection(SetFun, Family1) -> Family2 when + SetFun :: set_fun(), + Family1 :: family(), + Family2 :: family()). family_projection(SetFun, F) when ?IS_SET(F) -> - case ?TYPE(F) of + case ?TYPE(F) of ?FAMILY(_, _) when [] =:= ?LIST(F) -> empty_set(); ?FAMILY(DT, Type) -> @@ -1172,7 +1496,7 @@ family_projection(SetFun, F) when ?IS_SET(F) -> Bad -> erlang:error(Bad, [SetFun, F]) end; - _ -> + _ -> erlang:error(badarg, [SetFun, F]) end; ?ANYTYPE -> F; @@ -1183,6 +1507,9 @@ family_projection(SetFun, F) when ?IS_SET(F) -> %%% Digraph functions %%% +-spec(family_to_digraph(Family) -> Graph when + Graph :: digraph(), + Family :: family()). family_to_digraph(F) when ?IS_SET(F) -> case ?TYPE(F) of ?FAMILY(_, _) -> fam2digraph(F, digraph:new()); @@ -1190,6 +1517,10 @@ family_to_digraph(F) when ?IS_SET(F) -> _Else -> erlang:error(badarg, [F]) end. +-spec(family_to_digraph(Family, GraphType) -> Graph when + Graph :: digraph(), + Family :: family(), + GraphType :: [digraph:d_type()]). family_to_digraph(F, Type) when ?IS_SET(F) -> case ?TYPE(F) of ?FAMILY(_, _) -> ok; @@ -1208,12 +1539,19 @@ family_to_digraph(F, Type) when ?IS_SET(F) -> error:badarg -> erlang:error(badarg, [F, Type]) end. +-spec(digraph_to_family(Graph) -> Family when + Graph :: digraph(), + Family :: family()). digraph_to_family(G) -> case catch digraph_family(G) of {'EXIT', _} -> erlang:error(badarg, [G]); L -> ?SET(L, ?FAMILY(?ATOM_TYPE, ?ATOM_TYPE)) end. +-spec(digraph_to_family(Graph, Type) -> Family when + Graph :: digraph(), + Family :: family(), + Type :: type()). digraph_to_family(G, T) -> case {is_type(T), T} of {true, ?SET_OF(?FAMILY(_,_) = Type)} -> @@ -1284,7 +1622,7 @@ rel(Ts, [Type]) -> end; rel(Ts, Sz) -> rel(Ts, Sz, erlang:make_tuple(Sz, ?ATOM_TYPE)). - + atoms_only(Type, I) when ?IS_ATOM_TYPE(?REL_TYPE(I, Type)) -> atoms_only(Type, I+1); atoms_only(Type, I) when I > tuple_size(Type), ?IS_RELATION(Type) -> @@ -1312,7 +1650,7 @@ rel_type([], SL, Type) when ?IS_RELATION(Type) -> %% Inlined. a_func(Ts, T) -> case {T, is_type(T)} of - {[?BINREL(DT, RT) = Type], true} when ?IS_ATOM_TYPE(DT), + {[?BINREL(DT, RT) = Type], true} when ?IS_ATOM_TYPE(DT), ?IS_ATOM_TYPE(RT) -> func(Ts, Type); {[Type], true} -> @@ -1333,16 +1671,16 @@ func([], _X0, L, Type) -> %% Inlined. fam(Ts, T) -> case {T, is_type(T)} of - {[?FAMILY(DT, RT) = Type], true} when ?IS_ATOM_TYPE(DT), + {[?FAMILY(DT, RT) = Type], true} when ?IS_ATOM_TYPE(DT), ?IS_ATOM_TYPE(RT) -> fam2(Ts, Type); {[Type], true} -> func_type(Ts, [], Type, fun(?FAMILY(_,_)) -> true end) end. -fam2([], Type) -> +fam2([], Type) -> ?SET([], Type); -fam2(Ts, Type) -> +fam2(Ts, Type) -> fam2(sort(Ts), Ts, [], Type). fam2([{I,L} | T], I0, SL, Type) when I /= I0 -> @@ -1383,7 +1721,7 @@ setify(E, Type0) -> {Type, OrdSet} = make_element(E, Type0, Type0), ?ORDSET(OrdSet, Type). -is_no_lists(T) when is_tuple(T) -> +is_no_lists(T) when is_tuple(T) -> Sz = tuple_size(T), is_no_lists(T, Sz, Sz, []). @@ -1404,7 +1742,7 @@ create([], T, _T0, L) -> make_element(C, ?ANYTYPE, _T0) -> make_element(C); -make_element(C, Atom, ?ANYTYPE) when ?IS_ATOM_TYPE(Atom), +make_element(C, Atom, ?ANYTYPE) when ?IS_ATOM_TYPE(Atom), not is_list(C), not is_tuple(C) -> {Atom, C}; make_element(C, Atom, Atom) when ?IS_ATOM_TYPE(Atom) -> @@ -1585,12 +1923,12 @@ sympart([H1 | T1], [H2 | T2], L1, L12, L2, T) when H1 == H2 -> sympart([H1 | T1], [H2 | T2], L1, L12, L2, T) -> sympart2(T1, T2, L1, L12, [H2 | L2], T, H1); sympart(S1, [], L1, L12, L2, T) -> - {?SET(reverse(L1, S1), T), - ?SET(reverse(L12), T), + {?SET(reverse(L1, S1), T), + ?SET(reverse(L12), T), ?SET(reverse(L2), T)}; sympart(_, S2, L1, L12, L2, T) -> - {?SET(reverse(L1), T), - ?SET(reverse(L12), T), + {?SET(reverse(L1), T), + ?SET(reverse(L12), T), ?SET(reverse(L2, S2), T)}. sympart1([H1 | T1], T2, L1, L12, L2, T, H2) when H1 < H2 -> @@ -1600,8 +1938,8 @@ sympart1([H1 | T1], T2, L1, L12, L2, T, H2) when H1 == H2 -> sympart1([H1 | T1], T2, L1, L12, L2, T, H2) -> sympart2(T1, T2, L1, L12, [H2 | L2], T, H1); sympart1(_, T2, L1, L12, L2, T, H2) -> - {?SET(reverse(L1), T), - ?SET(reverse(L12), T), + {?SET(reverse(L1), T), + ?SET(reverse(L12), T), ?SET(reverse(L2, [H2 | T2]), T)}. sympart2(T1, [H2 | T2], L1, L12, L2, T, H1) when H1 > H2 -> @@ -1611,8 +1949,8 @@ sympart2(T1, [H2 | T2], L1, L12, L2, T, H1) when H1 == H2 -> sympart2(T1, [H2 | T2], L1, L12, L2, T, H1) -> sympart1(T1, T2, [H1 | L1], L12, L2, T, H2); sympart2(T1, _, L1, L12, L2, T, H1) -> - {?SET(reverse(L1, [H1 | T1]), T), - ?SET(reverse(L12), T), + {?SET(reverse(L1, [H1 | T1]), T), + ?SET(reverse(L12), T), ?SET(reverse(L2), T)}. prod([[E | Es] | Xs], T, L) -> @@ -1660,7 +1998,7 @@ lunion([[] | Ls]) -> lunion(Ls); lunion([S | Ss]) -> umerge(lunion(Ss, last(S), [S], [])); -lunion([]) -> +lunion([]) -> []. lunion([[E] = S | Ss], Last, SL, Ls) when E > Last -> % optimization @@ -1669,7 +2007,7 @@ lunion([S | Ss], Last, SL, Ls) when hd(S) > Last -> lunion(Ss, last(S), [S | SL], Ls); lunion([S | Ss], _Last, SL, Ls) -> lunion(Ss, last(S), [S], [append(reverse(SL)) | Ls]); -lunion([], _Last, SL, Ls) -> +lunion([], _Last, SL, Ls) -> [append(reverse(SL)) | Ls]. %% The empty list is always the first list, if present. @@ -1752,18 +2090,17 @@ relprod(B0, Bx0, By0, A0, L, Ax, [{Bx,By} | B], Ay) when Ay == Bx -> relprod(B0, Bx0, By0, A0, L, _Ax, _B, _Ay) -> relprod2(B0, Bx0, By0, A0, L). -relprod_n({}, _R, _EmptyG, _IsR) -> +relprod_n([], _R, _EmptyG, _IsR) -> {error, badarg}; -relprod_n(RT, R, EmptyR, IsR) -> - RL = tuple_to_list(RT), +relprod_n(RL, R, EmptyR, IsR) -> case domain_type(RL, ?ANYTYPE) of - Error = {error, _Reason} -> + Error = {error, _Reason} -> Error; DType -> Empty = any(fun is_empty_set/1, RL) or EmptyR, RType = range_type(RL, []), Type = ?BINREL(DType, RType), - Prod = + Prod = case Empty of true when DType =:= ?ANYTYPE; RType =:= ?ANYTYPE -> empty_set(); @@ -1771,7 +2108,7 @@ relprod_n(RT, R, EmptyR, IsR) -> ?SET([], Type); false -> TL = ?LIST((relprod_n(RL))), - Sz = tuple_size(RT), + Sz = length(RL), Fun = fun({X,A}) -> {X, flat(Sz, A, [])} end, ?SET(map(Fun, TL), Type) end, @@ -1799,12 +2136,12 @@ flat(N, {T,A}, L) -> domain_type([T | Ts], T0) when ?IS_SET(T) -> case ?TYPE(T) of - ?BINREL(DT, _RT) -> + ?BINREL(DT, _RT) -> case unify_types(DT, T0) of [] -> {error, type_mismatch}; T1 -> domain_type(Ts, T1) end; - ?ANYTYPE -> + ?ANYTYPE -> domain_type(Ts, T0); _ -> {error, badarg} end; @@ -1813,12 +2150,12 @@ domain_type([], T0) -> range_type([T | Ts], L) -> case ?TYPE(T) of - ?BINREL(_DT, RT) -> + ?BINREL(_DT, RT) -> range_type(Ts, [RT | L]); - ?ANYTYPE -> + ?ANYTYPE -> ?ANYTYPE end; -range_type([], L) -> +range_type([], L) -> list_to_tuple(reverse(L)). converse([{A,B} | X], L) -> @@ -1861,7 +2198,7 @@ weak1([E={X,_Y} | Es], Ys, L, X0) when X == X0 -> % when X < Y weak1(Es, Ys, L, X) -> weak(Es, Ys, [{X,X} | L]). -weak2([E={X,_Y} | Es], Ys, L, X0) when X == X0 -> % when X < _Y +weak2([E={X,_Y} | Es], Ys, L, X0) when X == X0 -> % when X < _Y weak2(Es, Ys, [E | L], X); weak2(Es, Ys, L, _X) -> weak(Es, Ys, L). @@ -1910,7 +2247,7 @@ restrict_n(I, [T | Ts], Key, Keys, L) -> end; restrict_n(_I, _Ts, _Key, _Keys, L) -> L. - + restrict_n(I, K, Ts, [Key | Keys], L, E) when K > Key -> restrict_n(I, K, Ts, Keys, L, E); restrict_n(I, K, Ts, [Key | Keys], L, E) when K == Key -> @@ -1933,7 +2270,7 @@ restrict([{K,E} | Ts], _Key, Keys, L) -> restrict(Ts, K, Keys, L, E); restrict(_Ts, _Key, _Keys, L) -> L. - + restrict(Ts, K, [Key | Keys], L, E) when K > Key -> restrict(Ts, K, Keys, L, E); restrict(Ts, K, [Key | Keys], L, E) when K == Key -> @@ -1956,7 +2293,7 @@ diff_restrict_n(I, _Ts, _Key, _Keys, L) when I =:= 1 -> reverse(L); diff_restrict_n(_I, _Ts, _Key, _Keys, L) -> sort(L). - + diff_restrict_n(I, K, Ts, [Key | Keys], L, T) when K > Key -> diff_restrict_n(I, K, Ts, Keys, L, T); diff_restrict_n(I, K, Ts, [Key | Keys], L, _T) when K == Key -> @@ -1981,7 +2318,7 @@ diff_restrict([{K,E} | Ts], _Key, Keys, L) -> diff_restrict(Ts, K, Keys, L, E); diff_restrict(_Ts, _Key, _Keys, L) -> L. - + diff_restrict(Ts, K, [Key | Keys], L, E) when K > Key -> diff_restrict(Ts, K, Keys, L, E); diff_restrict(Ts, K, [Key | Keys], L, _E) when K == Key -> @@ -2041,7 +2378,7 @@ external_fun({external, Function}) when is_atom(Function) -> false; external_fun({external, Fun}) -> Fun; -external_fun(_) -> +external_fun(_) -> false. %% Inlined. @@ -2121,7 +2458,7 @@ partition3_n(I, _Ts, _Key, _Keys, L1, L2) when I =:= 1 -> [reverse(L1) | reverse(L2)]; partition3_n(_I, _Ts, _Key, _Keys, L1, L2) -> [sort(L1) | sort(L2)]. - + partition3_n(I, K, Ts, [Key | Keys], L1, L2, T) when K > Key -> partition3_n(I, K, Ts, Keys, L1, L2, T); partition3_n(I, K, Ts, [Key | Keys], L1, L2, T) when K == Key -> @@ -2146,7 +2483,7 @@ partition3([{K,E} | Ts], _Key, Keys, L1, L2) -> partition3(Ts, K, Keys, L1, L2, E); partition3(_Ts, _Key, _Keys, L1, L2) -> [L1 | L2]. - + partition3(Ts, K, [Key | Keys], L1, L2, E) when K > Key -> partition3(Ts, K, Keys, L1, L2, E); partition3(Ts, K, [Key | Keys], L1, L2, E) when K == Key -> @@ -2192,7 +2529,7 @@ join_element(E1, E2, I2) -> join_element2([B | Bs], C, I2) when C =/= I2 -> [B | join_element2(Bs, C+1, I2)]; -join_element2([_ | Bs], _C, _I2) -> +join_element2([_ | Bs], _C, _I2) -> Bs. family2rel([{X,S} | F], L) -> @@ -2297,7 +2634,7 @@ check_function([{X,_} | XL], R) -> check_function(X, XL, R); check_function([], R) -> R. - + check_function(X0, [{X,_} | XL], R) when X0 /= X -> check_function(X, XL, R); check_function(X0, [{X,_} | _XL], _R) when X0 == X -> @@ -2371,14 +2708,14 @@ term2set(T, Type) -> ?ORDSET(T, Type). fam2digraph(F, G) -> - Fun = fun({From, ToL}) -> + Fun = fun({From, ToL}) -> digraph:add_vertex(G, From), Fun2 = fun(To) -> digraph:add_vertex(G, To), case digraph:add_edge(G, From, To) of - {error, {bad_edge, _}} -> + {error, {bad_edge, _}} -> throw({error, cyclic}); - _ -> + _ -> true end end, @@ -2397,7 +2734,7 @@ digraph_fam([V | Vs], V0, G, L) when V /= V0 -> digraph_fam([], _V0, _G, L) -> reverse(L). -%% -> bool() +%% -> boolean() check_fun(T, F, FunT) -> true = is_type(FunT), {NT, _MaxI} = number_tuples(T, 1), @@ -2424,7 +2761,7 @@ check_for_sort(T, _I) when T =:= ?ANYTYPE -> check_for_sort(T, I) when ?IS_RELATION(T), I =< ?REL_ARITY(T), I >= 1 -> I > 1; check_for_sort(_T, _I) -> - error. + error. inverse_substitution(L, Fun, Sort) -> %% One easily sees that the inverse of the tuples created by @@ -2477,11 +2814,11 @@ match_types(Type1, Type2) -> match_types1(Type1, Type2). match_types1(Atom, Atom) when ?IS_ATOM_TYPE(Atom) -> true; -match_types1(?ANYTYPE, _) -> +match_types1(?ANYTYPE, _) -> true; -match_types1(_, ?ANYTYPE) -> +match_types1(_, ?ANYTYPE) -> true; -match_types1(?SET_OF(Type1), ?SET_OF(Type2)) -> +match_types1(?SET_OF(Type1), ?SET_OF(Type2)) -> match_types1(Type1, Type2); match_types1(T1, T2) when tuple_size(T1) =:= tuple_size(T2) -> match_typesl(tuple_size(T1), T1, T2); diff --git a/lib/stdlib/src/stdlib.app.src b/lib/stdlib/src/stdlib.app.src index 3e52c48e42..9d15f01683 100644 --- a/lib/stdlib/src/stdlib.app.src +++ b/lib/stdlib/src/stdlib.app.src @@ -1,20 +1,20 @@ %% This is an -*- erlang -*- file. %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1996-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% %% {application, stdlib, @@ -23,6 +23,7 @@ {modules, [array, base64, beam_lib, + binary, c, calendar, dets, diff --git a/lib/stdlib/src/string.erl b/lib/stdlib/src/string.erl index 6636a03f06..30eac4f07d 100644 --- a/lib/stdlib/src/string.erl +++ b/lib/stdlib/src/string.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. 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,23 +29,23 @@ %%--------------------------------------------------------------------------- --type direction() :: 'left' | 'right' | 'both'. - -%%--------------------------------------------------------------------------- - %% Robert's bit %% len(String) %% Return the length of a string. --spec len(string()) -> non_neg_integer(). +-spec len(String) -> Length when + String :: string(), + Length :: non_neg_integer(). len(S) -> length(S). %% equal(String1, String2) %% Test if 2 strings are equal. --spec equal(string(), string()) -> boolean(). +-spec equal(String1, String2) -> boolean() when + String1 :: string(), + String2 :: string(). equal(S, S) -> true; equal(_, _) -> false. @@ -53,7 +53,10 @@ equal(_, _) -> false. %% concat(String1, String2) %% Concatenate 2 strings. --spec concat(string(), string()) -> string(). +-spec concat(String1, String2) -> String3 when + String1 :: string(), + String2 :: string(), + String3 :: string(). concat(S1, S2) -> S1 ++ S2. @@ -61,7 +64,10 @@ concat(S1, S2) -> S1 ++ S2. %% rchr(String, Char) %% Return the first/last index of the character in a string. --spec chr(string(), char()) -> non_neg_integer(). +-spec chr(String, Character) -> Index when + String :: string(), + Character :: char(), + Index :: non_neg_integer(). chr(S, C) when is_integer(C) -> chr(S, C, 1). @@ -69,7 +75,10 @@ chr([C|_Cs], C, I) -> I; chr([_|Cs], C, I) -> chr(Cs, C, I+1); chr([], _C, _I) -> 0. --spec rchr(string(), char()) -> non_neg_integer(). +-spec rchr(String, Character) -> Index when + String :: string(), + Character :: char(), + Index :: non_neg_integer(). rchr(S, C) when is_integer(C) -> rchr(S, C, 1, 0). @@ -85,7 +94,10 @@ rchr([], _C, _I, L) -> L. %% Return the first/last index of the sub-string in a string. %% index/2 is kept for backwards compatibility. --spec str(string(), string()) -> non_neg_integer(). +-spec str(String, SubString) -> Index when + String :: string(), + SubString :: string(), + Index :: non_neg_integer(). str(S, Sub) when is_list(Sub) -> str(S, Sub, 1). @@ -97,7 +109,10 @@ str([C|S], [C|Sub], I) -> str([_|S], Sub, I) -> str(S, Sub, I+1); str([], _Sub, _I) -> 0. --spec rstr(string(), string()) -> non_neg_integer(). +-spec rstr(String, SubString) -> Index when + String :: string(), + SubString :: string(), + Index :: non_neg_integer(). rstr(S, Sub) when is_list(Sub) -> rstr(S, Sub, 1, 0). @@ -116,7 +131,10 @@ prefix(Pre, String) when is_list(Pre), is_list(String) -> false. %% span(String, Chars) -> Length. %% cspan(String, Chars) -> Length. --spec span(string(), string()) -> non_neg_integer(). +-spec span(String, Chars) -> Length when + String :: string(), + Chars :: string(), + Length :: non_neg_integer(). span(S, Cs) when is_list(Cs) -> span(S, Cs, 0). @@ -127,7 +145,10 @@ span([C|S], Cs, I) -> end; span([], _Cs, I) -> I. --spec cspan(string(), string()) -> non_neg_integer(). +-spec cspan(String, Chars) -> Length when + String :: string(), + Chars :: string(), + Length :: non_neg_integer(). cspan(S, Cs) when is_list(Cs) -> cspan(S, Cs, 0). @@ -142,14 +163,21 @@ cspan([], _Cs, I) -> I. %% substr(String, Start, Length) %% Extract a sub-string from String. --spec substr(string(), pos_integer()) -> string(). +-spec substr(String, Start) -> SubString when + String :: string(), + SubString :: string(), + Start :: pos_integer(). substr(String, 1) when is_list(String) -> String; substr(String, S) when is_integer(S), S > 1 -> substr2(String, S). --spec substr(string(), pos_integer(), non_neg_integer()) -> string(). +-spec substr(String, Start, Length) -> SubString when + String :: string(), + SubString :: string(), + Start :: pos_integer(), + Length :: non_neg_integer(). substr(String, S, L) when is_integer(S), S >= 1, is_integer(L), L >= 0 -> substr1(substr2(String, S), L). @@ -163,7 +191,10 @@ substr2([_|String], S) -> substr2(String, S-1). %% tokens(String, Seperators). %% Return a list of tokens seperated by characters in Seperators. --spec tokens(string(), string()) -> [[char(),...]]. +-spec tokens(String, SeparatorList) -> Tokens when + String :: string(), + SeparatorList :: string(), + Tokens :: [Token :: nonempty_string()]. tokens(S, Seps) -> tokens1(S, Seps, []). @@ -184,11 +215,18 @@ tokens2([C|S], Seps, Toks, Cs) -> tokens2([], _Seps, Toks, Cs) -> reverse([reverse(Cs)|Toks]). --spec chars(char(), non_neg_integer()) -> string(). +-spec chars(Character, Number) -> String when + Character :: char(), + Number :: non_neg_integer(), + String :: string(). chars(C, N) -> chars(C, N, []). --spec chars(char(), non_neg_integer(), string()) -> string(). +-spec chars(Character, Number, Tail) -> String when + Character :: char(), + Number :: non_neg_integer(), + Tail :: string(), + String :: string(). chars(C, N, Tail) when N > 0 -> chars(C, N-1, [C|Tail]); @@ -199,9 +237,12 @@ chars(C, 0, Tail) when is_integer(C) -> %%% COPIES %%% --spec copies(string(), non_neg_integer()) -> string(). +-spec copies(String, Number) -> Copies when + String :: string(), + Copies :: string(), + Number :: non_neg_integer(). -copies(CharList, Num) when is_list(CharList), Num >= 0 -> +copies(CharList, Num) when is_list(CharList), is_integer(Num), Num >= 0 -> copies(CharList, Num, []). copies(_CharList, 0, R) -> @@ -211,11 +252,16 @@ copies(CharList, Num, R) -> %%% WORDS %%% --spec words(string()) -> pos_integer(). +-spec words(String) -> Count when + String :: string(), + Count :: pos_integer(). words(String) -> words(String, $\s). --spec words(string(), char()) -> pos_integer(). +-spec words(String, Character) -> Count when + String :: string(), + Character :: char(), + Count :: pos_integer(). words(String, Char) when is_integer(Char) -> w_count(strip(String, both, Char), Char, 0). @@ -226,11 +272,18 @@ w_count([_H|T], Char, Num) -> w_count(T, Char, Num). %%% SUB_WORDS %%% --spec sub_word(string(), integer()) -> string(). +-spec sub_word(String, Number) -> Word when + String :: string(), + Word :: string(), + Number :: integer(). sub_word(String, Index) -> sub_word(String, Index, $\s). --spec sub_word(string(), integer(), char()) -> string(). +-spec sub_word(String, Number, Character) -> Word when + String :: string(), + Word :: string(), + Number :: integer(), + Character :: char(). sub_word(String, Index, Char) when is_integer(Index), is_integer(Char) -> case words(String, Char) of @@ -254,14 +307,21 @@ s_word([_|T],Stop,Char,Index,Res) when Index < Stop -> strip(String) -> strip(String, both). --spec strip(string(), direction()) -> string(). +-spec strip(String, Direction) -> Stripped when + String :: string(), + Stripped :: string(), + Direction :: left | right | both. strip(String, left) -> strip_left(String, $\s); strip(String, right) -> strip_right(String, $\s); strip(String, both) -> strip_right(strip_left(String, $\s), $\s). --spec strip(string(), direction(), char()) -> string(). +-spec strip(String, Direction, Character) -> Stripped when + String :: string(), + Stripped :: string(), + Direction :: left | right | both, + Character :: char(). strip(String, right, Char) -> strip_right(String, Char); strip(String, left, Char) -> strip_left(String, Char); @@ -285,11 +345,18 @@ strip_right([], Sc) when is_integer(Sc) -> %%% LEFT %%% --spec left(string(), non_neg_integer()) -> string(). +-spec left(String, Number) -> Left when + String :: string(), + Left :: string(), + Number :: non_neg_integer(). left(String, Len) when is_integer(Len) -> left(String, Len, $\s). --spec left(string(), non_neg_integer(), char()) -> string(). +-spec left(String, Number, Character) -> Left when + String :: string(), + Left :: string(), + Number :: non_neg_integer(), + Character :: char(). left(String, Len, Char) when is_integer(Char) -> Slen = length(String), @@ -303,11 +370,18 @@ l_pad(String, Num, Char) -> String ++ chars(Char, Num). %%% RIGHT %%% --spec right(string(), non_neg_integer()) -> string(). +-spec right(String, Number) -> Right when + String :: string(), + Right :: string(), + Number :: non_neg_integer(). right(String, Len) when is_integer(Len) -> right(String, Len, $\s). --spec right(string(), non_neg_integer(), char()) -> string(). +-spec right(String, Number, Character) -> Right when + String :: string(), + Right :: string(), + Number :: non_neg_integer(), + Character :: char(). right(String, Len, Char) when is_integer(Char) -> Slen = length(String), @@ -321,11 +395,18 @@ r_pad(String, Num, Char) -> chars(Char, Num, String). %%% CENTRE %%% --spec centre(string(), non_neg_integer()) -> string(). +-spec centre(String, Number) -> Centered when + String :: string(), + Centered :: string(), + Number :: non_neg_integer(). centre(String, Len) when is_integer(Len) -> centre(String, Len, $\s). --spec centre(string(), non_neg_integer(), char()) -> string(). +-spec centre(String, Number, Character) -> Centered when + String :: string(), + Centered :: string(), + Number :: non_neg_integer(), + Character :: char(). centre(String, 0, Char) when is_list(String), is_integer(Char) -> []; % Strange cases to centre string @@ -341,11 +422,18 @@ centre(String, Len, Char) when is_integer(Char) -> %%% SUB_STRING %%% --spec sub_string(string(), pos_integer()) -> string(). +-spec sub_string(String, Start) -> SubString when + String :: string(), + SubString :: string(), + Start :: pos_integer(). sub_string(String, Start) -> substr(String, Start). --spec sub_string(string(), pos_integer(), pos_integer()) -> string(). +-spec sub_string(String, Start, Stop) -> SubString when + String :: string(), + SubString :: string(), + Start :: pos_integer(), + Stop :: pos_integer(). sub_string(String, Start, Stop) -> substr(String, Start, Stop - Start + 1). @@ -370,23 +458,34 @@ to_upper_char(C) when is_integer(C), 16#F8 =< C, C =< 16#FE -> to_upper_char(C) -> C. --spec to_lower(string()) -> string() - ; (char()) -> char(). +-spec to_lower(String) -> Result when + String :: string(), + Result :: string() + ; (Char) -> CharResult when + Char :: char(), + CharResult :: char(). to_lower(S) when is_list(S) -> [to_lower_char(C) || C <- S]; to_lower(C) when is_integer(C) -> to_lower_char(C). --spec to_upper(string()) -> string() - ; (char()) -> char(). +-spec to_upper(String) -> Result when + String :: string(), + Result :: string() + ; (Char) -> CharResult when + Char :: char(), + CharResult :: char(). to_upper(S) when is_list(S) -> [to_upper_char(C) || C <- S]; to_upper(C) when is_integer(C) -> to_upper_char(C). --spec join([string()], string()) -> string(). +-spec join(StringList, Separator) -> String when + StringList :: [string()], + Separator :: string(), + String :: string(). join([], Sep) when is_list(Sep) -> []; diff --git a/lib/stdlib/src/supervisor.erl b/lib/stdlib/src/supervisor.erl index 22269a8d1b..e60706ed05 100644 --- a/lib/stdlib/src/supervisor.erl +++ b/lib/stdlib/src/supervisor.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. 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,7 +21,7 @@ -behaviour(gen_server). %% External exports --export([start_link/2,start_link/3, +-export([start_link/2, start_link/3, start_child/2, restart_child/2, delete_child/2, terminate_child/2, which_children/1, count_children/1, @@ -33,28 +33,67 @@ -export([init/1, handle_call/3, handle_info/2, terminate/2, code_change/3]). -export([handle_cast/2]). +%%-------------------------------------------------------------------------- + +-export_type([child_spec/0, startchild_ret/0, strategy/0]). + +%%-------------------------------------------------------------------------- + +-type child() :: pid() | 'undefined'. +-type child_id() :: term(). +-type mfargs() :: {M :: module(), F :: atom(), A :: [term()] | undefined}. +-type modules() :: [module()] | 'dynamic'. +-type restart() :: 'permanent' | 'transient' | 'temporary'. +-type shutdown() :: 'brutal_kill' | timeout(). +-type worker() :: 'worker' | 'supervisor'. +-type sup_name() :: {'local', Name :: atom()} | {'global', Name :: atom()}. +-type sup_ref() :: (Name :: atom()) + | {Name :: atom(), Node :: node()} + | {'global', Name :: atom()} + | pid(). +-type child_spec() :: {Id :: child_id(), + StartFunc :: mfargs(), + Restart :: restart(), + Shutdown :: shutdown(), + Type :: worker(), + Modules :: modules()}. + +-type strategy() :: 'one_for_all' | 'one_for_one' + | 'rest_for_one' | 'simple_one_for_one'. + +%%-------------------------------------------------------------------------- + +-record(child, {% pid is undefined when child is not running + pid = undefined :: child(), + name, + mfargs :: mfargs(), + restart_type :: restart(), + shutdown :: shutdown(), + child_type :: worker(), + modules = [] :: modules()}). +-type child_rec() :: #child{}. + -define(DICT, dict). +-define(SETS, sets). +-define(SET, set). -record(state, {name, - strategy, - children = [], - dynamics = ?DICT:new(), - intensity, - period, + strategy :: strategy(), + children = [] :: [child_rec()], + dynamics :: ?DICT() | ?SET(), + intensity :: non_neg_integer(), + period :: pos_integer(), restarts = [], module, args}). - --record(child, {pid = undefined, % pid is undefined when child is not running - name, - mfa, - restart_type, - shutdown, - child_type, - modules = []}). +-type state() :: #state{}. -define(is_simple(State), State#state.strategy =:= simple_one_for_one). +%%-------------------------------------------------------------------------- + +-spec behaviour_info(atom()) -> 'undefined' | [{atom(), arity()}]. + behaviour_info(callbacks) -> [{init,1}]; behaviour_info(_Other) -> @@ -65,21 +104,54 @@ behaviour_info(_Other) -> %%% Servers/processes should/could also be built using gen_server.erl. %%% SupName = {local, atom()} | {global, atom()}. %%% --------------------------------------------------- + +-type startlink_err() :: {'already_started', pid()} | 'shutdown' | term(). +-type startlink_ret() :: {'ok', pid()} | 'ignore' | {'error', startlink_err()}. + +-spec start_link(Module, Args) -> startlink_ret() when + Module :: module(), + Args :: term(). start_link(Mod, Args) -> gen_server:start_link(supervisor, {self, Mod, Args}, []). +-spec start_link(SupName, Module, Args) -> startlink_ret() when + SupName :: sup_name(), + Module :: module(), + Args :: term(). start_link(SupName, Mod, Args) -> gen_server:start_link(SupName, supervisor, {SupName, Mod, Args}, []). %%% --------------------------------------------------- %%% Interface functions. %%% --------------------------------------------------- + +-type startchild_err() :: 'already_present' + | {'already_started', Child :: child()} | term(). +-type startchild_ret() :: {'ok', Child :: child()} + | {'ok', Child :: child(), Info :: term()} + | {'error', startchild_err()}. + +-spec start_child(SupRef, ChildSpec) -> startchild_ret() when + SupRef :: sup_ref(), + ChildSpec :: child_spec() | (List :: [term()]). start_child(Supervisor, ChildSpec) -> call(Supervisor, {start_child, ChildSpec}). +-spec restart_child(SupRef, Id) -> Result when + SupRef :: sup_ref(), + Id :: child_id(), + Result :: {'ok', Child :: child()} + | {'ok', Child :: child(), Info :: term()} + | {'error', Error}, + Error :: 'running' | 'not_found' | 'simple_one_for_one' | term(). restart_child(Supervisor, Name) -> call(Supervisor, {restart_child, Name}). +-spec delete_child(SupRef, Id) -> Result when + SupRef :: sup_ref(), + Id :: child_id(), + Result :: 'ok' | {'error', Error}, + Error :: 'running' | 'not_found' | 'simple_one_for_one'. delete_child(Supervisor, Name) -> call(Supervisor, {delete_child, Name}). @@ -89,18 +161,40 @@ delete_child(Supervisor, Name) -> %% Note that the child is *always* terminated in some %% way (maybe killed). %%----------------------------------------------------------------- + +-spec terminate_child(SupRef, Id) -> Result when + SupRef :: sup_ref(), + Id :: pid() | child_id(), + Result :: 'ok' | {'error', Error}, + Error :: 'not_found' | 'simple_one_for_one'. terminate_child(Supervisor, Name) -> call(Supervisor, {terminate_child, Name}). +-spec which_children(SupRef) -> [{Id,Child,Type,Modules}] when + SupRef :: sup_ref(), + Id :: child_id() | undefined, + Child :: child(), + Type :: worker(), + Modules :: modules(). which_children(Supervisor) -> call(Supervisor, which_children). +-spec count_children(SupRef) -> PropListOfCounts when + SupRef :: sup_ref(), + PropListOfCounts :: [Count], + Count :: {specs, ChildSpecCount :: non_neg_integer()} + | {active, ActiveProcessCount :: non_neg_integer()} + | {supervisors, ChildSupervisorCount :: non_neg_integer()} + |{workers, ChildWorkerCount :: non_neg_integer()}. count_children(Supervisor) -> call(Supervisor, count_children). call(Supervisor, Req) -> gen_server:call(Supervisor, Req, infinity). +-spec check_childspecs(ChildSpecs) -> Result when + ChildSpecs :: [child_spec()], + Result :: 'ok' | {'error', Error :: term()}. check_childspecs(ChildSpecs) when is_list(ChildSpecs) -> case check_startspec(ChildSpecs) of {ok, _} -> ok; @@ -113,6 +207,16 @@ check_childspecs(X) -> {error, {badarg, X}}. %%% Initialize the supervisor. %%% %%% --------------------------------------------------- + +-type init_sup_name() :: sup_name() | 'self'. + +-type stop_rsn() :: 'shutdown' | {'bad_return', {module(),'init', term()}} + | {'bad_start_spec', term()} | {'start_spec', term()} + | {'supervisor_data', term()}. + +-spec init({init_sup_name(), module(), [term()]}) -> + {'ok', state()} | 'ignore' | {'stop', stop_rsn()}. + init({SupName, Mod, Args}) -> process_flag(trap_exit, true), case Mod:init(Args) of @@ -130,7 +234,7 @@ init({SupName, Mod, Args}) -> Error -> {stop, {bad_return, {Mod, init, Error}}} end. - + init_children(State, StartSpec) -> SupName = State#state.name, case check_startspec(StartSpec) of @@ -158,12 +262,12 @@ init_dynamic(_State, StartSpec) -> %%----------------------------------------------------------------- %% Func: start_children/2 -%% Args: Children = [#child] in start order -%% SupName = {local, atom()} | {global, atom()} | {pid(),Mod} +%% Args: Children = [child_rec()] in start order +%% SupName = {local, atom()} | {global, atom()} | {pid(), Mod} %% Purpose: Start all children. The new list contains #child's %% with pids. %% Returns: {ok, NChildren} | {error, NChildren} -%% NChildren = [#child] in termination order (reversed +%% NChildren = [child_rec()] in termination order (reversed %% start order) %%----------------------------------------------------------------- start_children(Children, SupName) -> start_children(Children, [], SupName). @@ -182,8 +286,8 @@ start_children([], NChildren, _SupName) -> {ok, NChildren}. do_start_child(SupName, Child) -> - #child{mfa = {M, F, A}} = Child, - case catch apply(M, F, A) of + #child{mfargs = {M, F, Args}} = Child, + case catch apply(M, F, Args) of {ok, Pid} when is_pid(Pid) -> NChild = Child#child{pid = Pid}, report_progress(NChild, SupName), @@ -192,7 +296,7 @@ do_start_child(SupName, Child) -> NChild = Child#child{pid = Pid}, report_progress(NChild, SupName), {ok, Pid, Extra}; - ignore -> + ignore -> {ok, undefined}; {error, What} -> {error, What}; What -> {error, What} @@ -211,31 +315,50 @@ do_start_child_i(M, F, A) -> What -> {error, What} end. - %%% --------------------------------------------------- %%% %%% Callback functions. %%% %%% --------------------------------------------------- +-type call() :: 'which_children' | 'count_children' | {_, _}. % XXX: refine +-spec handle_call(call(), term(), state()) -> {'reply', term(), state()}. + handle_call({start_child, EArgs}, _From, State) when ?is_simple(State) -> - #child{mfa = {M, F, A}} = hd(State#state.children), + Child = hd(State#state.children), + #child{mfargs = {M, F, A}} = Child, Args = A ++ EArgs, case do_start_child_i(M, F, Args) of {ok, Pid} -> - NState = State#state{dynamics = - ?DICT:store(Pid, Args, State#state.dynamics)}, + NState = save_dynamic_child(Child#child.restart_type, Pid, Args, State), {reply, {ok, Pid}, NState}; {ok, Pid, Extra} -> - NState = State#state{dynamics = - ?DICT:store(Pid, Args, State#state.dynamics)}, + NState = save_dynamic_child(Child#child.restart_type, Pid, Args, State), {reply, {ok, Pid, Extra}, NState}; What -> {reply, What, State} end; -%%% The requests terminate_child, delete_child and restart_child are -%%% invalid for simple_one_for_one supervisors. +%% terminate_child for simple_one_for_one can only be done with pid +handle_call({terminate_child, Name}, _From, State) when not is_pid(Name), + ?is_simple(State) -> + {reply, {error, simple_one_for_one}, State}; + +handle_call({terminate_child, Name}, _From, State) -> + case get_child(Name, State, ?is_simple(State)) of + {value, Child} -> + case do_terminate(Child, State#state.name) of + #child{restart_type=RT} when RT=:=temporary; ?is_simple(State) -> + {reply, ok, state_del_child(Child, State)}; + NChild -> + {reply, ok, replace_child(NChild, State)} + end; + false -> + {reply, {error, not_found}, State} + end; + +%%% The requests delete_child and restart_child are invalid for +%%% simple_one_for_one supervisors. handle_call({_Req, _Data}, _From, State) when ?is_simple(State) -> {reply, {error, simple_one_for_one}, State}; @@ -278,37 +401,56 @@ handle_call({delete_child, Name}, _From, State) -> {reply, {error, not_found}, State} end; -handle_call({terminate_child, Name}, _From, State) -> - case get_child(Name, State) of - {value, Child} -> - NChild = do_terminate(Child, State#state.name), - {reply, ok, replace_child(NChild, State)}; - _ -> - {reply, {error, not_found}, State} - end; +handle_call(which_children, _From, #state{children = [#child{restart_type = temporary, + child_type = CT, + modules = Mods}]} = + State) when ?is_simple(State) -> + Reply = lists:map(fun(Pid) -> {undefined, Pid, CT, Mods} end, + ?SETS:to_list(dynamics_db(temporary, State#state.dynamics))), + {reply, Reply, State}; -handle_call(which_children, _From, State) when ?is_simple(State) -> - [#child{child_type = CT, modules = Mods}] = State#state.children, +handle_call(which_children, _From, #state{children = [#child{restart_type = RType, + child_type = CT, + modules = Mods}]} = + State) when ?is_simple(State) -> Reply = lists:map(fun({Pid, _}) -> {undefined, Pid, CT, Mods} end, - ?DICT:to_list(State#state.dynamics)), + ?DICT:to_list(dynamics_db(RType, State#state.dynamics))), {reply, Reply, State}; handle_call(which_children, _From, State) -> Resp = lists:map(fun(#child{pid = Pid, name = Name, child_type = ChildType, modules = Mods}) -> - {Name, Pid, ChildType, Mods} + {Name, Pid, ChildType, Mods} end, State#state.children), {reply, Resp, State}; -handle_call(count_children, _From, State) when ?is_simple(State) -> - [#child{child_type = CT}] = State#state.children, + +handle_call(count_children, _From, #state{children = [#child{restart_type = temporary, + child_type = CT}]} = State) + when ?is_simple(State) -> + {Active, Count} = + ?SETS:fold(fun(Pid, {Alive, Tot}) -> + if is_pid(Pid) -> {Alive+1, Tot +1}; + true -> {Alive, Tot + 1} end + end, {0, 0}, dynamics_db(temporary, State#state.dynamics)), + Reply = case CT of + supervisor -> [{specs, 1}, {active, Active}, + {supervisors, Count}, {workers, 0}]; + worker -> [{specs, 1}, {active, Active}, + {supervisors, 0}, {workers, Count}] + end, + {reply, Reply, State}; + +handle_call(count_children, _From, #state{children = [#child{restart_type = RType, + child_type = CT}]} = State) + when ?is_simple(State) -> {Active, Count} = ?DICT:fold(fun(Pid, _Val, {Alive, Tot}) -> if is_pid(Pid) -> {Alive+1, Tot +1}; true -> {Alive, Tot + 1} end - end, {0, 0}, State#state.dynamics), + end, {0, 0}, dynamics_db(RType, State#state.dynamics)), Reply = case CT of supervisor -> [{specs, 1}, {active, Active}, {supervisors, Count}, {workers, 0}]; @@ -318,7 +460,6 @@ handle_call(count_children, _From, State) when ?is_simple(State) -> {reply, Reply, State}; handle_call(count_children, _From, State) -> - %% Specs and children are together on the children list... {Specs, Active, Supers, Workers} = lists:foldl(fun(Child, Counts) -> @@ -347,15 +488,19 @@ count_child(#child{pid = Pid, child_type = supervisor}, %%% Hopefully cause a function-clause as there is no API function %%% that utilizes cast. +-spec handle_cast('null', state()) -> {'noreply', state()}. + handle_cast(null, State) -> error_logger:error_msg("ERROR: Supervisor received cast-message 'null'~n", []), - {noreply, State}. %% %% Take care of terminated children. %% +-spec handle_info(term(), state()) -> + {'noreply', state()} | {'stop', 'shutdown', state()}. + handle_info({'EXIT', Pid, Reason}, State) -> case restart_child(Pid, Reason, State) of {ok, State1} -> @@ -368,9 +513,12 @@ handle_info(Msg, State) -> error_logger:error_msg("Supervisor received unexpected message: ~p~n", [Msg]), {noreply, State}. + %% %% Terminate this server. %% +-spec terminate(term(), state()) -> 'ok'. + terminate(_Reason, State) -> terminate_children(State#state.children, State#state.name), ok. @@ -384,6 +532,9 @@ terminate(_Reason, State) -> %% NOTE: This requires that the init function of the call-back module %% does not have any side effects. %% +-spec code_change(term(), state(), term()) -> + {'ok', state()} | {'error', term()}. + code_change(_, State, _) -> case (State#state.module):init(State#state.args) of {ok, {SupFlags, StartSpec}} -> @@ -411,7 +562,7 @@ check_flags({Strategy, MaxIntensity, Period}) -> check_flags(What) -> {bad_flags, What}. -update_childspec(State, StartSpec) when ?is_simple(State) -> +update_childspec(State, StartSpec) when ?is_simple(State) -> case check_startspec(StartSpec) of {ok, [Child]} -> {ok, State#state{children = [Child]}}; @@ -437,7 +588,7 @@ update_childspec1([Child|OldC], Children, KeepOld) -> update_childspec1(OldC, Children, [Child|KeepOld]) end; update_childspec1([], Children, KeepOld) -> - % Return them in (keeped) reverse start order. + %% Return them in (kept) reverse start order. lists:reverse(Children ++ KeepOld). update_chsp(OldCh, Children) -> @@ -462,15 +613,11 @@ handle_start_child(Child, State) -> false -> case do_start_child(State#state.name, Child) of {ok, Pid} -> - Children = State#state.children, {{ok, Pid}, - State#state{children = - [Child#child{pid = Pid}|Children]}}; + save_child(Child#child{pid = Pid}, State)}; {ok, Pid, Extra} -> - Children = State#state.children, {{ok, Pid, Extra}, - State#state{children = - [Child#child{pid = Pid}|Children]}}; + save_child(Child#child{pid = Pid}, State)}; {error, What} -> {{error, {What, Child}}, State} end; @@ -482,27 +629,26 @@ handle_start_child(Child, State) -> %%% --------------------------------------------------- %%% Restart. A process has terminated. -%%% Returns: {ok, #state} | {shutdown, #state} +%%% Returns: {ok, state()} | {shutdown, state()} %%% --------------------------------------------------- -restart_child(Pid, Reason, State) when ?is_simple(State) -> - case ?DICT:find(Pid, State#state.dynamics) of +restart_child(Pid, Reason, #state{children = [Child]} = State) when ?is_simple(State) -> + RestartType = Child#child.restart_type, + case dynamic_child_args(Pid, dynamics_db(RestartType, State#state.dynamics)) of {ok, Args} -> - [Child] = State#state.children, - RestartType = Child#child.restart_type, - {M, F, _} = Child#child.mfa, - NChild = Child#child{pid = Pid, mfa = {M, F, Args}}, + {M, F, _} = Child#child.mfargs, + NChild = Child#child{pid = Pid, mfargs = {M, F, Args}}, do_restart(RestartType, Reason, NChild, State); error -> - {ok, State} + {ok, State} end; + restart_child(Pid, Reason, State) -> Children = State#state.children, - case lists:keysearch(Pid, #child.pid, Children) of - {value, Child} -> - RestartType = Child#child.restart_type, + case lists:keyfind(Pid, #child.pid, Children) of + #child{restart_type = RestartType} = Child -> do_restart(RestartType, Reason, Child, State); - _ -> + false -> {ok, State} end. @@ -534,8 +680,9 @@ restart(Child, State) -> end. restart(simple_one_for_one, Child, State) -> - #child{mfa = {M, F, A}} = Child, - Dynamics = ?DICT:erase(Child#child.pid, State#state.dynamics), + #child{mfargs = {M, F, A}} = Child, + Dynamics = ?DICT:erase(Child#child.pid, dynamics_db(Child#child.restart_type, + State#state.dynamics)), case do_start_child_i(M, F, A) of {ok, Pid} -> NState = State#state{dynamics = ?DICT:store(Pid, A, Dynamics)}, @@ -580,9 +727,9 @@ restart(one_for_all, Child, State) -> %%----------------------------------------------------------------- %% Func: terminate_children/2 -%% Args: Children = [#child] in termination order +%% Args: Children = [child_rec()] in termination order %% SupName = {local, atom()} | {global, atom()} | {pid(),Mod} -%% Returns: NChildren = [#child] in +%% Returns: NChildren = [child_rec()] in %% startup order (reversed termination order) %%----------------------------------------------------------------- terminate_children(Children, SupName) -> @@ -595,14 +742,15 @@ terminate_children([], _SupName, Res) -> Res. do_terminate(Child, SupName) when Child#child.pid =/= undefined -> - case shutdown(Child#child.pid, - Child#child.shutdown) of - ok -> - Child#child{pid = undefined}; - {error, OtherReason} -> - report_error(shutdown_error, OtherReason, Child, SupName), - Child#child{pid = undefined} - end; + case shutdown(Child#child.pid, Child#child.shutdown) of + ok -> + ok; + {error, normal} when Child#child.restart_type =/= permanent -> + ok; + {error, OtherReason} -> + report_error(shutdown_error, OtherReason, Child, SupName) + end, + Child#child{pid = undefined}; do_terminate(Child, _SupName) -> Child. @@ -617,7 +765,6 @@ do_terminate(Child, _SupName) -> %% Returns: ok | {error, OtherReason} (this should be reported) %%----------------------------------------------------------------- shutdown(Pid, brutal_kill) -> - case monitor_child(Pid) of ok -> exit(Pid, kill), @@ -630,9 +777,7 @@ shutdown(Pid, brutal_kill) -> {error, Reason} -> {error, Reason} end; - shutdown(Pid, Time) -> - case monitor_child(Pid) of ok -> exit(Pid, shutdown), %% Try to shutdown gracefully @@ -684,15 +829,54 @@ monitor_child(Pid) -> %%----------------------------------------------------------------- %% Child/State manipulating functions. %%----------------------------------------------------------------- -state_del_child(#child{pid = Pid}, State) when ?is_simple(State) -> - NDynamics = ?DICT:erase(Pid, State#state.dynamics), + +%% Note we do not want to save the parameter list for temporary processes as +%% they will not be restarted, and hence we do not need this information. +%% Especially for dynamic children to simple_one_for_one supervisors +%% it could become very costly as it is not uncommon to spawn +%% very many such processes. +save_child(#child{restart_type = temporary, + mfargs = {M, F, _}} = Child, #state{children = Children} = State) -> + State#state{children = [Child#child{mfargs = {M, F, undefined}} |Children]}; +save_child(Child, #state{children = Children} = State) -> + State#state{children = [Child |Children]}. + +save_dynamic_child(temporary, Pid, _, #state{dynamics = Dynamics} = State) -> + State#state{dynamics = ?SETS:add_element(Pid, dynamics_db(temporary, Dynamics))}; +save_dynamic_child(RestartType, Pid, Args, #state{dynamics = Dynamics} = State) -> + State#state{dynamics = ?DICT:store(Pid, Args, dynamics_db(RestartType, Dynamics))}. + +dynamics_db(temporary, undefined) -> + ?SETS:new(); +dynamics_db(_, undefined) -> + ?DICT:new(); +dynamics_db(_,Dynamics) -> + Dynamics. + +dynamic_child_args(Pid, Dynamics) -> + case ?SETS:is_set(Dynamics) of + true -> + {ok, undefined}; + false -> + ?DICT:find(Pid, Dynamics) + end. + +state_del_child(#child{pid = Pid, restart_type = temporary}, State) when ?is_simple(State) -> + NDynamics = ?SETS:del_element(Pid, dynamics_db(temporary, State#state.dynamics)), + State#state{dynamics = NDynamics}; +state_del_child(#child{pid = Pid, restart_type = RType}, State) when ?is_simple(State) -> + NDynamics = ?DICT:erase(Pid, dynamics_db(RType, State#state.dynamics)), State#state{dynamics = NDynamics}; state_del_child(Child, State) -> NChildren = del_child(Child#child.name, State#state.children), State#state{children = NChildren}. +del_child(Name, [Ch|Chs]) when Ch#child.name =:= Name, Ch#child.restart_type =:= temporary -> + Chs; del_child(Name, [Ch|Chs]) when Ch#child.name =:= Name -> [Ch#child{pid = undefined} | Chs]; +del_child(Pid, [Ch|Chs]) when Ch#child.pid =:= Pid, Ch#child.restart_type =:= temporary -> + Chs; del_child(Pid, [Ch|Chs]) when Ch#child.pid =:= Pid -> [Ch#child{pid = undefined} | Chs]; del_child(Name, [Ch|Chs]) -> @@ -715,7 +899,31 @@ split_child(_, [], After) -> {lists:reverse(After), []}. get_child(Name, State) -> + get_child(Name, State, false). +get_child(Pid, State, AllowPid) when AllowPid, is_pid(Pid) -> + get_dynamic_child(Pid, State); +get_child(Name, State, _) -> lists:keysearch(Name, #child.name, State#state.children). + +get_dynamic_child(Pid, #state{children=[Child], dynamics=Dynamics}) -> + case is_dynamic_pid(Pid, dynamics_db(Child#child.restart_type, Dynamics)) of + true -> + {value, Child#child{pid=Pid}}; + false -> + case erlang:is_process_alive(Pid) of + true -> false; + false -> {value, Child} + end + end. + +is_dynamic_pid(Pid, Dynamics) -> + case ?SETS:is_set(Dynamics) of + true -> + ?SETS:is_element(Pid, Dynamics); + false -> + ?DICT:is_key(Pid, Dynamics) + end. + replace_child(Child, State) -> Chs = do_replace_child(Child, State#state.children), State#state{children = Chs}. @@ -738,9 +946,9 @@ remove_child(Child, State) -> %% MaxIntensity = integer() %% Period = integer() %% Mod :== atom() -%% Arsg :== term() +%% Args :== term() %% Purpose: Check that Type is of correct type (!) -%% Returns: {ok, #state} | Error +%% Returns: {ok, state()} | Error %%----------------------------------------------------------------- init_state(SupName, Type, Mod, Args) -> case catch init_state1(SupName, Type, Mod, Args) of @@ -755,11 +963,11 @@ init_state1(SupName, {Strategy, MaxIntensity, Period}, Mod, Args) -> validIntensity(MaxIntensity), validPeriod(Period), {ok, #state{name = supname(SupName,Mod), - strategy = Strategy, - intensity = MaxIntensity, - period = Period, - module = Mod, - args = Args}}; + strategy = Strategy, + intensity = MaxIntensity, + period = Period, + module = Mod, + args = Args}}; init_state1(_SupName, Type, _, _) -> {invalid_type, Type}. @@ -771,26 +979,26 @@ validStrategy(What) -> throw({invalid_strategy, What}). validIntensity(Max) when is_integer(Max), Max >= 0 -> true; -validIntensity(What) -> throw({invalid_intensity, What}). +validIntensity(What) -> throw({invalid_intensity, What}). validPeriod(Period) when is_integer(Period), Period > 0 -> true; validPeriod(What) -> throw({invalid_period, What}). -supname(self,Mod) -> {self(),Mod}; -supname(N,_) -> N. +supname(self, Mod) -> {self(), Mod}; +supname(N, _) -> N. %%% ------------------------------------------------------ %%% Check that the children start specification is valid. %%% Shall be a six (6) tuple %%% {Name, Func, RestartType, Shutdown, ChildType, Modules} %%% where Name is an atom -%%% Func is {Mod, Fun, Args} == {atom, atom, list} +%%% Func is {Mod, Fun, Args} == {atom(), atom(), list()} %%% RestartType is permanent | temporary | transient %%% Shutdown = integer() | infinity | brutal_kill %%% ChildType = supervisor | worker %%% Modules = [atom()] | dynamic -%%% Returns: {ok, [#child]} | Error +%%% Returns: {ok, [child_rec()]} | Error %%% ------------------------------------------------------ check_startspec(Children) -> check_startspec(Children, []). @@ -818,14 +1026,14 @@ check_childspec(Name, Func, RestartType, Shutdown, ChildType, Mods) -> validChildType(ChildType), validShutdown(Shutdown, ChildType), validMods(Mods), - {ok, #child{name = Name, mfa = Func, restart_type = RestartType, + {ok, #child{name = Name, mfargs = Func, restart_type = RestartType, shutdown = Shutdown, child_type = ChildType, modules = Mods}}. validChildType(supervisor) -> true; validChildType(worker) -> true; validChildType(What) -> throw({invalid_child_type, What}). -validName(_Name) -> true. +validName(_Name) -> true. validFunc({M, F, A}) when is_atom(M), is_atom(F), @@ -923,7 +1131,7 @@ report_error(Error, Reason, Child, SupName) -> extract_child(Child) -> [{pid, Child#child.pid}, {name, Child#child.name}, - {mfa, Child#child.mfa}, + {mfargs, Child#child.mfargs}, {restart_type, Child#child.restart_type}, {shutdown, Child#child.shutdown}, {child_type, Child#child.child_type}]. diff --git a/lib/stdlib/src/supervisor_bridge.erl b/lib/stdlib/src/supervisor_bridge.erl index 3d2bd2c9a5..555cb5a66f 100644 --- a/lib/stdlib/src/supervisor_bridge.erl +++ b/lib/stdlib/src/supervisor_bridge.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -49,9 +49,25 @@ behaviour_info(_Other) -> %%%----------------------------------------------------------------- -record(state, {mod, pid, child_state, name}). +-spec start_link(Module, Args) -> Result when + Module :: module(), + Args :: term(), + Result :: {ok, Pid} | ignore | {error, Error}, + Error :: {already_started, Pid} | term(), + Pid :: pid(). + start_link(Mod, StartArgs) -> gen_server:start_link(supervisor_bridge, [Mod, StartArgs, self], []). +-spec start_link(SupBridgeName, Module, Args) -> Result when + SupBridgeName :: {local, Name} | {global, Name}, + Name :: atom(), + Module :: module(), + Args :: term(), + Result :: {ok, Pid} | ignore | {error, Error}, + Error :: {already_started, Pid} | term(), + Pid :: pid(). + start_link(Name, Mod, StartArgs) -> gen_server:start_link(Name, supervisor_bridge, [Mod, StartArgs, Name], []). diff --git a/lib/stdlib/src/sys.erl b/lib/stdlib/src/sys.erl index 12209c16d7..8ab72c9b50 100644 --- a/lib/stdlib/src/sys.erl +++ b/lib/stdlib/src/sys.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. 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,22 +33,74 @@ %%----------------------------------------------------------------- -type name() :: pid() | atom() | {'global', atom()}. --type system_event() :: {'in', _Msg} | {'in', _Msg, _From} | {'out', _Msg, _To}. +-type system_event() :: {'in', Msg :: _} + | {'in', Msg :: _, From :: _} + | {'out', Msg :: _, To :: _} + | term(). +-opaque dbg_opt() :: list(). +-type dbg_fun() :: fun((FuncState :: _, + Event :: system_event(), + ProcState :: _) -> 'done' | (NewFuncState :: _)). %%----------------------------------------------------------------- %% System messages %%----------------------------------------------------------------- +-spec suspend(Name) -> Void when + Name :: name(), + Void :: term(). suspend(Name) -> send_system_msg(Name, suspend). +-spec suspend(Name, Timeout) -> Void when + Name :: name(), + Timeout :: timeout(), + Void :: term(). suspend(Name, Timeout) -> send_system_msg(Name, suspend, Timeout). +-spec resume(Name) -> Void when + Name :: name(), + Void :: term(). resume(Name) -> send_system_msg(Name, resume). +-spec resume(Name, Timeout) -> Void when + Name :: name(), + Timeout :: timeout(), + Void :: term(). resume(Name, Timeout) -> send_system_msg(Name, resume, Timeout). +-spec get_status(Name) -> Status when + Name :: name(), + Status :: {status, Pid :: pid(), {module, Module :: module()}, [SItem]}, + SItem :: (PDict :: [{Key :: term(), Value :: term()}]) + | (SysState :: 'running' | 'suspended') + | (Parent :: pid()) + | (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(), + Status :: {status, Pid :: pid(), {module, Module :: module()}, [SItem]}, + SItem :: (PDict :: [{Key :: term(), Value :: term()}]) + | (SysState :: 'running' | 'suspended') + | (Parent :: pid()) + | (Dbg :: dbg_opt()) + | (Misc :: term()). get_status(Name, Timeout) -> send_system_msg(Name, get_status, Timeout). +-spec change_code(Name, Module, OldVsn, Extra) -> 'ok' | {error, Reason} when + Name :: name(), + Module :: module(), + OldVsn :: 'undefined' | term(), + Extra :: term(), + 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(), + Module :: module(), + OldVsn :: 'undefined' | term(), + Extra :: term(), + Timeout :: timeout(), + Reason :: term(). change_code(Name, Mod, Vsn, Extra, Timeout) -> send_system_msg(Name, {change_code, Mod, Vsn, Extra}, Timeout). @@ -56,52 +108,116 @@ change_code(Name, Mod, Vsn, Extra, Timeout) -> %% Debug commands %%----------------------------------------------------------------- --type log_flag() :: 'true' | {'true',pos_integer()} | 'false' | 'get' | 'print'. - --spec log(name(), log_flag()) -> 'ok' | {'ok', [system_event()]}. +-spec log(Name, Flag) -> 'ok' | {'ok', [system_event()]} when + Name :: name(), + Flag :: 'true' | + {'true', N :: pos_integer()} + | 'false' | 'get' | 'print'. log(Name, Flag) -> send_system_msg(Name, {debug, {log, Flag}}). --spec log(name(), log_flag(), timeout()) -> 'ok' | {'ok', [system_event()]}. +-spec log(Name, Flag, Timeout) -> 'ok' | {'ok', [system_event()]} when + Name :: name(), + Flag :: 'true' | + {'true', N :: pos_integer()} + | 'false' | 'get' | 'print', + Timeout :: timeout(). log(Name, Flag, Timeout) -> send_system_msg(Name, {debug, {log, Flag}}, Timeout). --spec trace(name(), boolean()) -> 'ok'. +-spec trace(Name, Flag) -> 'ok' when + Name :: name(), + Flag :: boolean(). trace(Name, Flag) -> send_system_msg(Name, {debug, {trace, Flag}}). --spec trace(name(), boolean(), timeout()) -> 'ok'. +-spec trace(Name, Flag, Timeout) -> 'ok' when + Name :: name(), + Flag :: boolean(), + Timeout :: timeout(). trace(Name, Flag, Timeout) -> send_system_msg(Name, {debug, {trace, Flag}}, Timeout). --type l2f_fname() :: string() | 'false'. - --spec log_to_file(name(), l2f_fname()) -> 'ok' | {'error','open_file'}. +-spec log_to_file(Name, Flag) -> 'ok' | {'error','open_file'} when + Name :: name(), + Flag :: (FileName :: string()) | 'false'. log_to_file(Name, FileName) -> send_system_msg(Name, {debug, {log_to_file, FileName}}). --spec log_to_file(name(), l2f_fname(), timeout()) -> 'ok' | {'error','open_file'}. +-spec log_to_file(Name, Flag, Timeout) -> 'ok' | {'error','open_file'} when + Name :: name(), + Flag :: (FileName :: string()) | 'false', + Timeout :: timeout(). log_to_file(Name, FileName, Timeout) -> send_system_msg(Name, {debug, {log_to_file, FileName}}, Timeout). +-spec statistics(Name, Flag) -> 'ok' | {'ok', Statistics} when + Name :: name(), + Flag :: 'true' | 'false' | 'get', + Statistics :: [StatisticsTuple], + StatisticsTuple :: {'start_time', DateTime1} + | {'current_time', DateTime2} + | {'reductions', non_neg_integer()} + | {'messages_in', non_neg_integer()} + | {'messages_out', non_neg_integer()}, + DateTime1 :: file:date_time(), + DateTime2 :: file:date_time(). statistics(Name, Flag) -> send_system_msg(Name, {debug, {statistics, Flag}}). + +-spec statistics(Name, Flag, Timeout) -> 'ok' | {'ok', Statistics} when + Name :: name(), + Flag :: 'true' | 'false' | 'get', + Statistics :: [StatisticsTuple], + StatisticsTuple :: {'start_time', DateTime1} + | {'current_time', DateTime2} + | {'reductions', non_neg_integer()} + | {'messages_in', non_neg_integer()} + | {'messages_out', non_neg_integer()}, + DateTime1 :: file:date_time(), + DateTime2 :: file:date_time(), + Timeout :: timeout(). statistics(Name, Flag, Timeout) -> send_system_msg(Name, {debug, {statistics, Flag}}, Timeout). --spec no_debug(name()) -> 'ok'. +-spec no_debug(Name) -> 'ok' when + Name :: name(). no_debug(Name) -> send_system_msg(Name, {debug, no_debug}). --spec no_debug(name(), timeout()) -> 'ok'. +-spec no_debug(Name, Timeout) -> 'ok' when + Name :: name(), + Timeout :: timeout(). no_debug(Name, Timeout) -> send_system_msg(Name, {debug, no_debug}, Timeout). +-spec install(Name, FuncSpec) -> Void when + Name :: name(), + FuncSpec :: {Func, FuncState}, + Func :: dbg_fun(), + FuncState :: term(), + Void :: term(). install(Name, {Func, FuncState}) -> send_system_msg(Name, {debug, {install, {Func, FuncState}}}). +-spec install(Name, FuncSpec, Timeout) -> Void when + Name :: name(), + FuncSpec :: {Func, FuncState}, + Func :: dbg_fun(), + FuncState :: term(), + Timeout :: timeout(), + Void :: term(). install(Name, {Func, FuncState}, Timeout) -> send_system_msg(Name, {debug, {install, {Func, FuncState}}}, Timeout). +-spec remove(Name, Func) -> Void when + Name :: name(), + Func :: dbg_fun(), + Void :: term(). remove(Name, Func) -> send_system_msg(Name, {debug, {remove, Func}}). +-spec remove(Name, Func, Timeout) -> Void when + Name :: name(), + Func :: dbg_fun(), + Timeout :: timeout(), + Void :: term(). remove(Name, Func, Timeout) -> send_system_msg(Name, {debug, {remove, Func}}, Timeout). @@ -150,6 +266,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 + Msg :: term(), + From :: {pid(), Tag :: _}, + Parent :: pid(), + Module :: module(), + Debug :: [dbg_opt()], + Misc :: term(), + Void :: term(). handle_system_msg(Msg, From, Parent, Module, Debug, Misc) -> handle_system_msg(running, Msg, From, Parent, Module, Debug, Misc, false). @@ -176,6 +300,11 @@ handle_system_msg(SysState, Msg, From, Parent, Mod, Debug, Misc, Hib) -> %% Func is a formatting function, called as Func(Device, Event). %% Returns: [debug_opts()] %%----------------------------------------------------------------- +-spec handle_debug(Debug, FormFunc, Extra, Event) -> [dbg_opt()] when + Debug :: [dbg_opt()], + FormFunc :: dbg_fun(), + Extra :: term(), + Event :: system_event(). handle_debug([{trace, true} | T], FormFunc, State, Event) -> print_event({Event, State, FormFunc}), [{trace, true} | handle_debug(T, FormFunc, State, Event)]; @@ -341,24 +470,36 @@ trim(N, LogData) -> %% Debug structure manipulating functions %%----------------------------------------------------------------- install_debug(Item, Data, Debug) -> - case get_debug(Item, Debug, undefined) of + case get_debug2(Item, Debug, undefined) of undefined -> [{Item, Data} | Debug]; _ -> Debug end. remove_debug(Item, Debug) -> lists:keydelete(Item, 1, Debug). + +-spec get_debug(Item, Debug, Default) -> term() when + Item :: 'log' | 'statistics', + Debug :: [dbg_opt()], + Default :: term(). get_debug(Item, Debug, Default) -> + get_debug2(Item, Debug, Default). + +%% Workaround: accepts more Item types than get_debug/3. +get_debug2(Item, Debug, Default) -> case lists:keysearch(Item, 1, Debug) of {value, {Item, Data}} -> Data; _ -> Default end. +-spec print_log(Debug) -> Void when + Debug :: [dbg_opt()], + Void :: term(). print_log(Debug) -> {_N, Logs} = get_debug(log, Debug, {0, []}), lists:foreach(fun print_event/1, lists:reverse(Logs)). close_log_file(Debug) -> - case get_debug(log_to_file, Debug, []) of + case get_debug2(log_to_file, Debug, []) of [] -> Debug; Fd -> @@ -375,6 +516,15 @@ close_log_file(Debug) -> %% system messages. %% Returns: [debug_opts()] %%----------------------------------------------------------------- + +-spec debug_options(Options) -> [dbg_opt()] when + Options :: [Opt], + Opt :: 'trace' | 'log' | 'statistics' | {'log_to_file', FileName} + | {'install', FuncSpec}, + FileName :: file:name(), + FuncSpec :: {Func, FuncState}, + Func :: dbg_fun(), + FuncState :: term(). debug_options(Options) -> debug_options(Options, []). debug_options([trace | T], Debug) -> diff --git a/lib/stdlib/src/timer.erl b/lib/stdlib/src/timer.erl index 36fdb48c75..89fae05e4f 100644 --- a/lib/stdlib/src/timer.erl +++ b/lib/stdlib/src/timer.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1996-2011. 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(timer). @@ -22,7 +22,7 @@ send_after/3, send_after/2, exit_after/3, exit_after/2, kill_after/2, kill_after/1, apply_interval/4, send_interval/3, send_interval/2, - cancel/1, sleep/1, tc/3, now_diff/2, + cancel/1, sleep/1, tc/1, tc/2, tc/3, now_diff/2, seconds/1, minutes/1, hours/1, hms/3]). -export([start_link/0, start/0, @@ -33,6 +33,9 @@ %% internal exports for test purposes only -export([get_status/0]). +%% types which can be used by other modules +-export_type([tref/0]). + %% Max -define(MAX_TIMEOUT, 16#0800000). -define(TIMER_TAB, timer_tab). @@ -41,94 +44,191 @@ %% %% Time is in milliseconds. %% --opaque tref() :: any(). +-opaque tref() :: {integer(), reference()}. -type time() :: non_neg_integer(). --type timestamp() :: {non_neg_integer(), non_neg_integer(), non_neg_integer()}. %% %% Interface functions %% --spec apply_after(time(), atom(), atom(), [_]) -> {'ok', tref()} | {'error', _}. +-spec apply_after(Time, Module, Function, Arguments) -> + {'ok', TRef} | {'error', Reason} when + Time :: time(), + Module :: module(), + Function :: atom(), + Arguments :: [term()], + TRef :: tref(), + Reason :: term(). + apply_after(Time, M, F, A) -> req(apply_after, {Time, {M, F, A}}). --spec send_after(time(), pid() | atom(), term()) -> {'ok', tref()} | {'error', _}. +-spec send_after(Time, Pid, Message) -> {'ok', TRef} | {'error', Reason} when + Time :: time(), + Pid :: pid() | (RegName :: atom()), + Message :: term(), + TRef :: tref(), + Reason :: term(). send_after(Time, Pid, Message) -> req(apply_after, {Time, {?MODULE, send, [Pid, Message]}}). --spec send_after(time(), _) -> {'ok', tref()} | {'error', _}. +-spec send_after(Time, Message) -> {'ok', TRef} | {'error', Reason} when + Time :: time(), + Message :: term(), + TRef :: tref(), + Reason :: term(). send_after(Time, Message) -> send_after(Time, self(), Message). --spec exit_after(time(), pid() | atom(), _) -> {'ok', tref()} | {'error', _}. +-spec exit_after(Time, Pid, Reason1) -> {'ok', TRef} | {'error', Reason2} when + Time :: time(), + Pid :: pid() | (RegName :: atom()), + TRef :: tref(), + Reason1 :: term(), + Reason2 :: term(). exit_after(Time, Pid, Reason) -> req(apply_after, {Time, {erlang, exit, [Pid, Reason]}}). --spec exit_after(time(), term()) -> {'ok', tref()} | {'error', _}. +-spec exit_after(Time, Reason1) -> {'ok', TRef} | {'error', Reason2} when + Time :: time(), + TRef :: tref(), + Reason1 :: term(), + Reason2 :: term(). exit_after(Time, Reason) -> exit_after(Time, self(), Reason). --spec kill_after(time(), pid() | atom()) -> {'ok', tref()} | {'error', _}. +-spec kill_after(Time, Pid) -> {'ok', TRef} | {'error', Reason2} when + Time :: time(), + Pid :: pid() | (RegName :: atom()), + TRef :: tref(), + Reason2 :: term(). kill_after(Time, Pid) -> exit_after(Time, Pid, kill). --spec kill_after(time()) -> {'ok', tref()} | {'error', _}. +-spec kill_after(Time) -> {'ok', TRef} | {'error', Reason2} when + Time :: time(), + TRef :: tref(), + Reason2 :: term(). kill_after(Time) -> exit_after(Time, self(), kill). --spec apply_interval(time(), atom(), atom(), [_]) -> {'ok', tref()} | {'error', _}. +-spec apply_interval(Time, Module, Function, Arguments) -> + {'ok', TRef} | {'error', Reason} when + Time :: time(), + Module :: module(), + Function :: atom(), + Arguments :: [term()], + TRef :: tref(), + Reason :: term(). apply_interval(Time, M, F, A) -> req(apply_interval, {Time, self(), {M, F, A}}). --spec send_interval(time(), pid() | atom(), term()) -> {'ok', tref()} | {'error', _}. +-spec send_interval(Time, Pid, Message) -> + {'ok', TRef} | {'error', Reason} when + Time :: time(), + Pid :: pid() | (RegName :: atom()), + Message :: term(), + TRef :: tref(), + Reason :: term(). send_interval(Time, Pid, Message) -> req(apply_interval, {Time, Pid, {?MODULE, send, [Pid, Message]}}). --spec send_interval(time(), term()) -> {'ok', tref()} | {'error', _}. +-spec send_interval(Time, Message) -> {'ok', TRef} | {'error', Reason} when + Time :: time(), + Message :: term(), + TRef :: tref(), + Reason :: term(). send_interval(Time, Message) -> send_interval(Time, self(), Message). --spec cancel(tref()) -> {'ok', 'cancel'} | {'error', _}. +-spec cancel(TRef) -> {'ok', 'cancel'} | {'error', Reason} when + TRef :: tref(), + Reason :: term(). cancel(BRef) -> req(cancel, BRef). --spec sleep(timeout()) -> 'ok'. +-spec sleep(Time) -> 'ok' when + Time :: timeout(). sleep(T) -> receive after T -> ok end. %% +%% Measure the execution time (in microseconds) for Fun(). +%% +-spec tc(Fun) -> {Time, Value} when + Fun :: function(), + Time :: integer(), + Value :: term(). +tc(F) -> + Before = os:timestamp(), + Val = F(), + After = os:timestamp(), + {now_diff(After, Before), Val}. + +%% +%% Measure the execution time (in microseconds) for Fun(Args). +%% +-spec tc(Fun, Arguments) -> {Time, Value} when + Fun :: function(), + Arguments :: [term()], + Time :: integer(), + Value :: term(). +tc(F, A) -> + Before = os:timestamp(), + Val = apply(F, A), + After = os:timestamp(), + {now_diff(After, Before), Val}. + +%% %% Measure the execution time (in microseconds) for an MFA. %% --spec tc(atom(), atom(), [_]) -> {time(), term()}. +-spec tc(Module, Function, Arguments) -> {Time, Value} when + Module :: module(), + Function :: atom(), + Arguments :: [term()], + Time :: integer(), + Value :: term(). tc(M, F, A) -> - Before = erlang:now(), - Val = (catch apply(M, F, A)), - After = erlang:now(), + Before = os:timestamp(), + Val = apply(M, F, A), + After = os:timestamp(), {now_diff(After, Before), Val}. %% %% Calculate the time difference (in microseconds) of two %% erlang:now() timestamps, T2-T1. %% --spec now_diff(timestamp(), timestamp()) -> integer(). +-spec now_diff(T1, T2) -> Tdiff when + T1 :: calendar:t_now(), + T2 :: calendar:t_now(), + Tdiff :: integer(). now_diff({A2, B2, C2}, {A1, B1, C1}) -> ((A2-A1)*1000000 + B2-B1)*1000000 + C2-C1. %% %% Convert seconds, minutes etc. to milliseconds. %% --spec seconds(non_neg_integer()) -> non_neg_integer(). +-spec seconds(Seconds) -> MilliSeconds when + Seconds :: non_neg_integer(), + MilliSeconds :: non_neg_integer(). seconds(Seconds) -> 1000*Seconds. --spec minutes(non_neg_integer()) -> non_neg_integer(). +-spec minutes(Minutes) -> MilliSeconds when + Minutes :: non_neg_integer(), + MilliSeconds :: non_neg_integer(). minutes(Minutes) -> 1000*60*Minutes. --spec hours(non_neg_integer()) -> non_neg_integer(). +-spec hours(Hours) -> MilliSeconds when + Hours :: non_neg_integer(), + MilliSeconds :: non_neg_integer(). hours(Hours) -> 1000*60*60*Hours. --spec hms(non_neg_integer(), non_neg_integer(), non_neg_integer()) -> non_neg_integer(). +-spec hms(Hours, Minutes, Seconds) -> MilliSeconds when + Hours :: non_neg_integer(), + Minutes :: non_neg_integer(), + Seconds :: non_neg_integer(), + MilliSeconds :: non_neg_integer(). hms(H, M, S) -> hours(H) + minutes(M) + seconds(S). @@ -141,7 +241,7 @@ hms(H, M, S) -> start() -> ensure_started(). --spec start_link() -> {'ok', pid()} | {'error', _}. +-spec start_link() -> {'ok', pid()} | {'error', term()}. start_link() -> gen_server:start_link({local, timer_server}, ?MODULE, [], []). @@ -152,6 +252,7 @@ init([]) -> ?INTERVAL_TAB = ets:new(?INTERVAL_TAB, [named_table,protected]), {ok, [], infinity}. +-spec ensure_started() -> 'ok'. ensure_started() -> case whereis(timer_server) of undefined -> @@ -175,6 +276,10 @@ req(Req, Arg) -> %% %% Time and Timeout is in milliseconds. Started is in microseconds. %% +-type timers() :: term(). % XXX: refine? + +-spec handle_call(term(), term(), timers()) -> + {'reply', term(), timers(), timeout()} | {'noreply', timers(), timeout()}. handle_call({apply_after, {Time, Op}, Started}, _From, _Ts) when is_integer(Time), Time >= 0 -> BRef = {Started + 1000*Time, make_ref()}, @@ -194,7 +299,7 @@ handle_call({apply_interval, {Time, To, MFA}, Started}, _From, _Ts) Interval = Time*1000, BRef2 = {Started + Interval, Ref}, Timer = {BRef2, {repeat, Interval, Pid}, MFA}, - ets:insert(?INTERVAL_TAB,{BRef1,BRef2,Pid}), + ets:insert(?INTERVAL_TAB, {BRef1,BRef2,Pid}), ets:insert(?TIMER_TAB, Timer), Timeout = timer_timeout(SysTime), {reply, {ok, BRef1}, [], Timeout}; @@ -202,7 +307,7 @@ handle_call({apply_interval, {Time, To, MFA}, Started}, _From, _Ts) {reply, {error, badarg}, [], next_timeout()} end; handle_call({cancel, BRef = {_Time, Ref}, _}, _From, Ts) - when is_reference(Ref) -> + when is_reference(Ref) -> delete_ref(BRef), {reply, {ok, cancel}, Ts, next_timeout()}; handle_call({cancel, _BRef, _}, _From, Ts) -> @@ -214,6 +319,7 @@ handle_call({apply_interval, _, _}, _From, Ts) -> handle_call(_Else, _From, Ts) -> % Catch anything else {noreply, Ts, next_timeout()}. +-spec handle_info(term(), timers()) -> {'noreply', timers(), timeout()}. handle_info(timeout, Ts) -> % Handle timeouts Timeout = timer_timeout(system_time()), {noreply, Ts, Timeout}; @@ -223,19 +329,21 @@ handle_info({'EXIT', Pid, _Reason}, Ts) -> % Oops, someone died handle_info(_OtherMsg, Ts) -> % Other Msg's {noreply, Ts, next_timeout()}. +-spec handle_cast(term(), timers()) -> {'noreply', timers(), timeout()}. handle_cast(_Req, Ts) -> % Not predicted but handled {noreply, Ts, next_timeout()}. --spec terminate(_, _) -> 'ok'. +-spec terminate(term(), _State) -> 'ok'. terminate(_Reason, _State) -> ok. +-spec code_change(term(), State, term()) -> {'ok', State}. code_change(_OldVsn, State, _Extra) -> %% According to the man for gen server no timer can be set here. {ok, State}. %% -%% timer_timeout(Timers, SysTime) +%% timer_timeout(SysTime) %% %% Apply and remove already timed-out timers. A timer is a tuple %% {Time, BRef, Op, MFA}, where Time is in microseconds. @@ -279,12 +387,13 @@ delete_ref(BRef = {interval, _}) -> ok end; delete_ref(BRef) -> - ets:delete(?TIMER_TAB,BRef). + ets:delete(?TIMER_TAB, BRef). %% %% pid_delete %% +-spec pid_delete(pid()) -> 'ok'. pid_delete(Pid) -> IntervalTimerList = ets:select(?INTERVAL_TAB, @@ -292,13 +401,14 @@ pid_delete(Pid) -> [{'==','$1',Pid}], ['$_']}]), lists:foreach(fun({IntKey, TimerKey, _ }) -> - ets:delete(?INTERVAL_TAB,IntKey), - ets:delete(?TIMER_TAB,TimerKey) + ets:delete(?INTERVAL_TAB, IntKey), + ets:delete(?TIMER_TAB, TimerKey) end, IntervalTimerList). %% Calculate time to the next timeout. Returned timeout must fit in a %% small int. +-spec next_timeout() -> timeout(). next_timeout() -> case ets:first(?TIMER_TAB) of '$end_of_table' -> @@ -358,7 +468,7 @@ get_pid(_) -> get_status() -> Info1 = ets:info(?TIMER_TAB), - {value,{size,TotalNumTimers}} = lists:keysearch(size, 1, Info1), + {size,TotalNumTimers} = lists:keyfind(size, 1, Info1), Info2 = ets:info(?INTERVAL_TAB), - {value,{size,NumIntervalTimers}} = lists:keysearch(size, 1, Info2), + {size,NumIntervalTimers} = lists:keyfind(size, 1, Info2), {{?TIMER_TAB,TotalNumTimers},{?INTERVAL_TAB,NumIntervalTimers}}. diff --git a/lib/stdlib/src/unicode.erl b/lib/stdlib/src/unicode.erl index 09b1deff9c..a5d9965ca2 100644 --- a/lib/stdlib/src/unicode.erl +++ b/lib/stdlib/src/unicode.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2009. All Rights Reserved. +%% Copyright Ericsson AB 2008-2011. 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,8 +25,39 @@ %% 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([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]). + +-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 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 latin1_binary() :: binary(). +-type latin1_char() :: byte(). +-type latin1_chardata() :: latin1_charlist() | latin1_binary(). +-type latin1_charlist() :: [latin1_char() | latin1_binary() + | latin1_charlist()]. + +-spec characters_to_list(Data) -> Result when + Data :: latin1_chardata() | chardata() | external_chardata(), + Result :: list() + | {error, list(), RestData} + | {incomplete, list(), binary()}, + RestData :: latin1_chardata() | chardata() | external_chardata(). characters_to_list(ML) -> unicode:characters_to_list(ML,unicode). @@ -60,6 +91,13 @@ do_characters_to_list(ML, Encoding) -> end. +-spec characters_to_binary(Data) -> Result when + Data :: latin1_chardata() | chardata() | external_chardata(), + Result :: binary() + | {error, binary(), RestData} + | {incomplete, binary(), binary()}, + RestData :: latin1_chardata() | chardata() | external_chardata(). + characters_to_binary(ML) -> try unicode:characters_to_binary(ML,unicode) @@ -95,6 +133,15 @@ characters_to_binary_int(ML,InEncoding) -> erlang:raise(error,TheError,[{Mod,characters_to_binary,L}|Rest]) end. +-spec characters_to_binary(Data, InEncoding, OutEncoding) -> Result when + Data :: latin1_chardata() | chardata() | external_chardata(), + InEncoding :: encoding(), + OutEncoding :: encoding(), + Result :: binary() + | {error, binary(), RestData} + | {incomplete, binary(), binary()}, + RestData :: latin1_chardata() | chardata() | external_chardata(). + characters_to_binary(ML, latin1, latin1) when is_binary(ML) -> ML; characters_to_binary(ML, latin1, Uni) when is_binary(ML) and ((Uni =:= utf8) or (Uni =:= unicode)) -> @@ -206,6 +253,13 @@ characters_to_binary_int(ML, InEncoding, OutEncoding) -> Res end. +-spec bom_to_encoding(Bin) -> {Encoding, Length} when + Bin :: binary(), + Encoding :: 'latin1' | 'utf8' + | {'utf16', endian()} + | {'utf32', endian()}, + Length :: non_neg_integer(). + bom_to_encoding(<<239,187,191,_/binary>>) -> {utf8,3}; bom_to_encoding(<<0,0,254,255,_/binary>>) -> @@ -219,6 +273,10 @@ bom_to_encoding(<<255,254,_/binary>>) -> bom_to_encoding(Bin) when is_binary(Bin) -> {latin1,0}. +-spec encoding_to_bom(InEncoding) -> Bin when + Bin :: binary(), + InEncoding :: encoding(). + encoding_to_bom(unicode) -> <<239,187,191>>; encoding_to_bom(utf8) -> diff --git a/lib/stdlib/src/win32reg.erl b/lib/stdlib/src/win32reg.erl index ee0d17bc94..598e77ffdc 100644 --- a/lib/stdlib/src/win32reg.erl +++ b/lib/stdlib/src/win32reg.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2011. 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 @@ -52,14 +52,17 @@ -define(reg_dword, 4). %% Basic types internal to this file. --type open_mode() :: 'read' | 'write'. --type reg_handle() :: {'win32reg',port()}. +-opaque reg_handle() :: {'win32reg',port()}. -type name() :: string() | 'default'. -type value() :: string() | integer() | binary(). %%% Exported functions. --spec open([open_mode()]) -> {'ok', reg_handle()} | {'error', 'enotsup'}. +-spec open(OpenModeList) -> ReturnValue when + OpenModeList :: [OpenMode], + OpenMode :: 'read' | 'write', + ReturnValue :: {'ok', RegHandle} | {'error', ErrorId :: 'enotsup'}, + RegHandle :: reg_handle(). open(Modes) -> case os:type() of @@ -75,14 +78,17 @@ open(Modes) -> {error, enotsup} end. --spec close(reg_handle()) -> 'ok'. +-spec close(RegHandle) -> 'ok' when + RegHandle :: reg_handle(). close({win32reg, Reg}) when is_port(Reg) -> unlink(Reg), exit(Reg, die), ok. --spec current_key(reg_handle()) -> {'ok', string()}. +-spec current_key(RegHandle) -> ReturnValue when + RegHandle :: reg_handle(), + ReturnValue :: {'ok', string()}. current_key({win32reg, Reg}) when is_port(Reg) -> Cmd = [?cmd_get_current], @@ -94,12 +100,18 @@ current_key({win32reg, Reg}) when is_port(Reg) -> _ -> Root ++ [$\\|Name] end}. --spec change_key(reg_handle(), string()) -> 'ok' | {'error', atom()}. +-spec change_key(RegHandle, Key) -> ReturnValue when + RegHandle :: reg_handle(), + Key :: string(), + ReturnValue :: 'ok' | {'error', ErrorId :: atom()}. change_key({win32reg, Reg}, Key) when is_port(Reg) -> change_key(Reg, ?cmd_open_key, Key). --spec change_key_create(reg_handle(), string()) -> 'ok' | {'error', atom()}. +-spec change_key_create(RegHandle, Key) -> ReturnValue when + RegHandle :: reg_handle(), + Key :: string(), + ReturnValue :: 'ok' | {'error', ErrorId :: atom()}. change_key_create({win32reg, Reg}, Key) when is_port(Reg) -> change_key(Reg, ?cmd_create_key, Key). @@ -113,21 +125,30 @@ change_key(Reg, Cmd, Key) -> {error, Reason} end. --spec sub_keys(reg_handle()) -> {'ok', [string()]} | {'error', atom()}. +-spec sub_keys(RegHandle) -> ReturnValue when + RegHandle :: reg_handle(), + ReturnValue :: {'ok', [SubKey]} | {'error', ErrorId :: atom()}, + SubKey :: string(). sub_keys({win32reg, Reg}) when is_port(Reg) -> Cmd = [?cmd_get_all_subkeys], Reg ! {self(), {command, Cmd}}, collect_keys(Reg, []). --spec delete_key(reg_handle()) -> 'ok' | {'error', atom()}. +-spec delete_key(RegHandle) -> ReturnValue when + RegHandle :: reg_handle(), + ReturnValue :: 'ok' | {'error', ErrorId :: atom()}. delete_key({win32reg, Reg}) when is_port(Reg) -> Cmd = [?cmd_delete_key], Reg ! {self(), {command, Cmd}}, get_result(Reg). --spec set_value(reg_handle(), name(), value()) -> 'ok' | {'error', atom()}. +-spec set_value(RegHandle, Name, Value) -> ReturnValue when + RegHandle :: reg_handle(), + Name :: name(), + Value :: value(), + ReturnValue :: 'ok' | {'error', ErrorId :: atom()}. set_value({win32reg, Reg}, Name0, Value) when is_port(Reg) -> Name = @@ -140,7 +161,10 @@ set_value({win32reg, Reg}, Name0, Value) when is_port(Reg) -> Reg ! {self(), {command, Cmd}}, get_result(Reg). --spec value(reg_handle(), name()) -> {'ok', value()} | {'error', atom()}. +-spec value(RegHandle, Name) -> ReturnValue when + RegHandle :: reg_handle(), + Name :: name(), + ReturnValue :: {'ok', Value :: value()} | {'error', ErrorId :: atom()}. value({win32reg, Reg}, Name) when is_port(Reg) -> Cmd = [?cmd_get_value, Name, 0], @@ -152,14 +176,20 @@ value({win32reg, Reg}, Name) when is_port(Reg) -> {error, Reason} end. --spec values(reg_handle()) -> {'ok', [{name(), value()}]} | {'error', atom()}. +-spec values(RegHandle) -> ReturnValue when + RegHandle :: reg_handle(), + ReturnValue :: {'ok', [ValuePair]} | {'error', ErrorId :: atom()}, + ValuePair :: {Name :: name(), Value :: value()}. values({win32reg, Reg}) when is_port(Reg) -> Cmd = [?cmd_get_all_values], Reg ! {self(), {command, Cmd}}, collect_values(Reg, []). --spec delete_value(reg_handle(), name()) -> 'ok' | {'error', atom()}. +-spec delete_value(RegHandle, Name) -> ReturnValue when + RegHandle :: reg_handle(), + Name :: name(), + ReturnValue :: 'ok' | {'error', ErrorId :: atom()}. delete_value({win32reg, Reg}, Name0) when is_port(Reg) -> Name = @@ -171,7 +201,9 @@ delete_value({win32reg, Reg}, Name0) when is_port(Reg) -> Reg ! {self(), {command, Cmd}}, get_result(Reg). --spec expand(string()) -> string(). +-spec expand(String) -> ExpandedString when + String :: string(), + ExpandedString :: string(). expand(Value) -> expand(Value, [], []). @@ -195,7 +227,9 @@ expand([C|Rest], Env, Result) -> expand([], [], Result) -> lists:reverse(Result). --spec format_error(atom()) -> string(). +-spec format_error(ErrorId) -> ErrorString when + ErrorId :: atom(), + ErrorString :: string(). format_error(ErrorId) -> erl_posix_msg:message(ErrorId). @@ -203,7 +237,7 @@ format_error(ErrorId) -> %%% Implementation. -spec collect_values(port(), [{name(), value()}]) -> - {'ok', [{name(), value()}]} | {'error', atom()}. + {'ok', [{name(), value()}]} | {'error', ErrorId :: atom()}. collect_values(P, Result) -> case get_result(P) of @@ -215,7 +249,7 @@ collect_values(P, Result) -> {error, Reason} end. --spec collect_keys(port(), string()) -> {'ok', [string()]} | {'error', atom()}. +-spec collect_keys(port(), string()) -> {'ok', [string()]} | {'error', ErrorId :: atom()}. collect_keys(P, Result) -> case get_result(P) of diff --git a/lib/stdlib/src/zip.erl b/lib/stdlib/src/zip.erl index f44d97c227..524d709431 100644 --- a/lib/stdlib/src/zip.erl +++ b/lib/stdlib/src/zip.erl @@ -1,26 +1,26 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2006-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2006-2011. 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(zip). %% Basic api -export([unzip/1, unzip/2, extract/1, extract/2, - zip/2, zip/3, create/2, create/3, + zip/2, zip/3, create/2, create/3, foldl/3, list_dir/1, list_dir/2, table/1, table/2, t/1, tt/1]). @@ -38,7 +38,7 @@ zip_t/1, zip_tt/1, zip_list_dir/1, zip_list_dir/2, zip_close/1]). - + %% just for debugging zip server, not documented, not tested, not to be used -export([zip_get_state/1]). @@ -82,7 +82,7 @@ -record(openzip_opts, { output, % output object (fun) open_opts, % file:open options - cwd % directory to relate paths to + cwd % directory to relate paths to }). % openzip record, state for an open zip-file @@ -93,10 +93,10 @@ input, % archive io object (fun) output, % output io object (fun) zlib, % handle to open zlib - cwd % directory to relate paths to + cwd % directory to relate paths to }). -% Things that I would like to add to the public record #zip_file, +% Things that I would like to add to the public record #zip_file, % but can't as it would make things fail at upgrade. % Instead we use {#zip_file,#zip_file_extra} internally. -record(zip_file_extra, { @@ -203,6 +203,9 @@ zip_comment_length}). +-type zip_file() :: #zip_file{}. +-type zip_comment() :: #zip_comment{}. + %% Open a zip archive with options %% @@ -278,7 +281,7 @@ file_name_search(Name,Files) -> [ZFile|_] -> ZFile; [] -> false end. - + %% %% add a file to an open archive %% openzip_add(File, OpenZip) -> %% case ?CATCH do_openzip_add(File, OpenZip) of @@ -323,8 +326,33 @@ openzip_close(_) -> %% Accepted options: %% verbose, cooked, file_list, keep_old_files, file_filter, memory +-spec(unzip(Archive) -> RetValue when + Archive :: file:name() | binary(), + RetValue :: {ok, FileList} + | {ok, FileBinList} + | {error, Reason :: term()} + | {error, {Name :: file:name(), Reason :: term()}}, + FileList :: [file:name()], + FileBinList :: [{file:name(),binary()}]). + unzip(F) -> unzip(F, []). +-spec(unzip(Archive, Options) -> RetValue when + Archive :: file:name() | binary(), + Options :: [Option], + Option :: {file_list, FileList} + | keep_old_files | verbose | memory | + {file_filter, FileFilter} | {cwd, CWD}, + FileList :: [file:name()], + FileBinList :: [{file:name(),binary()}], + FileFilter :: fun((ZipFile) -> boolean()), + CWD :: string(), + ZipFile :: zip_file(), + RetValue :: {ok, FileList} + | {ok, FileBinList} + | {error, Reason :: term()} + | {error, {Name :: file:name(), Reason :: term()}}). + unzip(F, Options) -> case ?CATCH do_unzip(F, Options) of {ok, R} -> {ok, R}; @@ -344,13 +372,69 @@ do_unzip(F, Options) -> Input(close, In1), {ok, Files}. +%% Iterate over all files in a zip archive +-spec(foldl(Fun, Acc0, Archive) -> {ok, Acc1} | {error, Reason} when + Fun :: fun((FileInArchive, GetInfo, GetBin, AccIn) -> AccOut), + FileInArchive :: file:name(), + GetInfo :: fun(() -> file:file_info()), + GetBin :: fun(() -> binary()), + Acc0 :: term(), + Acc1 :: term(), + AccIn :: term(), + AccOut :: term(), + Archive :: file:name() | {file:name(), binary()}, + Reason :: term()). + +foldl(Fun, Acc0, Archive) when is_function(Fun, 4) -> + ZipFun = + fun({Name, GetInfo, GetBin}, A) -> + A2 = Fun(Name, GetInfo, GetBin, A), + {true, false, A2} + end, + case prim_zip:open(ZipFun, Acc0, Archive) of + {ok, PrimZip, Acc1} -> + ok = prim_zip:close(PrimZip), + {ok, Acc1}; + {error, bad_eocd} -> + {error, "Not an archive file"}; + {error, Reason} -> + {error, Reason} + end; +foldl(_,_, _) -> + {error, einval}. + %% Create zip archive name F from Files or binaries %% %% Accepted options: %% verbose, cooked, memory, comment +-spec(zip(Name, FileList) -> RetValue when + Name :: file:name(), + FileList :: [FileSpec], + FileSpec :: file:name() | {file:name(), binary()} + | {file:name(), binary(), file:file_info()}, + RetValue :: {ok, FileName :: file:name()} + | {ok, {FileName :: file:name(), binary()}} + | {error, Reason :: term()}). + zip(F, Files) -> zip(F, Files, []). +-spec(zip(Name, FileList, Options) -> RetValue when + Name :: file:name(), + FileList :: [FileSpec], + FileSpec :: file:name() | {file:name(), binary()} + | {file:name(), binary(), file:file_info()}, + Options :: [Option], + Option :: memory | cooked | verbose | {comment, Comment} + | {cwd, CWD} | {compress, What} | {uncompress, What}, + What :: all | [Extension] | {add, [Extension]} | {del, [Extension]}, + Extension :: string(), + Comment :: string(), + CWD :: string(), + RetValue :: {ok, FileName :: file:name()} + | {ok, {FileName :: file:name(), binary()}} + | {error, Reason :: term()}). + zip(F, Files, Options) -> case ?CATCH do_zip(F, Files, Options) of {ok, R} -> {ok, R}; @@ -373,8 +457,20 @@ do_zip(F, Files, Options) -> %% Accepted options: %% cooked, file_filter, file_output (latter 2 undocumented) +-spec(list_dir(Archive) -> RetValue when + Archive :: file:name() | binary(), + RetValue :: {ok, CommentAndFiles} | {error, Reason :: term()}, + CommentAndFiles :: [zip_comment() | zip_file()]). + list_dir(F) -> list_dir(F, []). +-spec(list_dir(Archive, Options) -> RetValue when + Archive :: file:name() | binary(), + RetValue :: {ok, CommentAndFiles} | {error, Reason :: term()}, + CommentAndFiles :: [zip_comment() | zip_file()], + Options :: [Option], + Option :: cooked). + list_dir(F, Options) -> case ?CATCH do_list_dir(F, Options) of {ok, R} -> {ok, R}; @@ -383,7 +479,7 @@ list_dir(F, Options) -> do_list_dir(F, Options) -> Opts = get_list_dir_options(F, Options), - #list_dir_opts{input = Input, open_opts = OpO, + #list_dir_opts{input = Input, open_opts = OpO, raw_iterator = RawIterator} = Opts, In0 = Input({open, F, OpO}, []), {Info, In1} = get_central_dir(In0, RawIterator, Input), @@ -392,6 +488,10 @@ do_list_dir(F, Options) -> %% Print zip directory in short form +-spec(t(Archive) -> ok when + Archive :: file:name() | binary | ZipHandle, + ZipHandle :: pid()). + t(F) when is_pid(F) -> zip_t(F); t(F) when is_record(F, openzip) -> openzip_t(F); t(F) -> t(F, fun raw_short_print_info_etc/5). @@ -412,12 +512,16 @@ do_t(F, RawPrint) -> %% Print zip directory in long form (like ls -l) +-spec(tt(Archive) -> ok when + Archive :: file:name() | binary | ZipHandle, + ZipHandle :: pid()). + tt(F) when is_pid(F) -> zip_tt(F); tt(F) when is_record(F, openzip) -> openzip_tt(F); tt(F) -> t(F, fun raw_long_print_info_etc/5). -%% option utils +%% option utils get_unzip_opt([], Opts) -> Opts; get_unzip_opt([verbose | Rest], Opts) -> @@ -470,7 +574,7 @@ get_zip_opt([{cwd, CWD} | Rest], Opts) -> get_zip_opt([{comment, C} | Rest], Opts) -> get_zip_opt(Rest, Opts#zip_opts{comment = C}); get_zip_opt([{compress, Which} = O| Rest], Opts) -> - Which2 = + Which2 = case Which of all -> all; @@ -485,7 +589,7 @@ get_zip_opt([{compress, Which} = O| Rest], Opts) -> end, get_zip_opt(Rest, Opts#zip_opts{compress = Which2}); get_zip_opt([{uncompress, Which} = O| Rest], Opts) -> - Which2 = + Which2 = case Which of all -> all; @@ -560,16 +664,24 @@ get_openzip_options(Options) -> get_input(F) when is_binary(F) -> fun binary_io/2; get_input(F) when is_list(F) -> - fun file_io/2. + fun file_io/2; +get_input(_) -> + throw(einval). get_zip_input({F, B}) when is_binary(B), is_list(F) -> fun binary_io/2; +get_zip_input({F, B, #file_info{}}) when is_binary(B), is_list(F) -> + fun binary_io/2; +get_zip_input({F, #file_info{}, B}) when is_binary(B), is_list(F) -> + fun binary_io/2; get_zip_input(F) when is_list(F) -> fun file_io/2; get_zip_input({files, []}) -> fun binary_io/2; get_zip_input({files, [File | _]}) -> - get_zip_input(File). + get_zip_input(File); +get_zip_input(_) -> + throw(einval). get_list_dir_options(F, Options) -> Opts = #list_dir_opts{raw_iterator = fun raw_file_info_public/5, @@ -578,11 +690,78 @@ get_list_dir_options(F, Options) -> get_list_dir_opt(Options, Opts). %% aliases for erl_tar compatibility +-spec(table(Archive) -> RetValue when + Archive :: file:name() | binary(), + RetValue :: {ok, CommentAndFiles} | {error, Reason :: term()}, + CommentAndFiles :: [zip_comment() | zip_file()]). + table(F) -> list_dir(F). + +-spec(table(Archive, Options) -> RetValue when + Archive :: file:name() | binary(), + RetValue :: {ok, CommentAndFiles} | {error, Reason :: term()}, + CommentAndFiles :: [zip_comment() | zip_file()], + + Options :: [Option], + Option :: cooked). + table(F, O) -> list_dir(F, O). + +-spec(create(Name, FileList) -> RetValue when + Name :: file:name(), + FileList :: [FileSpec], + FileSpec :: file:name() | {file:name(), binary()} + | {file:name(), binary(), file:file_info()}, + RetValue :: {ok, FileName :: file:name()} + | {ok, {FileName :: file:name(), binary()}} + | {error, Reason :: term()}). + create(F, Fs) -> zip(F, Fs). + +-spec(create(Name, FileList, Options) -> RetValue when + Name :: file:name(), + FileList :: [FileSpec], + FileSpec :: file:name() | {file:name(), binary()} + | {file:name(), binary(), file:file_info()}, + Options :: [Option], + Option :: memory | cooked | verbose | {comment, Comment} + | {cwd, CWD} | {compress, What} | {uncompress, What}, + What :: all | [Extension] | {add, [Extension]} | {del, [Extension]}, + Extension :: string(), + Comment :: string(), + CWD :: string(), + RetValue :: {ok, FileName :: file:name()} + | {ok, {FileName :: file:name(), binary()}} + | {error, Reason :: term()}). create(F, Fs, O) -> zip(F, Fs, O). + +-spec(extract(Archive) -> RetValue when + Archive :: file:name() | binary(), + RetValue :: {ok, FileList} + | {ok, FileBinList} + | {error, Reason :: term()} + | {error, {Name :: file:name(), Reason :: term()}}, + FileList :: [file:name()], + FileBinList :: [{file:name(),binary()}]). + extract(F) -> unzip(F). + +-spec(extract(Archive, Options) -> RetValue when + Archive :: file:name() | binary(), + Options :: [Option], + Option :: {file_list, FileList} + | keep_old_files | verbose | memory | + {file_filter, FileFilter} | {cwd, CWD}, + FileList :: [file:name()], + FileBinList :: [{file:name(),binary()}], + FileFilter :: fun((ZipFile) -> boolean()), + CWD :: string(), + ZipFile :: zip_file(), + RetValue :: {ok, FileList} + | {ok, FileBinList} + | {error, Reason :: term()} + | {error, {Name :: file:name(), Reason :: term()}}). + extract(F, O) -> unzip(F, O). @@ -620,6 +799,8 @@ put_eocd(N, Pos, Sz, Comment, Output, Out0) -> get_filename({Name, _}, Type) -> get_filename(Name, Type); +get_filename({Name, _, _}, Type) -> + get_filename(Name, Type); get_filename(Name, regular) -> Name; get_filename(Name, directory) -> @@ -895,7 +1076,7 @@ local_file_header_to_bin( CompSize:32/little, UncompSize:32/little, FileNameLength:16/little, - ExtraFieldLength:16/little>>. + ExtraFieldLength:16/little>>. eocd_to_bin(#eocd{disk_num = DiskNum, start_disk_num = StartDiskNum, @@ -912,7 +1093,7 @@ eocd_to_bin(#eocd{disk_num = DiskNum, Offset:32/little, ZipCommentLength:16/little>>. -%% put together a local file header +%% put together a local file header local_file_header_from_info_method_name(#file_info{mtime = MTime}, UncompSize, CompMethod, Name) -> @@ -939,7 +1120,7 @@ server_loop(OpenZip) -> server_loop(NewOpenZip); Error -> From ! {self(), Error} - end; + end; {From, close} -> From ! {self(), openzip_close(OpenZip)}; {From, get} -> @@ -961,21 +1142,52 @@ server_loop(OpenZip) -> {error, bad_msg} end. +-spec(zip_open(Archive) -> {ok, ZipHandle} | {error, Reason} when + Archive :: file:name() | binary(), + ZipHandle :: pid(), + Reason :: term()). + zip_open(Archive) -> zip_open(Archive, []). +-spec(zip_open(Archive, Options) -> {ok, ZipHandle} | {error, Reason} when + Archive :: file:name() | binary(), + ZipHandle :: pid(), + Options :: [Option], + Option :: cooked | memory | {cwd, CWD :: string()}, + Reason :: term()). + zip_open(Archive, Options) -> Pid = spawn(fun() -> server_loop(not_open) end), request(self(), Pid, {open, Archive, Options}). +-spec(zip_get(ZipHandle) -> {ok, [Result]} | {error, Reason} when + ZipHandle :: pid(), + Result :: file:name() | {file:name(), binary()}, + Reason :: term()). + zip_get(Pid) when is_pid(Pid) -> request(self(), Pid, get). +-spec(zip_close(ZipHandle) -> ok | {error, einval} when + ZipHandle :: pid()). + zip_close(Pid) when is_pid(Pid) -> request(self(), Pid, close). +-spec(zip_get(FileName, ZipHandle) -> {ok, [Result]} | {error, Reason} when + FileName :: file:name(), + ZipHandle :: pid(), + Result :: file:name() | {file:name(), binary()}, + Reason :: term()). + zip_get(FileName, Pid) when is_pid(Pid) -> request(self(), Pid, {get, FileName}). +-spec(zip_list_dir(ZipHandle) -> Result | {error, Reason} when + Result :: [zip_comment() | zip_file()], + ZipHandle :: pid(), + Reason :: term()). + zip_list_dir(Pid) when is_pid(Pid) -> request(self(), Pid, list_dir). @@ -1024,7 +1236,7 @@ lists_foreach(F, [Hd|Tl]) -> F(Hd), lists_foreach(F, Tl). -%% option utils +%% option utils get_openzip_opt([], Opts) -> Opts; get_openzip_opt([cooked | Rest], #openzip_opts{open_opts = OO} = Opts) -> @@ -1121,7 +1333,7 @@ raw_file_info_public(CD, FileName, FileComment, BExtraField, Acc0) -> Other -> Other end, [H2|T]. - + %% make a file_info from a central directory header cd_file_header_to_file_info(FileName, @@ -1213,8 +1425,8 @@ get_z_file(In0, Z, Input, Output, OpO, FB, CWD, {ZipFile,Extra}) -> {dir, In3}; _ -> %% FileInfo = local_file_header_to_file_info(LH) - %%{Out, In4, CRC, UncompSize} = - {Out, In4, CRC, _UncompSize} = + %%{Out, In4, CRC, UncompSize} = + {Out, In4, CRC, _UncompSize} = get_z_data(CompMethod, In3, FileName1, CompSize, Input, Output, OpO, Z), In5 = skip_z_data_descriptor(GPFlag, Input, In4), @@ -1280,7 +1492,7 @@ get_z_data_loop(CompSize, UncompSize, In0, Out0, Input, Output, Z) -> Out1 = Output({write, Uncompressed}, Out0), get_z_data_loop(CompSize-N, UncompSize + iolist_size(Uncompressed), In1, Out1, Input, Output, Z) - end. + end. %% skip data descriptor if any @@ -1298,7 +1510,7 @@ dos_date_time_to_datetime(DosDate, DosTime) -> <<Hour:5, Min:6, Sec:5>> = <<DosTime:16>>, <<YearFrom1980:7, Month:4, Day:5>> = <<DosDate:16>>, {{YearFrom1980+1980, Month, Day}, - {Hour, Min, Sec}}. + {Hour, Min, Sec}}. dos_date_time_from_datetime({{Year, Month, Day}, {Hour, Min, Sec}}) -> YearFrom1980 = Year-1980, @@ -1319,7 +1531,6 @@ unix_extra_field_and_var_from_bin(<<TSize:16/little, Var}; unix_extra_field_and_var_from_bin(_) -> throw(bad_unix_extra_field). - %% A pwrite-like function for iolists (used by memory-option) @@ -1478,6 +1689,8 @@ local_file_header_from_bin(_) -> %% io functions binary_io({file_info, {_Filename, _B, #file_info{} = FI}}, _A) -> FI; +binary_io({file_info, {_Filename, #file_info{} = FI, _B}}, _A) -> + FI; binary_io({file_info, {_Filename, B}}, A) -> binary_io({file_info, B}, A); binary_io({file_info, B}, _) -> @@ -1493,9 +1706,11 @@ binary_io({file_info, B}, _) -> links = 1, major_device = 0, minor_device = 0, inode = 0, uid = 0, gid = 0}; -binary_io({open, {_Filename, B, _FI}, _Opts}, _) -> +binary_io({open, {_Filename, B, _FI}, _Opts}, _) when is_binary(B) -> + {0, B}; +binary_io({open, {_Filename, _FI, B}, _Opts}, _) when is_binary(B) -> {0, B}; -binary_io({open, {_Filename, B}, _Opts}, _) -> +binary_io({open, {_Filename, B}, _Opts}, _) when is_binary(B) -> {0, B}; binary_io({open, B, _Opts}, _) when is_binary(B) -> {0, B}; diff --git a/lib/stdlib/test/Makefile b/lib/stdlib/test/Makefile index 9beac93eb8..5502c69fa5 100644 --- a/lib/stdlib/test/Makefile +++ b/lib/stdlib/test/Makefile @@ -9,6 +9,8 @@ MODULES= \ array_SUITE \ base64_SUITE \ beam_lib_SUITE \ + binary_module_SUITE \ + binref \ c_SUITE \ calendar_SUITE \ dets_SUITE \ @@ -131,9 +133,9 @@ release_spec: opt release_tests_spec: make_emakefile $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) stdlib.spec stdlib.spec.vxworks $(EMAKEFILE) \ + $(INSTALL_DATA) stdlib.spec $(EMAKEFILE) \ $(ERL_FILES) $(COVERFILE) $(RELSYSDIR) - chmod -f -R u+w $(RELSYSDIR) + chmod -R u+w $(RELSYSDIR) @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) release_docs_spec: diff --git a/lib/stdlib/test/array_SUITE.erl b/lib/stdlib/test/array_SUITE.erl index e7cfc65be1..1b496bb8ec 100644 --- a/lib/stdlib/test/array_SUITE.erl +++ b/lib/stdlib/test/array_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2009. All Rights Reserved. +%% Copyright Ericsson AB 2007-2011. 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 +19,7 @@ -module(array_SUITE). --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). %% Default timetrap timeout (set in init_per_testcase). %% This should be set relatively high (10-15 times the expected @@ -27,8 +27,9 @@ -define(default_timeout, ?t:seconds(60)). %% Test server specific exports --export([all/1]). --export([init_per_testcase/2, fin_per_testcase/2]). +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2]). +-export([init_per_testcase/2, end_per_testcase/2]). -export([ new_test/1, @@ -64,33 +65,37 @@ %% %% all/1 %% -all(doc) -> - []; -all(suite) -> - [new_test, - fix_test, - relax_test, - resize_test, - set_get_test, - to_list_test, - sparse_to_list_test, - from_list_test, - to_orddict_test, - sparse_to_orddict_test, - from_orddict_test, - map_test, - sparse_map_test, - foldl_test, - sparse_foldl_test, - foldr_test, - sparse_foldr_test - ]. +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [new_test, fix_test, relax_test, resize_test, + set_get_test, to_list_test, sparse_to_list_test, + from_list_test, to_orddict_test, sparse_to_orddict_test, + from_orddict_test, map_test, sparse_map_test, + foldl_test, sparse_foldl_test, foldr_test, + sparse_foldr_test]. + +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + init_per_testcase(_Case, Config) -> ?line Dog=test_server:timetrap(?default_timeout), [{watchdog, Dog}|Config]. -fin_per_testcase(_Case, Config) -> +end_per_testcase(_Case, Config) -> Dog=?config(watchdog, Config), test_server:timetrap_cancel(Dog), ok. diff --git a/lib/stdlib/test/base64_SUITE.erl b/lib/stdlib/test/base64_SUITE.erl index 44742063b3..c64a961ffa 100644 --- a/lib/stdlib/test/base64_SUITE.erl +++ b/lib/stdlib/test/base64_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2009. All Rights Reserved. +%% Copyright Ericsson AB 2007-2011. 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,18 +18,19 @@ %% -module(base64_SUITE). --author('[email protected]'). --include("test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). -include("test_server_line.hrl"). %% Test server specific exports --export([all/1, init_per_testcase/2, end_per_testcase/2]). +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2, + init_per_testcase/2, end_per_testcase/2]). %% Test cases must be exported. -export([base64_encode/1, base64_decode/1, base64_otp_5635/1, base64_otp_6279/1, big/1, illegal/1, mime_decode/1, - roundtrip/1]). + mime_decode_to_string/1, roundtrip/1]). init_per_testcase(_, Config) -> Dog = test_server:timetrap(?t:minutes(2)), @@ -44,14 +45,29 @@ end_per_testcase(_, Config) -> %%------------------------------------------------------------------------- %% Test cases starts here. %%------------------------------------------------------------------------- -all(doc) -> - ["Test library functions for base64 encode and decode " - "(taken from inets/test/http_format_SUITE)"]; -all(suite) -> +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> [base64_encode, base64_decode, base64_otp_5635, - base64_otp_6279, big, illegal, mime_decode, + base64_otp_6279, big, illegal, mime_decode, mime_decode_to_string, roundtrip]. +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + %%------------------------------------------------------------------------- base64_encode(doc) -> @@ -59,7 +75,7 @@ base64_encode(doc) -> base64_encode(suite) -> []; base64_encode(Config) when is_list(Config) -> - %% Two pads + %% Two pads <<"QWxhZGRpbjpvcGVuIHNlc2FtZQ==">> = base64:encode("Aladdin:open sesame"), %% One pad @@ -77,8 +93,8 @@ base64_decode(doc) -> base64_decode(suite) -> []; base64_decode(Config) when is_list(Config) -> - %% Two pads - <<"Aladdin:open sesame">> = + %% Two pads + <<"Aladdin:open sesame">> = base64:decode("QWxhZGRpbjpvcGVuIHNlc2FtZQ=="), %% One pad <<"Hello World">> = base64:decode(<<"SGVsbG8gV29ybGQ=">>), @@ -138,20 +154,85 @@ illegal(Config) when is_list(Config) -> {'EXIT',{function_clause, _}} = (catch base64:decode("()")), ok. %%------------------------------------------------------------------------- +%% mime_decode and mime_decode_to_string have different implementations +%% so test both with the same input separately. Both functions have +%% the same implementation for binary/string arguments. mime_decode(doc) -> ["Test base64:mime_decode/1."]; mime_decode(suite) -> []; mime_decode(Config) when is_list(Config) -> - %% Two pads - <<"Aladdin:open sesame">> = + %% Test correct padding + <<"one">> = base64:mime_decode(<<"b25l">>), + <<"on">> = base64:mime_decode(<<"b24=">>), + <<"o">> = base64:mime_decode(<<"bw==">>), + %% Test 1 extra padding + <<"one">> = base64:mime_decode(<<"b25l= =">>), + <<"on">> = base64:mime_decode(<<"b24== =">>), + <<"o">> = base64:mime_decode(<<"bw=== =">>), + %% Test 2 extra padding + <<"one">> = base64:mime_decode(<<"b25l===">>), + <<"on">> = base64:mime_decode(<<"b24====">>), + <<"o">> = base64:mime_decode(<<"bw=====">>), + %% Test misc embedded padding + <<"one">> = base64:mime_decode(<<"b2=5l===">>), + <<"on">> = base64:mime_decode(<<"b=24====">>), + <<"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=�= ==">>), + <<"o">> = base64:mime_decode(<<"\nb=w=====">>), + %% Two pads + <<"Aladdin:open sesame">> = base64:mime_decode("QWxhZGRpbjpvc()GVuIHNlc2FtZQ=="), - %% One pad, followed by ignored text - <<"Hello World">> = base64:mime_decode(<<"SGVsb)(G8gV29ybGQ=apa">>), + %% One pad to ignore, followed by more text + <<"Hello World!!">> = base64:mime_decode(<<"SGVsb)(G8gV29ybGQ=h IQ= =">>), + %% No pad + <<"Aladdin:open sesam">> = + base64:mime_decode("QWxhZGRpbjpvcG�\")(VuIHNlc2Ft"), + %% Encoded base 64 strings may be divided by non base 64 chars. + %% In this cases whitespaces. + <<"0123456789!@#0^&*();:<>,. []{}">> = + base64:mime_decode( + <<"MDEy MzQ1Njc4 \tOSFAIzBeJ \nio)(oKTs6 PD4sLi \r\nBbXXt9">>), + ok. + +%%------------------------------------------------------------------------- + +%% Repeat of mime_decode() tests +mime_decode_to_string(doc) -> + ["Test base64:mime_decode_to_string/1."]; +mime_decode_to_string(suite) -> + []; +mime_decode_to_string(Config) when is_list(Config) -> + %% Test correct padding + "one" = base64:mime_decode_to_string(<<"b25l">>), + "on" = base64:mime_decode_to_string(<<"b24=">>), + "o" = base64:mime_decode_to_string(<<"bw==">>), + %% Test 1 extra padding + "one" = base64:mime_decode_to_string(<<"b25l= =">>), + "on" = base64:mime_decode_to_string(<<"b24== =">>), + "o" = base64:mime_decode_to_string(<<"bw=== =">>), + %% Test 2 extra padding + "one" = base64:mime_decode_to_string(<<"b25l===">>), + "on" = base64:mime_decode_to_string(<<"b24====">>), + "o" = base64:mime_decode_to_string(<<"bw=====">>), + %% Test misc embedded padding + "one" = base64:mime_decode_to_string(<<"b2=5l===">>), + "on" = base64:mime_decode_to_string(<<"b=24====">>), + "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=�= ==">>), + "o" = base64:mime_decode_to_string(<<"\nb=w=====">>), + %% Two pads + "Aladdin:open sesame" = + base64:mime_decode_to_string("QWxhZGRpbjpvc()GVuIHNlc2FtZQ=="), + %% One pad to ignore, followed by more text + "Hello World!!" = base64:mime_decode_to_string(<<"SGVsb)(G8gV29ybGQ=h IQ= =">>), %% No pad "Aladdin:open sesam" = 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^&*();:<>,. []{}" = @@ -159,6 +240,7 @@ mime_decode(Config) when is_list(Config) -> <<"MDEy MzQ1Njc4 \tOSFAIzBeJ \nio)(oKTs6 PD4sLi \r\nBbXXt9">>), ok. +%%------------------------------------------------------------------------- roundtrip(Config) when is_list(Config) -> Sizes = lists:seq(1, 255) ++ lists:seq(2400-5, 2440), diff --git a/lib/stdlib/test/beam_lib_SUITE.erl b/lib/stdlib/test/beam_lib_SUITE.erl index bc867a3770..4ccc863795 100644 --- a/lib/stdlib/test/beam_lib_SUITE.erl +++ b/lib/stdlib/test/beam_lib_SUITE.erl @@ -1,6 +1,19 @@ %% %% %CopyrightBegin% %% +%% Copyright Ericsson AB 1997-2011. 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(beam_lib_SUITE). @@ -14,25 +27,45 @@ -define(t,test_server). -define(privdir, "beam_lib_SUITE_priv"). -else. --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). -define(format(S, A), ok). -define(privdir, ?config(priv_dir, Conf)). -endif. --export([all/1, normal/1, error/1, cmp/1, cmp_literals/1, strip/1, otp_6711/1, +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2, + normal/1, error/1, cmp/1, cmp_literals/1, strip/1, otp_6711/1, building/1, md5/1, encrypted_abstr/1, encrypted_abstr_file/1]). --export([init_per_testcase/2, fin_per_testcase/2]). +-export([init_per_testcase/2, end_per_testcase/2]). + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [error, normal, cmp, cmp_literals, strip, otp_6711, + building, md5, encrypted_abstr, encrypted_abstr_file]. + +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. -all(suite) -> - [error, normal, cmp, cmp_literals, strip, otp_6711, building, md5, - encrypted_abstr, encrypted_abstr_file]. init_per_testcase(_Case, Config) -> Dog=?t:timetrap(?t:minutes(2)), [{watchdog, Dog}|Config]. -fin_per_testcase(_Case, Config) -> +end_per_testcase(_Case, Config) -> Dog=?config(watchdog, Config), test_server:timetrap_cancel(Dog), ok. diff --git a/lib/stdlib/test/binary_module_SUITE.erl b/lib/stdlib/test/binary_module_SUITE.erl new file mode 100644 index 0000000000..8fb63f33bd --- /dev/null +++ b/lib/stdlib/test/binary_module_SUITE.erl @@ -0,0 +1,1363 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1997-2011. 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(binary_module_SUITE). + +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2, + interesting/1,random_ref_comp/1,random_ref_sr_comp/1, + random_ref_fla_comp/1,parts/1, bin_to_list/1, list_to_bin/1, + copy/1, referenced/1,guard/1,encode_decode/1,badargs/1,longest_common_trap/1]). + +-export([random_number/1, make_unaligned/1]). + + + +%%-define(STANDALONE,1). + +-ifdef(STANDALONE). + +-define(line,erlang:display({?MODULE,?LINE}),). + +-else. + +-include_lib("test_server/include/test_server.hrl"). +-export([init_per_testcase/2, end_per_testcase/2]). +% Default timetrap timeout (set in init_per_testcase). +% Some of these testcases are really heavy... +-define(default_timeout, ?t:minutes(20)). + +-endif. + + + +-ifdef(STANDALONE). +-export([run/0]). + +run() -> + [ apply(?MODULE,X,[[]]) || X <- all(suite) ]. + +-else. + +init_per_testcase(_Case, Config) -> + ?line Dog = ?t:timetrap(?default_timeout), + [{watchdog, Dog} | Config]. + +end_per_testcase(_Case, Config) -> + ?line Dog = ?config(watchdog, Config), + ?line test_server:timetrap_cancel(Dog), + ok. +-endif. + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [interesting, random_ref_fla_comp, random_ref_sr_comp, + random_ref_comp, parts, bin_to_list, list_to_bin, copy, + referenced, guard, encode_decode, badargs, + longest_common_trap]. + +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + +-define(MASK_ERROR(EXPR),mask_error((catch (EXPR)))). + + +badargs(doc) -> + ["Tests various badarg exceptions in the module"]; +badargs(Config) when is_list(Config) -> + ?line badarg = ?MASK_ERROR(binary:compile_pattern([<<1,2,3:3>>])), + ?line badarg = ?MASK_ERROR(binary:compile_pattern([<<1,2,3>>|<<1,2>>])), + ?line badarg = ?MASK_ERROR(binary:compile_pattern(<<1,2,3:3>>)), + ?line badarg = ?MASK_ERROR(binary:compile_pattern(<<>>)), + ?line badarg = ?MASK_ERROR(binary:match(<<1,2,3:3>>,<<1>>)), + ?line badarg = ?MASK_ERROR(binary:matches(<<1,2,3:3>>,<<1>>)), + ?line badarg = ?MASK_ERROR(binary:match(<<1,2,3>>,<<1>>, + [{scope,{0,1},1}])), + ?line badarg = ?MASK_ERROR(binary:match(<<1,2,3>>,<<1>>, + [{scape,{0,1}}])), + ?line badarg = ?MASK_ERROR(binary:match(<<1,2,3>>,<<1>>, + [{scope,{0,1,1}}])), + ?line badarg = ?MASK_ERROR(binary:match(<<1,2,3>>,<<1>>,[{scope,0,1}])), + ?line badarg = ?MASK_ERROR(binary:match(<<1,2,3>>,<<1>>,[{scope,[0,1]}])), + ?line badarg = ?MASK_ERROR(binary:match(<<1,2,3>>,<<1>>, + [{scope,{0.1,1}}])), + ?line badarg = ?MASK_ERROR(binary:match(<<1,2,3>>,<<1>>, + [{scope,{1,1.1}}])), + ?line badarg = + ?MASK_ERROR( + binary:match(<<1,2,3>>,<<1>>, + [{scope,{16#FF, + 16#FFFFFFFFFFFFFFFF}}])), + ?line badarg = + ?MASK_ERROR( + binary:match(<<1,2,3>>,<<1>>, + [{scope,{16#FFFFFFFFFFFFFFFF, + -16#7FFFFFFFFFFFFFFF-1}}])), + ?line badarg = + ?MASK_ERROR( + binary:match(<<1,2,3>>,<<1>>, + [{scope,{16#FFFFFFFFFFFFFFFF, + 16#7FFFFFFFFFFFFFFF}}])), + ?line badarg = + ?MASK_ERROR( + binary:part(<<1,2,3>>,{16#FF, + 16#FFFFFFFFFFFFFFFF})), + ?line badarg = + ?MASK_ERROR( + binary:part(<<1,2,3>>,{16#FFFFFFFFFFFFFFFF, + -16#7FFFFFFFFFFFFFFF-1})), + ?line badarg = + ?MASK_ERROR( + binary:part(<<1,2,3>>,{16#FFFFFFFFFFFFFFFF, + 16#7FFFFFFFFFFFFFFF})), + ?line badarg = + ?MASK_ERROR( + binary:part(make_unaligned(<<1,2,3>>),{1,1,1})), + ?line badarg = + ?MASK_ERROR( + binary_part(make_unaligned(<<1,2,3>>),{1,1,1})), + ?line badarg = + ?MASK_ERROR( + binary_part(make_unaligned(<<1,2,3>>),{16#FFFFFFFFFFFFFFFF, + -16#7FFFFFFFFFFFFFFF-1})), + ?line badarg = + ?MASK_ERROR( + binary_part(make_unaligned(<<1,2,3>>),{16#FF, + 16#FFFFFFFFFFFFFFFF})), + ?line badarg = + ?MASK_ERROR( + binary_part(make_unaligned(<<1,2,3>>),{16#FFFFFFFFFFFFFFFF, + 16#7FFFFFFFFFFFFFFF})), + ?line badarg = + ?MASK_ERROR( + binary_part(make_unaligned(<<1,2,3>>),{16#FFFFFFFFFFFFFFFFFF, + -16#7FFF})), + ?line badarg = + ?MASK_ERROR( + binary_part(make_unaligned(<<1,2,3>>),{16#FF, + -16#7FFF})), + ?line badarg = + ?MASK_ERROR( + binary:bin_to_list(<<1,2,3>>,{16#FF, + 16#FFFFFFFFFFFFFFFF})), + ?line badarg = + ?MASK_ERROR( + binary:bin_to_list(<<1,2,3>>,{16#FFFFFFFFFFFFFFFF, + -16#7FFFFFFFFFFFFFFF-1})), + ?line badarg = + ?MASK_ERROR( + binary:bin_to_list(<<1,2,3>>,{16#FFFFFFFFFFFFFFFF, + 16#7FFFFFFFFFFFFFFF})), + ?line [1,2,3] = + ?MASK_ERROR( + binary:bin_to_list(<<1,2,3>>)), + ?line badarg = + ?MASK_ERROR( + binary:bin_to_list(<<1,2,3>>,[])), + ?line badarg = + ?MASK_ERROR( + binary:bin_to_list(<<1,2,3>>,{1,2,3})), + ?line badarg = + ?MASK_ERROR( + binary:bin_to_list(<<1,2,3>>,{1.0,1})), + ?line badarg = + ?MASK_ERROR( + binary:bin_to_list(<<1,2,3>>,{1,1.0})), + ?line badarg = + ?MASK_ERROR( + binary:bin_to_list(<<1,2,3:3>>,{1,1})), + ?line badarg = + ?MASK_ERROR( + binary:bin_to_list(<<1,2,3:3>>)), + ?line badarg = + ?MASK_ERROR( + binary:bin_to_list([1,2,3])), + + ?line nomatch = + ?MASK_ERROR(binary:match(<<1,2,3>>,<<1>>,[{scope,{0,0}}])), + ?line badarg = + ?MASK_ERROR(binary:match(<<1,2,3>>,{bm,<<>>},[{scope,{0,1}}])), + ?line badarg = + ?MASK_ERROR(binary:match(<<1,2,3>>,[],[{scope,{0,1}}])), + ?line badarg = + ?MASK_ERROR(binary:match(<<1,2,3>>,{ac,<<>>},[{scope,{0,1}}])), + ?line {bm,BMMagic} = binary:compile_pattern([<<1,2,3>>]), + ?line {ac,ACMagic} = binary:compile_pattern([<<1,2,3>>,<<4,5>>]), + ?line badarg = + ?MASK_ERROR(binary:match(<<1,2,3>>,{bm,ACMagic},[{scope,{0,1}}])), + ?line badarg = + ?MASK_ERROR(binary:match(<<1,2,3>>,{ac,BMMagic},[{scope,{0,1}}])), + ?line badarg = + ?MASK_ERROR( + binary:match(<<1,2,3>>, + {bm,ets:match_spec_compile([{'_',[],['$_']}])}, + [{scope,{0,1}}])), + ?line badarg = + ?MASK_ERROR( + binary:match(<<1,2,3>>, + {ac,ets:match_spec_compile([{'_',[],['$_']}])}, + [{scope,{0,1}}])), + ?line [] = + ?MASK_ERROR(binary:matches(<<1,2,3>>,<<1>>,[{scope,{0,0}}])), + ?line badarg = + ?MASK_ERROR(binary:matches(<<1,2,3>>,{bm,<<>>},[{scope,{0,1}}])), + ?line badarg = + ?MASK_ERROR(binary:matches(<<1,2,3>>,[],[{scope,{0,1}}])), + ?line badarg = + ?MASK_ERROR(binary:matches(<<1,2,3>>,{ac,<<>>},[{scope,{0,1}}])), + ?line badarg = + ?MASK_ERROR(binary:matches(<<1,2,3>>,{bm,ACMagic},[{scope,{0,1}}])), + ?line badarg = + ?MASK_ERROR(binary:matches(<<1,2,3>>,{ac,BMMagic},[{scope,{0,1}}])), + ?line badarg = + ?MASK_ERROR( + binary:matches(<<1,2,3>>, + {bm,ets:match_spec_compile([{'_',[],['$_']}])}, + [{scope,{0,1}}])), + ?line badarg = + ?MASK_ERROR( + binary:matches(<<1,2,3>>, + {ac,ets:match_spec_compile([{'_',[],['$_']}])}, + [{scope,{0,1}}])), + ?line badarg = + ?MASK_ERROR(binary:longest_common_prefix( + [<<0:10000,1,2,4,1:3>>, + <<0:10000,1,2,3>>])), + ?line badarg = + ?MASK_ERROR(binary:longest_common_suffix( + [<<0:10000,1,2,4,1:3>>, + <<0:10000,1,2,3>>])), + ?line badarg = + ?MASK_ERROR(binary:encode_unsigned(-1)), + ?line badarg = + ?MASK_ERROR( + binary:encode_unsigned(-16#FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), + ?line badarg = + ?MASK_ERROR( + binary:first(<<1,2,4,1:3>>)), + ?line badarg = + ?MASK_ERROR( + binary:first([1,2,4])), + ?line badarg = + ?MASK_ERROR( + binary:last(<<1,2,4,1:3>>)), + ?line badarg = + ?MASK_ERROR( + binary:last([1,2,4])), + ?line badarg = + ?MASK_ERROR( + binary:at(<<1,2,4,1:3>>,2)), + ?line badarg = + ?MASK_ERROR( + binary:at(<<>>,2)), + ?line badarg = + ?MASK_ERROR( + binary:at([1,2,4],2)), + ok. + +longest_common_trap(doc) -> + ["Whitebox test to force special trap conditions in longest_common_{prefix,suffix}"]; +longest_common_trap(Config) when is_list(Config) -> + ?line erts_debug:set_internal_state(available_internal_state,true), + ?line io:format("oldlimit: ~p~n", + [erts_debug:set_internal_state(binary_loop_limit,10)]), + erlang:bump_reductions(10000000), + ?line _ = binary:longest_common_prefix( + [<<0:10000,1,2,4>>, + <<0:10000,1,2,3>>, + <<0:10000,1,3,3>>, + <<0:10000,1,2,4>>, + <<0:10000,1,2,4>>, + <<0:10000,1,2,3>>, + <<0:10000,1,3,3>>, + <<0:10000,1,2,3>>, + <<0:10000,1,3,3>>, + <<0:10000,1,2,4>>, + <<0:10000,1,2,4>>, + <<0:10000,1,2,3>>, + <<0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0:10000,1,3,3>>, + <<0:10000,1,2,4>>]), + ?line _ = binary:longest_common_prefix( + [<<0:10000,1,2,4>>, + <<0:10000,1,2,3>>, + <<0:10000,1,3,3>>, + <<0:10000,1,2,4>>, + <<0:10000,1,2,4>>, + <<0:10000,1,2,3>>, + <<0:10000,1,3,3>>, + <<0:10000,1,2,3>>, + <<0:10000,1,3,3>>, + <<0:10000,1,2,4>>, + <<0:10000,1,2,4>>, + <<0:10000,1,2,3>>, + <<0,0,0,0,0,0,0,0,0,0,0,0,0,0>>, + <<0:10000,1,2,4>>]), + erlang:bump_reductions(10000000), + ?line _ = binary:longest_common_suffix( + [<<1,2,4,0:10000>>, + <<1,2,4,0:10000>>, + <<1,2,4,0:10000>>, + <<1,2,4,0:10000>>, + <<1,2,4,0:10000>>, + <<1,2,4,0:10000>>, + <<1,2,4,0:10000>>, + <<1,2,4,0:10000>>, + <<1,2,4,0:10000>>, + <<1,2,4,0:10000>>, + <<1,2,4,0:10000>>, + <<1,2,4,0:10000>>, + <<1,3,3,0:10000,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0>>, + <<1,2,4,0:10000>>]), + ?line _ = binary:longest_common_suffix( + [<<1,2,4,0:10000>>, + <<1,2,4,0:10000>>, + <<1,2,4,0:10000>>, + <<1,2,4,0:10000>>, + <<1,2,4,0:10000>>, + <<1,2,4,0:10000>>, + <<1,2,4,0:10000>>, + <<1,2,4,0:10000>>, + <<1,2,4,0:10000>>, + <<1,2,4,0:10000>>, + <<1,2,4,0:10000>>, + <<1,2,4,0:10000>>, + <<0,0,0,0,0,0,0,0,0,0,0,0,0,0>>, + <<1,2,4,0:10000>>]), + Subj = subj(), + Len = byte_size(Subj), + ?line Len = binary:longest_common_suffix( + [Subj,Subj,Subj]), + ?line io:format("limit was: ~p~n", + [erts_debug:set_internal_state(binary_loop_limit, + default)]), + ?line erts_debug:set_internal_state(available_internal_state,false), + ok. + +subj() -> + Me = self(), + spawn(fun() -> + X0 = iolist_to_binary([ + "1234567890", + %lists:seq(16#21, 16#7e), + lists:duplicate(100, $x) + ]), + Me ! X0, + receive X -> X end + end), + X0 = receive A -> A end, + <<X1:32/binary,_/binary>> = X0, + Subject= <<X1/binary>>, + Subject. + + +interesting(doc) -> + ["Try some interesting patterns"]; +interesting(Config) when is_list(Config) -> + X = do_interesting(binary), + X = do_interesting(binref). + +do_interesting(Module) -> + ?line {0,4} = Module:match(<<"123456">>, + Module:compile_pattern([<<"12">>,<<"1234">>, + <<"23">>,<<"3">>, + <<"34">>,<<"456">>, + <<"45">>,<<"6">>])), + ?line [{0,4},{5,1}] = Module:matches(<<"123456">>, + Module:compile_pattern([<<"12">>,<<"1234">>, + <<"23">>,<<"3">>, + <<"34">>,<<"456">>, + <<"45">>,<<"6">>])), + ?line [{0,4}] = Module:matches(<<"123456">>, + Module:compile_pattern([<<"12">>,<<"1234">>, + <<"23">>,<<"3">>, + <<"34">>,<<"456">>, + <<"45">>])), + ?line [{0,2},{2,2}] = Module:matches(<<"123456">>, + Module:compile_pattern([<<"12">>, + <<"23">>,<<"3">>, + <<"34">>,<<"456">>, + <<"45">>])), + ?line {1,4} = Module:match(<<"123456">>, + Module:compile_pattern([<<"34">>,<<"34">>, + <<"12347">>,<<"2345">>])), + ?line [{1,4}] = Module:matches(<<"123456">>, + Module:compile_pattern([<<"34">>,<<"34">>, + <<"12347">>,<<"2345">>])), + ?line [{2,2}] = Module:matches(<<"123456">>, + Module:compile_pattern([<<"34">>,<<"34">>, + <<"12347">>,<<"2346">>])), + + ?line {0,4} = Module:match(<<"123456">>, + [<<"12">>,<<"1234">>, + <<"23">>,<<"3">>, + <<"34">>,<<"456">>, + <<"45">>,<<"6">>]), + ?line [{0,4},{5,1}] = Module:matches(<<"123456">>, + [<<"12">>,<<"1234">>, + <<"23">>,<<"3">>, + <<"34">>,<<"456">>, + <<"45">>,<<"6">>]), + ?line [{0,4}] = Module:matches(<<"123456">>, + [<<"12">>,<<"1234">>, + <<"23">>,<<"3">>, + <<"34">>,<<"456">>, + <<"45">>]), + ?line [{0,2},{2,2}] = Module:matches(<<"123456">>, + [<<"12">>, + <<"23">>,<<"3">>, + <<"34">>,<<"456">>, + <<"45">>]), + ?line {1,4} = Module:match(<<"123456">>, + [<<"34">>,<<"34">>, + <<"12347">>,<<"2345">>]), + ?line [{1,4}] = Module:matches(<<"123456">>, + [<<"34">>,<<"34">>, + <<"12347">>,<<"2345">>]), + ?line [{2,2}] = Module:matches(<<"123456">>, + [<<"34">>,<<"34">>, + <<"12347">>,<<"2346">>]), + ?line nomatch = Module:match(<<1,2,3,4>>,<<2>>,[{scope,{0,1}}]), + ?line {1,1} = Module:match(<<1,2,3,4>>,<<2>>,[{scope,{0,2}}]), + ?line nomatch = Module:match(<<1,2,3,4>>,<<2,3>>,[{scope,{0,2}}]), + ?line {1,2} = Module:match(<<1,2,3,4>>,<<2,3>>,[{scope,{0,3}}]), + ?line {1,2} = Module:match(<<1,2,3,4>>,<<2,3>>,[{scope,{0,4}}]), + ?line badarg = ?MASK_ERROR(Module:match(<<1,2,3,4>>,<<2,3>>, + [{scope,{0,5}}])), + ?line {1,2} = Module:match(<<1,2,3,4>>,<<2,3>>,[{scope,{4,-4}}]), + ?line {0,3} = Module:match(<<1,2,3,4>>,<<1,2,3>>,[{scope,{4,-4}}]), + ?line {0,4} = Module:match(<<1,2,3,4>>,<<1,2,3,4>>,[{scope,{4,-4}}]), + ?line badarg = ?MASK_ERROR(Module:match(<<1,2,3,4>>,<<1,2,3,4>>, + [{scope,{3,-4}}])), + ?line [] = Module:matches(<<1,2,3,4>>,<<2>>,[{scope,{0,1}}]), + ?line [{1,1}] = Module:matches(<<1,2,3,4>>,[<<2>>,<<3>>],[{scope,{0,2}}]), + ?line [] = Module:matches(<<1,2,3,4>>,<<2,3>>,[{scope,{0,2}}]), + ?line [{1,2}] = Module:matches(<<1,2,3,4>>,<<2,3>>,[{scope,{0,3}}]), + ?line [{1,2}] = Module:matches(<<1,2,3,4>>,<<2,3>>,[{scope,{0,4}}]), + ?line [{1,2}] = Module:matches(<<1,2,3,4>>,[<<2,3>>,<<4>>], + [{scope,{0,3}}]), + ?line [{1,2},{3,1}] = Module:matches(<<1,2,3,4>>,[<<2,3>>,<<4>>], + [{scope,{0,4}}]), + ?line badarg = ?MASK_ERROR(Module:matches(<<1,2,3,4>>,<<2,3>>, + [{scope,{0,5}}])), + ?line [{1,2}] = Module:matches(<<1,2,3,4>>,<<2,3>>,[{scope,{4,-4}}]), + ?line [{1,2},{3,1}] = Module:matches(<<1,2,3,4>>,[<<2,3>>,<<4>>], + [{scope,{4,-4}}]), + ?line [{0,3}] = Module:matches(<<1,2,3,4>>,<<1,2,3>>,[{scope,{4,-4}}]), + ?line [{0,4}] = Module:matches(<<1,2,3,4>>,<<1,2,3,4>>,[{scope,{4,-4}}]), + ?line badarg = ?MASK_ERROR(Module:matches(<<1,2,3,4>>,<<1,2,3,4>>, + [{scope,{3,-4}}])), + ?line badarg = ?MASK_ERROR(Module:matches(<<1,2,3,4>>,[<<1,2,3,4>>], + [{scope,{3,-4}}])), + ?line [<<1,2,3>>,<<6,7,8>>] = Module:split(<<1,2,3,4,5,6,7,8>>,<<4,5>>), + ?line [<<1,2,3>>,<<6,7,8>>] = Module:split(<<1,2,3,4,5,6,7,8>>, + [<<4,5>>,<<7>>]), + ?line [<<1,2,3>>,<<6>>,<<8>>] = Module:split(<<1,2,3,4,5,6,7,8>>, + [<<4,5>>,<<7>>],[global]), + ?line [<<1,2,3>>,<<6>>,<<>>,<<>>] = Module:split(<<1,2,3,4,5,6,7,8>>, + [<<4,5>>,<<7>>,<<8>>], + [global]), + ?line [<<1,2,3>>,<<6>>] = Module:split(<<1,2,3,4,5,6,7,8>>, + [<<4,5>>,<<7>>,<<8>>], + [global,trim]), + ?line [<<1,2,3,4,5,6,7,8>>] = Module:split(<<1,2,3,4,5,6,7,8>>, + [<<4,5>>,<<7>>,<<8>>], + [global,trim,{scope,{0,4}}]), + ?line [<<1,2,3>>,<<6,7,8>>] = Module:split(<<1,2,3,4,5,6,7,8>>, + [<<4,5>>,<<7>>,<<8>>], + [global,trim,{scope,{0,5}}]), + ?line badarg = ?MASK_ERROR( + Module:replace(<<1,2,3,4,5,6,7,8>>, + [<<4,5>>,<<7>>,<<8>>],<<99>>, + [global,trim,{scope,{0,5}}])), + ?line <<1,2,3,99,6,7,8>> = Module:replace(<<1,2,3,4,5,6,7,8>>, + [<<4,5>>,<<7>>,<<8>>],<<99>>,[]), + ?line <<1,2,3,99,6,99,99>> = Module:replace(<<1,2,3,4,5,6,7,8>>, + [<<4,5>>,<<7>>,<<8>>],<<99>>, + [global]), + ?line <<1,2,3,99,6,7,8>> = Module:replace(<<1,2,3,4,5,6,7,8>>, + [<<4,5>>,<<7>>,<<8>>],<<99>>, + [global,{scope,{0,5}}]), + ?line <<1,2,3,99,6,7,8>> = Module:replace(<<1,2,3,4,5,6,7,8>>, + [<<4,5>>,<<7>>,<<8>>],<<99>>, + [global,{scope,{0,5}}]), + ?line <<1,2,3,99,6,7,8>> = Module:replace(<<1,2,3,4,5,6,7,8>>, + [<<4,5>>,<<7>>,<<8>>],<<99>>, + [global,{scope,{0,5}}]), + ?line badarg = ?MASK_ERROR(Module:replace(<<1,2,3,4,5,6,7,8>>, + [<<4,5>>,<<7>>,<<8>>],<<99>>, + [global,{scope,{0,5}}, + {insert,1}])), + ?line <<1,2,3,99,4,5,6,7,8>> = Module:replace(<<1,2,3,4,5,6,7,8>>, + [<<4,5>>,<<7>>,<<8>>],<<99>>, + [global,{scope,{0,5}}, + {insert_replaced,1}]), + ?line <<1,2,3,9,4,5,9,6,7,8>> = Module:replace(<<1,2,3,4,5,6,7,8>>, + [<<4,5>>,<<7>>,<<8>>], + <<9,9>>, + [global,{scope,{0,5}}, + {insert_replaced,1}]), + ?line badarg = ?MASK_ERROR(Module:replace(<<1,2,3,4,5,6,7,8>>, + [<<4,5>>,<<7>>,<<8>>],<<>>, + [global,{scope,{0,5}}, + {insert_replaced,1}])), + ?line 2 = Module:longest_common_prefix([<<1,2,4>>,<<1,2,3>>]), + ?line 2 = Module:longest_common_prefix([<<1,2,4>>,<<1,2>>]), + ?line 1 = Module:longest_common_prefix([<<1,2,4>>,<<1>>]), + ?line 0 = Module:longest_common_prefix([<<1,2,4>>,<<>>]), + ?line 1 = Module:longest_common_prefix([<<1,2,4>>,<<1,2,3>>,<<1,3,3>>]), + ?line 1 = Module:longest_common_prefix([<<1,2,4>>,<<1,2,3>>,<<1,3,3>>,<<1,2,4>>]), + ?line 1251 = Module:longest_common_prefix([<<0:10000,1,2,4>>, + <<0:10000,1,2,3>>, + <<0:10000,1,3,3>>, + <<0:10000,1,2,4>>]), + ?line 12501 = Module:longest_common_prefix([<<0:100000,1,2,4>>, + <<0:100000,1,2,3>>, + <<0:100000,1,3,3>>, + <<0:100000,1,2,4>>]), + ?line 1251 = Module:longest_common_prefix( + [make_unaligned(<<0:10000,1,2,4>>), + <<0:10000,1,2,3>>, + make_unaligned(<<0:10000,1,3,3>>), + <<0:10000,1,2,4>>]), + ?line 12501 = Module:longest_common_prefix( + [<<0:100000,1,2,4>>, + make_unaligned(<<0:100000,1,2,3>>), + <<0:100000,1,3,3>>, + make_unaligned(<<0:100000,1,2,4>>)]), + ?line 1250001 = Module:longest_common_prefix([<<0:10000000,1,2,4>>, + <<0:10000000,1,2,3>>, + <<0:10000000,1,3,3>>, + <<0:10000000,1,2,4>>]), + if % Too cruel for the reference implementation + Module =:= binary -> + ?line erts_debug:set_internal_state(available_internal_state,true), + ?line io:format("oldlimit: ~p~n", + [erts_debug:set_internal_state( + binary_loop_limit,100)]), + ?line 1250001 = Module:longest_common_prefix( + [<<0:10000000,1,2,4>>, + <<0:10000000,1,2,3>>, + <<0:10000000,1,3,3>>, + <<0:10000000,1,2,4>>]), + ?line io:format("limit was: ~p~n", + [erts_debug:set_internal_state(binary_loop_limit, + default)]), + ?line erts_debug:set_internal_state(available_internal_state, + false); + true -> + ok + end, + ?line 1 = Module:longest_common_suffix([<<0:100000000,1,2,4,5>>, + <<0:100000000,1,2,3,5>>, + <<0:100000000,1,3,3,5>>, + <<0:100000000,1,2,4,5>>]), + ?line 1 = Module:longest_common_suffix([<<1,2,4,5>>, + <<0:100000000,1,2,3,5>>, + <<0:100000000,1,3,3,5>>, + <<0:100000000,1,2,4,5>>]), + ?line 1 = Module:longest_common_suffix([<<1,2,4,5,5>>,<<5,5>>, + <<0:100000000,1,3,3,5,5>>, + <<0:100000000,1,2,4,5>>]), + ?line 0 = Module:longest_common_suffix([<<1,2,4,5,5>>,<<5,5>>, + <<0:100000000,1,3,3,5,5>>, + <<0:100000000,1,2,4>>]), + ?line 2 = Module:longest_common_suffix([<<1,2,4,5,5>>,<<5,5>>, + <<0:100000000,1,3,3,5,5>>, + <<0:100000000,1,2,4,5,5>>]), + ?line 1 = Module:longest_common_suffix([<<1,2,4,5,5>>,<<5>>, + <<0:100000000,1,3,3,5,5>>, + <<0:100000000,1,2,4,5,5>>]), + ?line 0 = Module:longest_common_suffix([<<1,2,4,5,5>>,<<>>, + <<0:100000000,1,3,3,5,5>>, + <<0:100000000,1,2,4,5,5>>]), + ?line 0 = Module:longest_common_suffix([<<>>,<<0:100000000,1,3,3,5,5>>, + <<0:100000000,1,2,4,5,5>>]), + ?line 0 = Module:longest_common_suffix([<<>>,<<0:100000000,1,3,3,5,5>>, + <<0:100000000,1,2,4,5,5>>]), + ?line 2 = Module:longest_common_suffix([<<5,5>>,<<0:100000000,1,3,3,5,5>>, + <<0:100000000,1,2,4,5,5>>]), + ?line 2 = Module:longest_common_suffix([<<5,5>>,<<5,5>>,<<4,5,5>>]), + ?line 2 = Module:longest_common_suffix([<<5,5>>,<<5,5>>,<<5,5>>]), + ?line 3 = Module:longest_common_suffix([<<4,5,5>>,<<4,5,5>>,<<4,5,5>>]), + ?line 0 = Module:longest_common_suffix([<<>>]), + ?line badarg = ?MASK_ERROR(Module:longest_common_suffix([])), + ?line badarg = ?MASK_ERROR(Module:longest_common_suffix([apa])), + ?line badarg = ?MASK_ERROR(Module:longest_common_suffix([[<<>>]])), + ?line badarg = ?MASK_ERROR(Module:longest_common_suffix([[<<0>>, + <<1:9>>]])), + ?line 0 = Module:longest_common_prefix([<<>>]), + ?line badarg = ?MASK_ERROR(Module:longest_common_prefix([])), + ?line badarg = ?MASK_ERROR(Module:longest_common_prefix([apa])), + ?line badarg = ?MASK_ERROR(Module:longest_common_prefix([[<<>>]])), + ?line badarg = ?MASK_ERROR(Module:longest_common_prefix([[<<0>>, + <<1:9>>]])), + + ?line <<1:6,Bin:3/binary,_:2>> = <<1:6,1,2,3,1:2>>, + ?line <<1,2,3>> = Bin, + ?line 1 = Module:first(Bin), + ?line 1 = Module:first(<<1>>), + ?line 1 = Module:first(<<1,2,3>>), + ?line badarg = ?MASK_ERROR(Module:first(<<>>)), + ?line badarg = ?MASK_ERROR(Module:first(apa)), + ?line 3 = Module:last(Bin), + ?line 1 = Module:last(<<1>>), + ?line 3 = Module:last(<<1,2,3>>), + ?line badarg = ?MASK_ERROR(Module:last(<<>>)), + ?line badarg = ?MASK_ERROR(Module:last(apa)), + ?line 1 = Module:at(Bin,0), + ?line 1 = Module:at(<<1>>,0), + ?line 1 = Module:at(<<1,2,3>>,0), + ?line 2 = Module:at(<<1,2,3>>,1), + ?line 3 = Module:at(<<1,2,3>>,2), + ?line badarg = ?MASK_ERROR(Module:at(<<1,2,3>>,3)), + ?line badarg = ?MASK_ERROR(Module:at(<<1,2,3>>,-1)), + ?line badarg = ?MASK_ERROR(Module:at(<<1,2,3>>,apa)), + ?line "hejsan" = [ Module:at(<<"hejsan">>,I) || I <- lists:seq(0,5) ], + + ?line badarg = ?MASK_ERROR(Module:bin_to_list(<<1,2,3>>,3,-4)), + ?line [1,2,3] = ?MASK_ERROR(Module:bin_to_list(<<1,2,3>>,3,-3)), + + ?line badarg = ?MASK_ERROR(Module:decode_unsigned(<<1,2,1:2>>,big)), + ?line badarg = ?MASK_ERROR(Module:decode_unsigned(<<1,2,1:2>>,little)), + ?line badarg = ?MASK_ERROR(Module:decode_unsigned(apa)), + ?line badarg = ?MASK_ERROR(Module:decode_unsigned(125,little)), + ?line 0 = ?MASK_ERROR(Module:decode_unsigned(<<>>,little)), + ?line 0 = ?MASK_ERROR(Module:decode_unsigned(<<>>,big)), + ?line 0 = ?MASK_ERROR(Module:decode_unsigned(<<0>>,little)), + ?line 0 = ?MASK_ERROR(Module:decode_unsigned(<<0>>,big)), + ?line 0 = ?MASK_ERROR(Module:decode_unsigned(make_unaligned(<<0>>), + little)), + ?line 0 = ?MASK_ERROR(Module:decode_unsigned(make_unaligned(<<0>>),big)), + ?line badarg = ?MASK_ERROR(Module:encode_unsigned(apa)), + ?line badarg = ?MASK_ERROR(Module:encode_unsigned(125.3,little)), + ?line badarg = ?MASK_ERROR(Module:encode_unsigned({1},little)), + ?line badarg = ?MASK_ERROR(Module:encode_unsigned([1],little)), + ?line <<0>> = ?MASK_ERROR(Module:encode_unsigned(0,little)), + ?line <<0>> = ?MASK_ERROR(Module:encode_unsigned(0,big)), + ok. + +encode_decode(doc) -> + ["test binary:encode_unsigned/1,2 and binary:decode_unsigned/1,2"]; +encode_decode(Config) when is_list(Config) -> + ?line random:seed({1271,769940,559934}), + ?line ok = encode_decode_loop({1,200},1000), % Need to be long enough + % to create offheap binaries + ok. + +encode_decode_loop(_Range,0) -> + ok; +encode_decode_loop(Range, X) -> + ?line N = random_number(Range), + ?line A = binary:encode_unsigned(N), + ?line B = binary:encode_unsigned(N,big), + ?line C = binref:encode_unsigned(N), + ?line D = binref:encode_unsigned(N,big), + ?line E = binary:encode_unsigned(N,little), + ?line F = binref:encode_unsigned(N,little), + ?line G = binary:decode_unsigned(A), + ?line H = binary:decode_unsigned(A,big), + ?line I = binref:decode_unsigned(A), + ?line J = binary:decode_unsigned(E,little), + ?line K = binref:decode_unsigned(E,little), + ?line L = binary:decode_unsigned(make_unaligned(A)), + ?line M = binary:decode_unsigned(make_unaligned(E),little), + ?line PaddedBig = <<0:48,A/binary>>, + ?line PaddedLittle = <<E/binary,0:48>>, + ?line O = binary:decode_unsigned(PaddedBig), + ?line P = binary:decode_unsigned(make_unaligned(PaddedBig)), + ?line Q = binary:decode_unsigned(PaddedLittle,little), + ?line R = binary:decode_unsigned(make_unaligned(PaddedLittle),little), + ?line S = binref:decode_unsigned(PaddedLittle,little), + ?line T = binref:decode_unsigned(PaddedBig), + case (((A =:= B) and (B =:= C) and (C =:= D)) and + ((E =:= F)) and + ((N =:= G) and (G =:= H) and (H =:= I) and + (I =:= J) and (J =:= K) and (K =:= L) and (L =:= M)) and + ((M =:= O) and (O =:= P) and (P =:= Q) and (Q =:= R) and + (R =:= S) and (S =:= T)))of + true -> + encode_decode_loop(Range,X-1); + _ -> + io:format("Failed to encode/decode ~w~n(Results ~p)~n", + [N,[A,B,C,D,E,F,G,H,I,J,K,L,M,x,O,P,Q,R,S,T]]), + exit(mismatch) + end. + +guard(doc) -> + ["Smoke test of the guard BIFs binary_part/2,3"]; +guard(Config) when is_list(Config) -> + {comment, "Guard tests are run in emulator test suite"}. + +referenced(doc) -> + ["Test refernced_byte_size/1 bif."]; +referenced(Config) when is_list(Config) -> + ?line badarg = ?MASK_ERROR(binary:referenced_byte_size([])), + ?line badarg = ?MASK_ERROR(binary:referenced_byte_size(apa)), + ?line badarg = ?MASK_ERROR(binary:referenced_byte_size({})), + ?line badarg = ?MASK_ERROR(binary:referenced_byte_size(1)), + ?line A = <<1,2,3>>, + ?line B = binary:copy(A,1000), + ?line 3 = binary:referenced_byte_size(A), + ?line 3000 = binary:referenced_byte_size(B), + ?line <<_:8,C:2/binary>> = A, + ?line 3 = binary:referenced_byte_size(C), + ?line 2 = binary:referenced_byte_size(binary:copy(C)), + ?line <<_:7,D:2/binary,_:1>> = A, + ?line 2 = binary:referenced_byte_size(binary:copy(D)), + ?line 3 = binary:referenced_byte_size(D), + ?line <<_:8,E:2/binary,_/binary>> = B, + ?line 3000 = binary:referenced_byte_size(E), + ?line 2 = binary:referenced_byte_size(binary:copy(E)), + ?line <<_:7,F:2/binary,_:1,_/binary>> = B, + ?line 2 = binary:referenced_byte_size(binary:copy(F)), + ?line 3000 = binary:referenced_byte_size(F), + ok. + + + +list_to_bin(doc) -> + ["Test list_to_bin/1 bif"]; +list_to_bin(Config) when is_list(Config) -> + %% Just some smoke_tests first, then go nuts with random cases + ?line badarg = ?MASK_ERROR(binary:list_to_bin({})), + ?line badarg = ?MASK_ERROR(binary:list_to_bin(apa)), + ?line badarg = ?MASK_ERROR(binary:list_to_bin(<<"apa">>)), + F1 = fun(L) -> + ?MASK_ERROR(binref:list_to_bin(L)) + end, + F2 = fun(L) -> + ?MASK_ERROR(binary:list_to_bin(L)) + end, + ?line random_iolist:run(1000,F1,F2), + ok. + +copy(doc) -> + ["Test copy/1,2 bif's"]; +copy(Config) when is_list(Config) -> + ?line <<1,2,3>> = binary:copy(<<1,2,3>>), + ?line RS = random_string({1,10000}), + ?line RS = RS2 = binary:copy(RS), + ?line false = erts_debug:same(RS,RS2), + ?line <<>> = ?MASK_ERROR(binary:copy(<<1,2,3>>,0)), + ?line badarg = ?MASK_ERROR(binary:copy(<<1,2,3:3>>,2)), + ?line badarg = ?MASK_ERROR(binary:copy([],0)), + ?line <<>> = ?MASK_ERROR(binary:copy(<<>>,0)), + ?line badarg = ?MASK_ERROR(binary:copy(<<1,2,3>>,1.0)), + ?line badarg = ?MASK_ERROR(binary:copy(<<1,2,3>>, + 16#FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), + ?line <<>> = binary:copy(<<>>,10000), + ?line random:seed({1271,769940,559934}), + ?line ok = random_copy(3000), + ?line erts_debug:set_internal_state(available_internal_state,true), + ?line io:format("oldlimit: ~p~n", + [erts_debug:set_internal_state(binary_loop_limit,10)]), + ?line Subj = subj(), + ?line XX = binary:copy(Subj,1000), + ?line XX = binref:copy(Subj,1000), + ?line ok = random_copy(1000), + ?line kill_copy_loop(1000), + ?line io:format("limit was: ~p~n", + [erts_debug:set_internal_state(binary_loop_limit, + default)]), + ?line erts_debug:set_internal_state(available_internal_state,false), + ok. + +kill_copy_loop(0) -> + ok; +kill_copy_loop(N) -> + {Pid,Ref} = spawn_monitor(fun() -> + ok = random_copy(1000) + end), + receive + after 10 -> + ok + end, + exit(Pid,kill), + receive + {'DOWN',Ref,process,Pid,_} -> + kill_copy_loop(N-1) + after 1000 -> + exit(did_not_die) + end. + +random_copy(0) -> + ok; +random_copy(N) -> + Str = random_string({0,N}), + Num = random:uniform(N div 10+1), + A = ?MASK_ERROR(binary:copy(Str,Num)), + B = ?MASK_ERROR(binref:copy(Str,Num)), + C = ?MASK_ERROR(binary:copy(make_unaligned(Str),Num)), + case {(A =:= B), (B =:= C)} of + {true,true} -> + random_copy(N-1); + _ -> + io:format("Failed to pick copy ~s ~p times~n", + [Str,Num]), + io:format("A:~p,~nB:~p,~n,C:~p.~n", + [A,B,C]), + exit(mismatch) + end. + +bin_to_list(doc) -> + ["Test bin_to_list/1,2,3 bif's"]; +bin_to_list(Config) when is_list(Config) -> + %% Just some smoke_tests first, then go nuts with random cases + ?line X = <<1,2,3,4,0:1000000,5>>, + ?line Y = make_unaligned(X), + ?line LX = binary:bin_to_list(X), + ?line LX = binary:bin_to_list(X,0,byte_size(X)), + ?line LX = binary:bin_to_list(X,byte_size(X),-byte_size(X)), + ?line LX = binary:bin_to_list(X,{0,byte_size(X)}), + ?line LX = binary:bin_to_list(X,{byte_size(X),-byte_size(X)}), + ?line LY = binary:bin_to_list(Y), + ?line LY = binary:bin_to_list(Y,0,byte_size(Y)), + ?line LY = binary:bin_to_list(Y,byte_size(Y),-byte_size(Y)), + ?line LY = binary:bin_to_list(Y,{0,byte_size(Y)}), + ?line LY = binary:bin_to_list(Y,{byte_size(Y),-byte_size(Y)}), + ?line 1 = hd(LX), + ?line 5 = lists:last(LX), + ?line 1 = hd(LY), + ?line 5 = lists:last(LY), + ?line X = list_to_binary(LY), + ?line Y = list_to_binary(LY), + ?line X = list_to_binary(LY), + ?line [5] = lists:nthtail(byte_size(X)-1,LX), + ?line [0,5] = lists:nthtail(byte_size(X)-2,LX), + ?line [0,5] = lists:nthtail(byte_size(Y)-2,LY), + ?line random:seed({1271,769940,559934}), + ?line ok = random_bin_to_list(5000), + ok. + +random_bin_to_list(0) -> + ok; +random_bin_to_list(N) -> + Str = random_string({1,N}), + Parts0 = random_parts(10,N), + Parts1 = Parts0 ++ [ {X+Y,-Y} || {X,Y} <- Parts0 ], + [ begin + try + true = ?MASK_ERROR(binary:bin_to_list(Str,Z)) =:= + ?MASK_ERROR(binref:bin_to_list(Str,Z)), + true = ?MASK_ERROR(binary:bin_to_list(Str,Z)) =:= + ?MASK_ERROR(binary:bin_to_list(make_unaligned(Str),Z)) + catch + _:_ -> + io:format("Error, Str = <<\"~s\">>.~nZ = ~p.~n", + [Str,Z]), + exit(badresult) + end + end || Z <- Parts1 ], + [ begin + try + true = ?MASK_ERROR(binary:bin_to_list(Str,A,B)) =:= + ?MASK_ERROR(binref:bin_to_list(Str,A,B)), + true = ?MASK_ERROR(binary:bin_to_list(Str,A,B)) =:= + ?MASK_ERROR(binary:bin_to_list(make_unaligned(Str),A,B)) + catch + _:_ -> + io:format("Error, Str = <<\"~s\">>.~nA = ~p.~nB = ~p.~n", + [Str,A,B]), + exit(badresult) + end + end || {A,B} <- Parts1 ], + random_bin_to_list(N-1). + +parts(doc) -> + ["Test the part/2,3 bif's"]; +parts(Config) when is_list(Config) -> + %% Some simple smoke tests to begin with + ?line Simple = <<1,2,3,4,5,6,7,8>>, + ?line <<1,2>> = binary:part(Simple,0,2), + ?line <<1,2>> = binary:part(Simple,{0,2}), + ?line Simple = binary:part(Simple,0,8), + ?line Simple = binary:part(Simple,{0,8}), + ?line badarg = ?MASK_ERROR(binary:part(Simple,0,9)), + ?line badarg = ?MASK_ERROR(binary:part(Simple,{0,9})), + ?line badarg = ?MASK_ERROR(binary:part(Simple,1,8)), + ?line badarg = ?MASK_ERROR(binary:part(Simple,{1,8})), + ?line badarg = ?MASK_ERROR(binary:part(Simple,{3,-4})), + ?line badarg = ?MASK_ERROR(binary:part(Simple,{3.0,1})), + ?line badarg = ?MASK_ERROR( + binary:part(Simple,{16#FFFFFFFFFFFFFFFFFFFFFFFFFFFFF + ,1})), + ?line <<2,3,4,5,6,7,8>> = binary:part(Simple,{1,7}), + ?line <<2,3,4,5,6,7,8>> = binary:part(Simple,{8,-7}), + ?line Simple = binary:part(Simple,{8,-8}), + ?line badarg = ?MASK_ERROR(binary:part(Simple,{1,-8})), + ?line badarg = ?MASK_ERROR(binary:part(Simple,{8,-9})), + ?line badarg = ?MASK_ERROR(binary:part(Simple,{0,-1})), + ?line <<>> = binary:part(Simple,{8,0}), + ?line badarg = ?MASK_ERROR(binary:part(Simple,{9,0})), + ?line badarg = ?MASK_ERROR(binary:part(Simple,{-1,0})), + ?line badarg = ?MASK_ERROR(binary:part(Simple,{7,2})), + ?line <<8>> = binary:part(Simple,{7,1}), + ?line random:seed({1271,769940,559934}), + ?line random_parts(5000), + ok. + + +random_parts(0) -> + ok; +random_parts(N) -> + Str = random_string({1,N}), + Parts0 = random_parts(10,N), + Parts1 = Parts0 ++ [ {X+Y,-Y} || {X,Y} <- Parts0 ], + [ begin + true = ?MASK_ERROR(binary:part(Str,Z)) =:= + ?MASK_ERROR(binref:part(Str,Z)), + true = ?MASK_ERROR(binary:part(Str,Z)) =:= + ?MASK_ERROR(erlang:binary_part(Str,Z)), + true = ?MASK_ERROR(binary:part(Str,Z)) =:= + ?MASK_ERROR(binary:part(make_unaligned(Str),Z)) + end || Z <- Parts1 ], + random_parts(N-1). + +random_parts(0,_) -> + []; +random_parts(X,N) -> + Pos = random:uniform(N), + Len = random:uniform((Pos * 12) div 10), + [{Pos,Len} | random_parts(X-1,N)]. + +random_ref_comp(doc) -> + ["Test pseudorandomly generated cases against reference imlementation"]; +random_ref_comp(Config) when is_list(Config) -> + ?line put(success_counter,0), + ?line random:seed({1271,769940,559934}), + ?line do_random_match_comp(5000,{1,40},{30,1000}), + io:format("Number of successes: ~p~n",[get(success_counter)]), + ?line do_random_match_comp2(5000,{1,40},{30,1000}), + io:format("Number of successes: ~p~n",[get(success_counter)]), + ?line do_random_match_comp3(5000,{1,40},{30,1000}), + io:format("Number of successes: ~p~n",[get(success_counter)]), + ?line do_random_match_comp4(5000,{1,40},{30,1000}), + io:format("Number of successes: ~p~n",[get(success_counter)]), + ?line do_random_matches_comp(5000,{1,40},{30,1000}), + io:format("Number of successes: ~p~n",[get(success_counter)]), + ?line do_random_matches_comp2(5000,{1,40},{30,1000}), + io:format("Number of successes: ~p~n",[get(success_counter)]), + ?line do_random_matches_comp3(5,{1,40},{30,1000}), + ?line erts_debug:set_internal_state(available_internal_state,true), + ?line io:format("oldlimit: ~p~n",[ erts_debug:set_internal_state(binary_loop_limit,100)]), + ?line do_random_match_comp(5000,{1,40},{30,1000}), + ?line do_random_matches_comp3(5,{1,40},{30,1000}), + ?line io:format("limit was: ~p~n",[ erts_debug:set_internal_state(binary_loop_limit,default)]), + ?line erts_debug:set_internal_state(available_internal_state,false), + ok. + +random_ref_sr_comp(doc) -> + ["Test pseudorandomly generated cases against reference imlementation of split and replace"]; +random_ref_sr_comp(Config) when is_list(Config) -> + ?line put(success_counter,0), + ?line random:seed({1271,769940,559934}), + ?line do_random_split_comp(5000,{1,40},{30,1000}), + io:format("Number of successes: ~p~n",[get(success_counter)]), + ?line do_random_replace_comp(5000,{1,40},{30,1000}), + io:format("Number of successes: ~p~n",[get(success_counter)]), + ?line do_random_split_comp2(5000,{1,40},{30,1000}), + io:format("Number of successes: ~p~n",[get(success_counter)]), + ?line do_random_replace_comp2(5000,{1,40},{30,1000}), + io:format("Number of successes: ~p~n",[get(success_counter)]), + ok. +random_ref_fla_comp(doc) -> + ["Test pseudorandomly generated cases against reference imlementation of split and replace"]; +random_ref_fla_comp(Config) when is_list(Config) -> + ?line put(success_counter,0), + ?line random:seed({1271,769940,559934}), + ?line do_random_first_comp(5000,{1,1000}), + ?line do_random_last_comp(5000,{1,1000}), + ?line do_random_at_comp(5000,{1,1000}), + io:format("Number of successes: ~p~n",[get(success_counter)]), + ok. + +do_random_first_comp(0,_) -> + ok; +do_random_first_comp(N,Range) -> + S = random_string(Range), + A = ?MASK_ERROR(binref:first(S)), + B = ?MASK_ERROR(binary:first(S)), + C = ?MASK_ERROR(binary:first(make_unaligned(S))), + case {(A =:= B), (B =:= C)} of + {true,true} -> + do_random_first_comp(N-1,Range); + _ -> + io:format("Failed to pick first of ~s~n", + [S]), + io:format("A:~p,~nB:~p,~n,C:~p.~n", + [A,B,C]), + exit(mismatch) + end. + +do_random_last_comp(0,_) -> + ok; +do_random_last_comp(N,Range) -> + S = random_string(Range), + A = ?MASK_ERROR(binref:last(S)), + B = ?MASK_ERROR(binary:last(S)), + C = ?MASK_ERROR(binary:last(make_unaligned(S))), + case {(A =:= B), (B =:= C)} of + {true,true} -> + do_random_last_comp(N-1,Range); + _ -> + io:format("Failed to pick last of ~s~n", + [S]), + io:format("A:~p,~nB:~p,~n,C:~p.~n", + [A,B,C]), + exit(mismatch) + end. +do_random_at_comp(0,_) -> + ok; +do_random_at_comp(N,{Min,Max}=Range) -> + S = random_string(Range), + XMax = Min + ((Max - Min) * 3) div 4, + Pos = random_length({Min,XMax}), %% some out of range + A = ?MASK_ERROR(binref:at(S,Pos)), + B = ?MASK_ERROR(binary:at(S,Pos)), + C = ?MASK_ERROR(binary:at(make_unaligned(S),Pos)), + if + A =/= badarg -> + put(success_counter,get(success_counter)+1); + true -> + ok + end, + case {(A =:= B), (B =:= C)} of + {true,true} -> + do_random_at_comp(N-1,Range); + _ -> + io:format("Failed to pick last of ~s~n", + [S]), + io:format("A:~p,~nB:~p,~n,C:~p.~n", + [A,B,C]), + exit(mismatch) + end. + +do_random_matches_comp(0,_,_) -> + ok; +do_random_matches_comp(N,NeedleRange,HaystackRange) -> + NumNeedles = element(2,HaystackRange) div element(2,NeedleRange), + Needles = [random_string(NeedleRange) || + _ <- lists:duplicate(NumNeedles,a)], + Haystack = random_string(HaystackRange), + true = do_matches_comp(Needles,Haystack), + do_random_matches_comp(N-1,NeedleRange,HaystackRange). + +do_random_matches_comp2(0,_,_) -> + ok; +do_random_matches_comp2(N,NeedleRange,HaystackRange) -> + NumNeedles = element(2,HaystackRange) div element(2,NeedleRange), + Haystack = random_string(HaystackRange), + Needles = [random_substring(NeedleRange,Haystack) || + _ <- lists:duplicate(NumNeedles,a)], + true = do_matches_comp(Needles,Haystack), + do_random_matches_comp2(N-1,NeedleRange,HaystackRange). + +do_random_matches_comp3(0,_,_) -> + ok; +do_random_matches_comp3(N,NeedleRange,HaystackRange) -> + NumNeedles = element(2,HaystackRange) div element(2,NeedleRange), + Haystack = random_string(HaystackRange), + Needles = [random_substring(NeedleRange,Haystack) || + _ <- lists:duplicate(NumNeedles,a)], + RefRes = binref:matches(Haystack,Needles), + true = do_matches_comp_loop(10000,Needles,Haystack, RefRes), + do_random_matches_comp3(N-1,NeedleRange,HaystackRange). + +do_matches_comp_loop(0,_,_,_) -> + true; +do_matches_comp_loop(N, Needles, Haystack0,RR) -> + DummySize=N*8, + Haystack1 = <<0:DummySize,Haystack0/binary>>, + RR1=[{X+N,Y} || {X,Y} <- RR], + true = do_matches_comp2(Needles,Haystack1,RR1), + Haystack2 = <<Haystack0/binary,Haystack1/binary>>, + RR2 = RR ++ [{X2+N+byte_size(Haystack0),Y2} || {X2,Y2} <- RR], + true = do_matches_comp2(Needles,Haystack2,RR2), + do_matches_comp_loop(N-1, Needles, Haystack0,RR). + + +do_matches_comp2(N,H,A) -> + C = ?MASK_ERROR(binary:matches(H,N)), + case (A =:= C) of + true -> + true; + _ -> + io:format("Failed to match ~p (needle) against ~s (haystack)~n", + [N,H]), + io:format("A:~p,~n,C:~p.~n", + [A,C]), + exit(mismatch) + end. +do_matches_comp(N,H) -> + A = ?MASK_ERROR(binref:matches(H,N)), + B = ?MASK_ERROR(binref:matches(H,binref:compile_pattern(N))), + C = ?MASK_ERROR(binary:matches(H,N)), + D = ?MASK_ERROR(binary:matches(make_unaligned(H), + binary:compile_pattern([make_unaligned2(X) || X <- N]))), + if + A =/= nomatch -> + put(success_counter,get(success_counter)+1); + true -> + ok + end, + case {(A =:= B), (B =:= C),(C =:= D)} of + {true,true,true} -> + true; + _ -> + io:format("Failed to match ~p (needle) against ~s (haystack)~n", + [N,H]), + io:format("A:~p,~nB:~p,~n,C:~p,~n,D:~p.~n", + [A,B,C,D]), + exit(mismatch) + end. + +do_random_match_comp(0,_,_) -> + ok; +do_random_match_comp(N,NeedleRange,HaystackRange) -> + Needle = random_string(NeedleRange), + Haystack = random_string(HaystackRange), + true = do_match_comp(Needle,Haystack), + do_random_match_comp(N-1,NeedleRange,HaystackRange). + +do_random_match_comp2(0,_,_) -> + ok; +do_random_match_comp2(N,NeedleRange,HaystackRange) -> + Haystack = random_string(HaystackRange), + Needle = random_substring(NeedleRange,Haystack), + true = do_match_comp(Needle,Haystack), + do_random_match_comp2(N-1,NeedleRange,HaystackRange). + +do_random_match_comp3(0,_,_) -> + ok; +do_random_match_comp3(N,NeedleRange,HaystackRange) -> + NumNeedles = element(2,HaystackRange) div element(2,NeedleRange), + Haystack = random_string(HaystackRange), + Needles = [random_substring(NeedleRange,Haystack) || + _ <- lists:duplicate(NumNeedles,a)], + true = do_match_comp3(Needles,Haystack), + do_random_match_comp3(N-1,NeedleRange,HaystackRange). + +do_random_match_comp4(0,_,_) -> + ok; +do_random_match_comp4(N,NeedleRange,HaystackRange) -> + NumNeedles = element(2,HaystackRange) div element(2,NeedleRange), + Haystack = random_string(HaystackRange), + Needles = [random_string(NeedleRange) || + _ <- lists:duplicate(NumNeedles,a)], + true = do_match_comp3(Needles,Haystack), + do_random_match_comp4(N-1,NeedleRange,HaystackRange). + +do_match_comp(N,H) -> + A = ?MASK_ERROR(binref:match(H,N)), + B = ?MASK_ERROR(binref:match(H,binref:compile_pattern([N]))), + C = ?MASK_ERROR(binary:match(make_unaligned(H),N)), + D = ?MASK_ERROR(binary:match(H,binary:compile_pattern([N]))), + E = ?MASK_ERROR(binary:match(H,binary:compile_pattern(make_unaligned(N)))), + if + A =/= nomatch -> + put(success_counter,get(success_counter)+1); + true -> + ok + end, + case {(A =:= B), (B =:= C),(C =:= D),(D =:= E)} of + {true,true,true,true} -> + true; + _ -> + io:format("Failed to match ~s (needle) against ~s (haystack)~n", + [N,H]), + io:format("A:~p,~nB:~p,~n,C:~p,~n,D:~p,E:~p.~n", + [A,B,C,D,E]), + exit(mismatch) + end. + +do_match_comp3(N,H) -> + A = ?MASK_ERROR(binref:match(H,N)), + B = ?MASK_ERROR(binref:match(H,binref:compile_pattern(N))), + C = ?MASK_ERROR(binary:match(H,N)), + D = ?MASK_ERROR(binary:match(H,binary:compile_pattern(N))), + if + A =/= nomatch -> + put(success_counter,get(success_counter)+1); + true -> + ok + end, + case {(A =:= B), (B =:= C),(C =:= D)} of + {true,true,true} -> + true; + _ -> + io:format("Failed to match ~s (needle) against ~s (haystack)~n", + [N,H]), + io:format("A:~p,~nB:~p,~n,C:~p,~n,D:~p.~n", + [A,B,C,D]), + exit(mismatch) + end. + +do_random_split_comp(0,_,_) -> + ok; +do_random_split_comp(N,NeedleRange,HaystackRange) -> + Haystack = random_string(HaystackRange), + Needle = random_substring(NeedleRange,Haystack), + true = do_split_comp(Needle,Haystack,[]), + true = do_split_comp(Needle,Haystack,[global]), + true = do_split_comp(Needle,Haystack,[global,trim]), + do_random_split_comp(N-1,NeedleRange,HaystackRange). +do_random_split_comp2(0,_,_) -> + ok; +do_random_split_comp2(N,NeedleRange,HaystackRange) -> + NumNeedles = element(2,HaystackRange) div element(2,NeedleRange), + Haystack = random_string(HaystackRange), + Needles = [random_substring(NeedleRange,Haystack) || + _ <- lists:duplicate(NumNeedles,a)], + true = do_split_comp(Needles,Haystack,[]), + true = do_split_comp(Needles,Haystack,[global]), + do_random_split_comp2(N-1,NeedleRange,HaystackRange). + +do_split_comp(N,H,Opts) -> + A = ?MASK_ERROR(binref:split(H,N,Opts)), + D = ?MASK_ERROR(binary:split(H,binary:compile_pattern(N),Opts)), + if + (A =/= [N]) and is_list(A) -> + put(success_counter,get(success_counter)+1); + true -> + ok + end, + case (A =:= D) of + true -> + true; + _ -> + io:format("Failed to split ~n~p ~n(haystack) with ~n~p ~n(needle) " + "~nand options ~p~n", + [H,N,Opts]), + io:format("A:~p,D:~p.~n", + [A,D]), + exit(mismatch) + end. + +do_random_replace_comp(0,_,_) -> + ok; +do_random_replace_comp(N,NeedleRange,HaystackRange) -> + Haystack = random_string(HaystackRange), + Needle = random_substring(NeedleRange,Haystack), + Repl = random_string(NeedleRange), + Insertat = random_length(NeedleRange), %Sometimes larger than Repl + true = do_replace_comp(Needle,Haystack,Repl,[]), + true = do_replace_comp(Needle,Haystack,Repl,[global]), + true = do_replace_comp(Needle,Haystack,Repl, + [global,{insert_replaced,Insertat}]), + do_random_replace_comp(N-1,NeedleRange,HaystackRange). +do_random_replace_comp2(0,_,_) -> + ok; +do_random_replace_comp2(N,NeedleRange,HaystackRange) -> + NumNeedles = element(2,HaystackRange) div element(2,NeedleRange), + Haystack = random_string(HaystackRange), + Needles = [random_substring(NeedleRange,Haystack) || + _ <- lists:duplicate(NumNeedles,a)], + Repl = random_string(NeedleRange), + Insertat = random_length(NeedleRange), %Sometimes larger than Repl + true = do_replace_comp(Needles,Haystack,Repl,[]), + true = do_replace_comp(Needles,Haystack,Repl,[global]), + true = do_replace_comp(Needles,Haystack,Repl, + [global,{insert_replaced,Insertat}]), + do_random_replace_comp2(N-1,NeedleRange,HaystackRange). + +do_replace_comp(N,H,R,Opts) -> + A = ?MASK_ERROR(binref:replace(H,N,R,Opts)), + D = ?MASK_ERROR(binary:replace(H,binary:compile_pattern(N),R,Opts)), + if + (A =/= N) and is_binary(A) -> + put(success_counter,get(success_counter)+1); + true -> + ok + end, + case (A =:= D) of + true -> + true; + _ -> + io:format("Failed to replace ~s (haystack) by ~s (needle) " + "inserting ~s (replacement) and options ~p~n", + [H,N,R,Opts]), + io:format("A:~p,D:~p.~n", + [A,D]), + exit(mismatch) + end. + +one_random_number(N) -> + M = ((N - 1) rem 10) + 1, + element(M,{$0,$1,$2,$3,$4,$5,$6,$7,$8,$9}). + +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}). + +random_number({Min,Max}) -> % Min and Max are *length* of number in + % decimal positions + X = random:uniform(Max - Min + 1) + Min - 1, + list_to_integer([one_random_number(random:uniform(10)) || _ <- lists:seq(1,X)]). + + +random_length({Min,Max}) -> + random:uniform(Max - Min + 1) + Min - 1. +random_string({Min,Max}) -> + X = random:uniform(Max - Min + 1) + Min - 1, + list_to_binary([one_random(random:uniform(68)) || _ <- lists:seq(1,X)]). +random_substring({Min,Max},Hay) -> + X = random:uniform(Max - Min + 1) + Min - 1, + Y = byte_size(Hay), + Z = if + X > Y -> Y; + true -> X + end, + PMax = Y - Z, + Pos = random:uniform(PMax + 1) - 1, + <<_:Pos/binary,Res:Z/binary,_/binary>> = Hay, + Res. + +mask_error({'EXIT',{Err,_}}) -> + Err; +mask_error(Else) -> + Else. + +make_unaligned(Bin0) when is_binary(Bin0) -> + Bin1 = <<0:3,Bin0/binary,31:5>>, + Sz = byte_size(Bin0), + <<0:3,Bin:Sz/binary,31:5>> = id(Bin1), + Bin. +make_unaligned2(Bin0) when is_binary(Bin0) -> + Bin1 = <<31:5,Bin0/binary,0:3>>, + Sz = byte_size(Bin0), + <<31:5,Bin:Sz/binary,0:3>> = id(Bin1), + Bin. + +id(I) -> I. diff --git a/lib/stdlib/test/binref.erl b/lib/stdlib/test/binref.erl new file mode 100644 index 0000000000..6d96736ef3 --- /dev/null +++ b/lib/stdlib/test/binref.erl @@ -0,0 +1,588 @@ +-module(binref). + +-export([compile_pattern/1,match/2,match/3,matches/2,matches/3, + split/2,split/3,replace/3,replace/4,first/1,last/1,at/2, + part/2,part/3,copy/1,copy/2,encode_unsigned/1,encode_unsigned/2, + decode_unsigned/1,decode_unsigned/2,referenced_byte_size/1, + longest_common_prefix/1,longest_common_suffix/1,bin_to_list/1, + bin_to_list/2,bin_to_list/3,list_to_bin/1]). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% compile_pattern, a dummy +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +compile_pattern(Pattern) when is_binary(Pattern) -> + {[Pattern]}; +compile_pattern(Pattern) -> + try + [ true = is_binary(P) || P <- Pattern ], + {Pattern} + catch + _:_ -> + erlang:error(badarg) + end. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% match and matches +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +match(H,N) -> + match(H,N,[]). +match(Haystack,Needle,Options) when is_binary(Needle) -> + match(Haystack,[Needle],Options); +match(Haystack,{Needles},Options) -> + match(Haystack,Needles,Options); +match(Haystack,Needles,Options) -> + try + true = is_binary(Haystack) and is_list(Needles), % badarg, not function_clause + case get_opts_match(Options,nomatch) of + nomatch -> + mloop(Haystack,Needles); + {A,B} when B > 0 -> + <<_:A/binary,SubStack:B/binary,_/binary>> = Haystack, + mloop(SubStack,Needles,A,B+A); + {A,B} when B < 0 -> + Start = A + B, + Len = -B, + <<_:Start/binary,SubStack:Len/binary,_/binary>> = Haystack, + mloop(SubStack,Needles,Start,Len+Start); + _ -> + nomatch + end + catch + _:_ -> + erlang:error(badarg) + end. +matches(H,N) -> + matches(H,N,[]). +matches(Haystack,Needle,Options) when is_binary(Needle) -> + matches(Haystack,[Needle],Options); +matches(Haystack,{Needles},Options) -> + matches(Haystack,Needles,Options); +matches(Haystack,Needles,Options) -> + try + true = is_binary(Haystack) and is_list(Needles), % badarg, not function_clause + case get_opts_match(Options,nomatch) of + nomatch -> + msloop(Haystack,Needles); + {A,B} when B > 0 -> + <<_:A/binary,SubStack:B/binary,_/binary>> = Haystack, + msloop(SubStack,Needles,A,B+A); + {A,B} when B < 0 -> + Start = A + B, + Len = -B, + <<_:Start/binary,SubStack:Len/binary,_/binary>> = Haystack, + msloop(SubStack,Needles,Start,Len+Start); + _ -> + [] + end + catch + _:_ -> + erlang:error(badarg) + end. + +mloop(Haystack,Needles) -> + mloop(Haystack,Needles,0,byte_size(Haystack)). + +mloop(_Haystack,_Needles,N,M) when N >= M -> + nomatch; +mloop(Haystack,Needles,N,M) -> + case mloop2(Haystack,Needles,N,nomatch) of + nomatch -> + % Not found + <<_:8,NewStack/binary>> = Haystack, + mloop(NewStack,Needles,N+1,M); + {N,Len} -> + {N,Len} + end. + +msloop(Haystack,Needles) -> + msloop(Haystack,Needles,0,byte_size(Haystack)). + +msloop(_Haystack,_Needles,N,M) when N >= M -> + []; +msloop(Haystack,Needles,N,M) -> + case mloop2(Haystack,Needles,N,nomatch) of + nomatch -> + % Not found + <<_:8,NewStack/binary>> = Haystack, + msloop(NewStack,Needles,N+1,M); + {N,Len} -> + NewN = N+Len, + if + NewN >= M -> + [{N,Len}]; + true -> + <<_:Len/binary,NewStack/binary>> = Haystack, + [{N,Len} | msloop(NewStack,Needles,NewN,M)] + end + end. + +mloop2(_Haystack,[],_N,Res) -> + Res; +mloop2(Haystack,[Needle|Tail],N,Candidate) -> + NS = byte_size(Needle), + case Haystack of + <<Needle:NS/binary,_/binary>> -> + NewCandidate = case Candidate of + nomatch -> + {N,NS}; + {N,ONS} when ONS < NS -> + {N,NS}; + Better -> + Better + end, + mloop2(Haystack,Tail,N,NewCandidate); + _ -> + mloop2(Haystack,Tail,N,Candidate) + end. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% split +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +split(H,N) -> + split(H,N,[]). +split(Haystack,{Needles},Options) -> + split(Haystack, Needles, Options); +split(Haystack,Needles0,Options) -> + try + Needles = if + is_list(Needles0) -> + Needles0; + is_binary(Needles0) -> + [Needles0]; + true -> + exit(badtype) + end, + {Part,Global,Trim} = get_opts_split(Options,{nomatch,false,false}), + {Start,End,NewStack} = + case Part of + nomatch -> + {0,byte_size(Haystack),Haystack}; + {A,B} when B >= 0 -> + <<_:A/binary,SubStack:B/binary,_/binary>> = Haystack, + {A,A+B,SubStack}; + {A,B} when B < 0 -> + S = A + B, + L = -B, + <<_:S/binary,SubStack:L/binary,_/binary>> = Haystack, + {S,S+L,SubStack} + end, + MList = if + Global -> + msloop(NewStack,Needles,Start,End); + true -> + case mloop(NewStack,Needles,Start,End) of + nomatch -> + []; + X -> + [X] + end + end, + do_split(Haystack,MList,0,Trim) + catch + _:_ -> + erlang:error(badarg) + end. + +do_split(H,[],N,true) when N >= byte_size(H) -> + []; +do_split(H,[],N,_) -> + [part(H,{N,byte_size(H)-N})]; +do_split(H,[{A,B}|T],N,Trim) -> + case part(H,{N,A-N}) of + <<>> -> + Rest = do_split(H,T,A+B,Trim), + case {Trim, Rest} of + {true,[]} -> + []; + _ -> + [<<>> | Rest] + end; + Oth -> + [Oth | do_split(H,T,A+B,Trim)] + end. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% replace +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +replace(H,N,R) -> + replace(H,N,R,[]). +replace(Haystack,{Needles},Replacement,Options) -> + replace(Haystack,Needles,Replacement,Options); + +replace(Haystack,Needles0,Replacement,Options) -> + try + Needles = if + is_list(Needles0) -> + Needles0; + is_binary(Needles0) -> + [Needles0]; + true -> + exit(badtype) + end, + true = is_binary(Replacement), % Make badarg instead of function clause + {Part,Global,Insert} = get_opts_replace(Options,{nomatch,false,[]}), + {Start,End,NewStack} = + case Part of + nomatch -> + {0,byte_size(Haystack),Haystack}; + {A,B} when B >= 0 -> + <<_:A/binary,SubStack:B/binary,_/binary>> = Haystack, + {A,A+B,SubStack}; + {A,B} when B < 0 -> + S = A + B, + L = -B, + <<_:S/binary,SubStack:L/binary,_/binary>> = Haystack, + {S,S+L,SubStack} + end, + MList = if + Global -> + msloop(NewStack,Needles,Start,End); + true -> + case mloop(NewStack,Needles,Start,End) of + nomatch -> + []; + X -> + [X] + end + end, + ReplList = case Insert of + [] -> + Replacement; + Y when is_integer(Y) -> + splitat(Replacement,0,[Y]); + Li when is_list(Li) -> + splitat(Replacement,0,lists:sort(Li)) + end, + erlang:iolist_to_binary(do_replace(Haystack,MList,ReplList,0)) + catch + _:_ -> + erlang:error(badarg) + end. + + +do_replace(H,[],_,N) -> + [part(H,{N,byte_size(H)-N})]; +do_replace(H,[{A,B}|T],Replacement,N) -> + [part(H,{N,A-N}), + if + is_list(Replacement) -> + do_insert(Replacement, part(H,{A,B})); + true -> + Replacement + end + | do_replace(H,T,Replacement,A+B)]. + +do_insert([X],_) -> + [X]; +do_insert([H|T],R) -> + [H,R|do_insert(T,R)]. + +splitat(H,N,[]) -> + [part(H,{N,byte_size(H)-N})]; +splitat(H,N,[I|T]) -> + [part(H,{N,I-N})|splitat(H,I,T)]. + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% first, last and at +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +first(Subject) -> + try + <<A:8,_/binary>> = Subject, + A + catch + _:_ -> + erlang:error(badarg) + end. + +last(Subject) -> + try + N = byte_size(Subject) - 1, + <<_:N/binary,A:8>> = Subject, + A + catch + _:_ -> + erlang:error(badarg) + end. + +at(Subject,X) -> + try + <<_:X/binary,A:8,_/binary>> = Subject, + A + catch + _:_ -> + erlang:error(badarg) + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% bin_to_list +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +bin_to_list(Subject) -> + try + binary_to_list(Subject) + catch + _:_ -> + erlang:error(badarg) + end. + +bin_to_list(Subject,T) -> + try + {A0,B0} = T, + {A,B} = if + B0 < 0 -> + {A0+B0,-B0}; + true -> + {A0,B0} + end, + binary_to_list(Subject,A+1,A+B) + catch + _:_ -> + erlang:error(badarg) + end. + +bin_to_list(Subject,A,B) -> + try + bin_to_list(Subject,{A,B}) + catch + _:_ -> + erlang:error(badarg) + end. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% list_to_bin +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +list_to_bin(List) -> + try + erlang:list_to_binary(List) + catch + _:_ -> + erlang:error(badarg) + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% longest_common_prefix +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +longest_common_prefix(LB) -> + try + true = is_list(LB) and (length(LB) > 0), % Make badarg instead of function clause + do_longest_common_prefix(LB,0) + catch + _:_ -> + erlang:error(badarg) + end. + +do_longest_common_prefix(LB,X) -> + case do_lcp(LB,X,no) of + true -> + do_longest_common_prefix(LB,X+1); + false -> + X + end. +do_lcp([],_,_) -> + true; +do_lcp([Bin|_],X,_) when byte_size(Bin) =< X -> + false; +do_lcp([Bin|T],X,no) -> + Ch = at(Bin,X), + do_lcp(T,X,Ch); +do_lcp([Bin|T],X,Ch) -> + Ch2 = at(Bin,X), + if + Ch =:= Ch2 -> + do_lcp(T,X,Ch); + true -> + false + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% longest_common_suffix +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +longest_common_suffix(LB) -> + try + true = is_list(LB) and (length(LB) > 0), % Make badarg instead of function clause + do_longest_common_suffix(LB,0) + catch + _:_ -> + erlang:error(badarg) + end. + +do_longest_common_suffix(LB,X) -> + case do_lcs(LB,X,no) of + true -> + do_longest_common_suffix(LB,X+1); + false -> + X + end. +do_lcs([],_,_) -> + true; +do_lcs([Bin|_],X,_) when byte_size(Bin) =< X -> + false; +do_lcs([Bin|T],X,no) -> + Ch = at(Bin,byte_size(Bin) - 1 - X), + do_lcs(T,X,Ch); +do_lcs([Bin|T],X,Ch) -> + Ch2 = at(Bin,byte_size(Bin) - 1 - X), + if + Ch =:= Ch2 -> + do_lcs(T,X,Ch); + true -> + false + end. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% part +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +part(Subject,Part) -> + try + do_part(Subject,Part) + catch + _:_ -> + erlang:error(badarg) + end. + +part(Subject,Pos,Len) -> + part(Subject,{Pos,Len}). + +do_part(Bin,{A,B}) when B >= 0 -> + <<_:A/binary,Sub:B/binary,_/binary>> = Bin, + Sub; +do_part(Bin,{A,B}) when B < 0 -> + S = A + B, + L = -B, + <<_:S/binary,Sub:L/binary,_/binary>> = Bin, + Sub. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% copy +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +copy(Subject) -> + copy(Subject,1). +copy(Subject,N) -> + try + true = is_integer(N) and (N >= 0) and is_binary(Subject), % Badarg, not function clause + erlang:list_to_binary(lists:duplicate(N,Subject)) + catch + _:_ -> + erlang:error(badarg) + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% encode_unsigned +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +encode_unsigned(Unsigned) -> + encode_unsigned(Unsigned,big). +encode_unsigned(Unsigned,Endian) -> + try + true = is_integer(Unsigned) and (Unsigned >= 0), + if + Unsigned =:= 0 -> + <<0>>; + true -> + case Endian of + big -> + list_to_binary(do_encode(Unsigned,[])); + little -> + list_to_binary(do_encode_r(Unsigned)) + end + end + catch + _:_ -> + erlang:error(badarg) + end. + +do_encode(0,L) -> + L; +do_encode(N,L) -> + Byte = N band 255, + NewN = N bsr 8, + do_encode(NewN,[Byte|L]). + +do_encode_r(0) -> + []; +do_encode_r(N) -> + Byte = N band 255, + NewN = N bsr 8, + [Byte|do_encode_r(NewN)]. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% decode_unsigned +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +decode_unsigned(Subject) -> + decode_unsigned(Subject,big). + +decode_unsigned(Subject,Endian) -> + try + true = is_binary(Subject), + case Endian of + big -> + do_decode(Subject,0); + little -> + do_decode_r(Subject,0) + end + catch + _:_ -> + erlang:error(badarg) + end. + +do_decode(<<>>,N) -> + N; +do_decode(<<X:8,Bin/binary>>,N) -> + do_decode(Bin,(N bsl 8) bor X). + +do_decode_r(<<>>,N) -> + N; +do_decode_r(Bin,N) -> + Sz = byte_size(Bin) - 1, + <<NewBin:Sz/binary,X>> = Bin, + do_decode_r(NewBin, (N bsl 8) bor X). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% referenced_byte_size cannot +%% be implemented in pure +%% erlang +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +referenced_byte_size(Bin) when is_binary(Bin) -> + erlang:error(not_implemented); +referenced_byte_size(_) -> + erlang:error(badarg). + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Simple helper functions +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% Option "parsing" +get_opts_match([],Part) -> + Part; +get_opts_match([{scope,{A,B}} | T],_Part) -> + get_opts_match(T,{A,B}); +get_opts_match(_,_) -> + throw(badopt). + +get_opts_split([],{Part,Global,Trim}) -> + {Part,Global,Trim}; +get_opts_split([{scope,{A,B}} | T],{_Part,Global,Trim}) -> + get_opts_split(T,{{A,B},Global,Trim}); +get_opts_split([global | T],{Part,_Global,Trim}) -> + get_opts_split(T,{Part,true,Trim}); +get_opts_split([trim | T],{Part,Global,_Trim}) -> + get_opts_split(T,{Part,Global,true}); +get_opts_split(_,_) -> + throw(badopt). + +get_opts_replace([],{Part,Global,Insert}) -> + {Part,Global,Insert}; +get_opts_replace([{scope,{A,B}} | T],{_Part,Global,Insert}) -> + get_opts_replace(T,{{A,B},Global,Insert}); +get_opts_replace([global | T],{Part,_Global,Insert}) -> + get_opts_replace(T,{Part,true,Insert}); +get_opts_replace([{insert_replaced,N} | T],{Part,Global,_Insert}) -> + get_opts_replace(T,{Part,Global,N}); +get_opts_replace(_,_) -> + throw(badopt). diff --git a/lib/stdlib/test/c_SUITE.erl b/lib/stdlib/test/c_SUITE.erl index 2edbc7ab4c..25281365be 100644 --- a/lib/stdlib/test/c_SUITE.erl +++ b/lib/stdlib/test/c_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% Copyright Ericsson AB 1998-2011. 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,18 +17,36 @@ %% %CopyrightEnd% %% -module(c_SUITE). --export([all/1]). +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2]). -export([c_1/1, c_2/1, c_3/1, c_4/1, nc_1/1, nc_2/1, nc_3/1, nc_4/1, memory/1]). --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). -import(c, [c/2, nc/2]). -all(doc) -> ["Test cases for the 'c' module."]; -all(suite) -> +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> [c_1, c_2, c_3, c_4, nc_1, nc_2, nc_3, nc_4, memory]. +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + %%% Write output to a directory other than current directory: c_1(doc) -> diff --git a/lib/stdlib/test/calendar_SUITE.erl b/lib/stdlib/test/calendar_SUITE.erl index 10fb72c1b1..8192d035ca 100644 --- a/lib/stdlib/test/calendar_SUITE.erl +++ b/lib/stdlib/test/calendar_SUITE.erl @@ -18,29 +18,43 @@ %% -module(calendar_SUITE). --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). --export([all/1, +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2, gregorian_days/1, gregorian_seconds/1, day_of_the_week/1, day_of_the_week_calibrate/1, leap_years/1, last_day_of_the_month/1, - local_time_to_universal_time_dst/1]). + local_time_to_universal_time_dst/1, + iso_week_number/1]). -define(START_YEAR, 1947). -define(END_YEAR, 2012). -all(suite) -> [gregorian_days, - gregorian_seconds, - day_of_the_week, - day_of_the_week_calibrate, - leap_years, - last_day_of_the_month, - local_time_to_universal_time_dst]; +suite() -> [{ct_hooks,[ts_install_cth]}]. -all(doc) -> "This is the test suite for calendar.erl". +all() -> + [gregorian_days, gregorian_seconds, day_of_the_week, + day_of_the_week_calibrate, leap_years, + last_day_of_the_month, local_time_to_universal_time_dst, iso_week_number]. + +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. gregorian_days(doc) -> "Tests that date_to_gregorian_days and gregorian_days_to_date " @@ -156,6 +170,15 @@ local_time_to_universal_time_dst_x(Config) when is_list(Config) -> {comment,"Bug in mktime() in this OS"} end. +iso_week_number(doc) -> + "Test the iso week number calculation for all three possibilities." + " When the date falls on the last week of the previous year," + " when the date falls on a week within the given year and finally," + " when the date falls on the first week of the next year."; +iso_week_number(suite) -> + []; +iso_week_number(Config) when is_list(Config) -> + ?line check_iso_week_number(). %% %% LOCAL FUNCTIONS @@ -245,7 +268,12 @@ check_last_day_of_the_month({SYr, SMon}, {EYr, EMon}) when SYr < EYr -> check_last_day_of_the_month(_, _) -> ok. - +%% check_iso_week_number +%% +check_iso_week_number() -> + ?line {2004, 53} = calendar:iso_week_number({2005, 1, 1}), + ?line {2007, 1} = calendar:iso_week_number({2007, 1, 1}), + ?line {2009, 1} = calendar:iso_week_number({2008, 12, 29}). diff --git a/lib/stdlib/test/dets_SUITE.erl b/lib/stdlib/test/dets_SUITE.erl index 760e610e00..698070368f 100644 --- a/lib/stdlib/test/dets_SUITE.erl +++ b/lib/stdlib/test/dets_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. 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,13 +28,15 @@ -define(privdir(_), "./dets_SUITE_priv"). -define(datadir(_), "./dets_SUITE_data"). -else. --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). -define(format(S, A), ok). -define(privdir(Conf), ?config(priv_dir, Conf)). -define(datadir(Conf), ?config(data_dir, Conf)). -endif. --export([all/1, not_run/1, newly_started/1, basic_v8/1, basic_v9/1, +-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, 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, @@ -50,13 +52,14 @@ otp_4208/1, otp_4989/1, many_clients/1, otp_4906/1, otp_5402/1, simultaneous_open/1, insert_new/1, repair_continuation/1, otp_5487/1, otp_6206/1, otp_6359/1, otp_4738/1, otp_7146/1, - otp_8070/1]). + otp_8070/1, otp_8856/1, otp_8898/1, otp_8899/1, otp_8903/1, + otp_8923/1, otp_9282/1]). -export([dets_dirty_loop/0]). -export([histogram/1, sum_histogram/1, ave_histogram/1]). --export([init_per_testcase/2, fin_per_testcase/2]). +-export([init_per_testcase/2, end_per_testcase/2]). %% Internal export. -export([client/2]). @@ -82,35 +85,51 @@ init_per_testcase(_Case, Config) -> Dog=?t:timetrap(?t:minutes(15)), [{watchdog, Dog}|Config]. -fin_per_testcase(_Case, _Config) -> +end_per_testcase(_Case, _Config) -> Dog=?config(watchdog, _Config), test_server:timetrap_cancel(Dog), ok. -all(suite) -> +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> case os:type() of - vxworks -> - [not_run]; + vxworks -> [not_run]; _ -> - {req,[stdlib], - [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]} + [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] end. +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + not_run(suite) -> []; not_run(Conf) when is_list(Conf) -> {comment, "Not runnable VxWorks/NFS"}. @@ -2935,6 +2954,57 @@ ets_init(Tab, N) -> ets:insert(Tab, {N,N}), ets_init(Tab, N - 1). +otp_8898(doc) -> + ["OTP-8898. Truncated Dets file."]; +otp_8898(suite) -> + []; +otp_8898(Config) when is_list(Config) -> + Tab = otp_8898, + ?line FName = filename(Tab, Config), + + Server = self(), + + ?line file:delete(FName), + ?line {ok, _} = dets:open_file(Tab,[{file, FName}]), + ?line [P1,P2,P3] = new_clients(3, Tab), + + Seq = [{P1,[sync]},{P2,[{lookup,1,[]}]},{P3,[{insert,{1,b}}]}], + ?line atomic_requests(Server, Tab, [[]], Seq), + ?line true = get_replies([{P1,ok},{P2,ok},{P3,ok}]), + ?line ok = dets:close(Tab), + ?line {ok, _} = dets:open_file(Tab,[{file, FName}]), + ?line file:delete(FName), + + ok. + +otp_8899(doc) -> + ["OTP-8899. Several clients. Updated Head was ignored."]; +otp_8899(suite) -> + []; +otp_8899(Config) when is_list(Config) -> + Tab = many_clients, + ?line FName = filename(Tab, Config), + + Server = self(), + + ?line file:delete(FName), + ?line {ok, _} = dets:open_file(Tab,[{file, FName},{version,9}]), + ?line [P1,P2,P3,P4] = new_clients(4, Tab), + + MC = [Tab], + Seq6a = [{P1,[{insert,[{used_to_be_skipped_by,match}]}, + {lookup,1,[{1,a}]}]}, + {P2,[{verbose,true,MC}]}, + {P3,[{lookup,1,[{1,a}]}]}, {P4,[{verbose,true,MC}]}], + ?line atomic_requests(Server, Tab, [[{1,a},{2,b},{3,c}]], Seq6a), + ?line true = get_replies([{P1,ok}, {P2,ok}, {P3,ok}, {P4,ok}]), + ?line [{1,a},{2,b},{3,c},{used_to_be_skipped_by,match}] = + lists:sort(dets:match_object(Tab, '_')), + ?line _ = dets:close(Tab), + ?line file:delete(FName), + + ok. + many_clients(doc) -> ["Several clients accessing a table simultaneously."]; many_clients(suite) -> @@ -3071,6 +3141,11 @@ client(S, Tab) -> eval([], _Tab) -> ok; +eval([{verbose,Bool,Expected} | L], Tab) -> + ?line case dets:verbose(Bool) of + Expected -> eval(L, Tab); + Error -> {error, {verbose,Error}} + end; eval([sync | L], Tab) -> ?line case dets:sync(Tab) of ok -> eval(L, Tab); @@ -3701,6 +3776,109 @@ otp_8070(Config) when is_list(Config) -> file:delete(File), ok. +otp_8856(doc) -> + ["OTP-8856. insert_new() bug."]; +otp_8856(suite) -> + []; +otp_8856(Config) when is_list(Config) -> + Tab = otp_8856, + File = filename(Tab, Config), + file:delete(File), + Me = self(), + ?line {ok, _} = dets:open_file(Tab, [{type, bag}, {file, File}]), + spawn(fun()-> Me ! {1, dets:insert(Tab, [])} end), + spawn(fun()-> Me ! {2, dets:insert_new(Tab, [])} end), + ?line ok = dets:close(Tab), + ?line receive {1, ok} -> ok end, + ?line receive {2, true} -> ok end, + file:delete(File), + + ?line {ok, _} = dets:open_file(Tab, [{type, set}, {file, File}]), + spawn(fun() -> dets:delete(Tab, 0) end), + spawn(fun() -> Me ! {3, dets:insert_new(Tab, {0,0})} end), + ?line ok = dets:close(Tab), + ?line receive {3, true} -> ok end, + file:delete(File), + ok. + +otp_8903(doc) -> + ["OTP-8903. bchunk/match/select bug."]; +otp_8903(suite) -> + []; +otp_8903(Config) when is_list(Config) -> + Tab = otp_8903, + File = filename(Tab, Config), + ?line {ok,T} = dets:open_file(bug, [{file,File}]), + ?line ok = dets:insert(T, [{1,a},{2,b},{3,c}]), + ?line dets:safe_fixtable(T, true), + ?line {[_],C1} = dets:match_object(T, '_', 1), + ?line {BC1,_D} = dets:bchunk(T, start), + ?line ok = dets:close(T), + ?line {'EXIT', {badarg, _}} = (catch {foo,dets:match_object(C1)}), + ?line {'EXIT', {badarg, _}} = (catch {foo,dets:bchunk(T, BC1)}), + ?line {ok,T} = dets:open_file(bug, [{file,File}]), + ?line false = dets:info(T, safe_fixed), + ?line {'EXIT', {badarg, _}} = (catch {foo,dets:match_object(C1)}), + ?line {'EXIT', {badarg, _}} = (catch {foo,dets:bchunk(T, BC1)}), + ?line ok = dets:close(T), + file:delete(File), + ok. + +otp_8923(doc) -> + ["OTP-8923. rehash due to lookup after initialization."]; +otp_8923(suite) -> + []; +otp_8923(Config) when is_list(Config) -> + Tab = otp_8923, + File = filename(Tab, Config), + %% Create a file with more than 256 keys: + file:delete(File), + Bin = list_to_binary([ 0 || _ <- lists:seq(1, 400) ]), + BigBin = list_to_binary([ 0 ||_ <- lists:seq(1, 4000)]), + Ets = ets:new(temp, [{keypos,1}]), + ?line [ true = ets:insert(Ets, {C,Bin}) || C <- lists:seq(1, 700) ], + ?line true = ets:insert(Ets, {helper_data,BigBin}), + ?line true = ets:insert(Ets, {prim_btree,BigBin}), + ?line true = ets:insert(Ets, {sec_btree,BigBin}), + %% Note: too few slots; re-hash will take place + ?line {ok, Tab} = dets:open_file(Tab, [{file,File}]), + ?line Tab = ets:to_dets(Ets, Tab), + ?line ok = dets:close(Tab), + ?line true = ets:delete(Ets), + + ?line {ok,Ref} = dets:open_file(File), + ?line [{1,_}] = dets:lookup(Ref, 1), + ?line ok = dets:close(Ref), + + ?line {ok,Ref2} = dets:open_file(File), + ?line [{helper_data,_}] = dets:lookup(Ref2, helper_data), + ?line ok = dets:close(Ref2), + + file:delete(File), + ok. + +otp_9282(doc) -> + ["OTP-9282. The name of a table can be an arbitrary term"]; +otp_9282(suite) -> + []; +otp_9282(Config) when is_list(Config) -> + some_calls(make_ref(), Config), + some_calls({a,typical,name}, Config), + some_calls(fun() -> a_funny_name end, Config), + ok. + +some_calls(Tab, Config) -> + File = filename(ref, Config), + ?line {ok,T} = dets:open_file(Tab, [{file,File}]), + ?line T = Tab, + ?line false = dets:info(T, safe_fixed), + ?line File = dets:info(T, filename), + ?line ok = dets:insert(Tab, [{3,0}]), + ?line [{3,0}] = dets:lookup(Tab, 3), + ?line [{3,0}] = dets:traverse(Tab, fun(X) -> {continue, X} end), + ?line ok = dets:close(T), + file:delete(File). + %% %% Parts common to several test cases %% diff --git a/lib/stdlib/test/dict_SUITE.erl b/lib/stdlib/test/dict_SUITE.erl index 6a90870bda..c46fc47b34 100644 --- a/lib/stdlib/test/dict_SUITE.erl +++ b/lib/stdlib/test/dict_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2009. All Rights Reserved. +%% Copyright Ericsson AB 2008-2011. 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,21 +22,41 @@ -module(dict_SUITE). --export([all/1,init_per_testcase/2,fin_per_testcase/2, +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2, + init_per_testcase/2,end_per_testcase/2, create/1,store/1]). --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). -import(lists, [foldl/3,reverse/1]). -all(suite) -> - [create,store]. +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [create, store]. + +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + init_per_testcase(_Case, Config) -> ?line Dog = ?t:timetrap(?t:minutes(5)), [{watchdog,Dog}|Config]. -fin_per_testcase(_Case, Config) -> +end_per_testcase(_Case, Config) -> Dog = ?config(watchdog, Config), test_server:timetrap_cancel(Dog), ok. diff --git a/lib/stdlib/test/dict_test_lib.erl b/lib/stdlib/test/dict_test_lib.erl index fd15baa5ff..92a75dad89 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-2009. All Rights Reserved. +%% Copyright Ericsson AB 2008-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 diff --git a/lib/stdlib/test/digraph_SUITE.erl b/lib/stdlib/test/digraph_SUITE.erl index 6ef5b1ddef..1d1326d60e 100644 --- a/lib/stdlib/test/digraph_SUITE.erl +++ b/lib/stdlib/test/digraph_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. 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,19 +23,41 @@ -ifdef(STANDALONE). -define(line, put(line, ?LINE), ). -else. --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). -endif. --export([all/1]). +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2]). --export([opts/1, degree/1, path/1, cycle/1, misc/1, vertices/1, - edges/1, data/1, tickets/1, otp_3522/1, otp_3630/1, otp_8066/1]). +-export([opts/1, degree/1, path/1, cycle/1, vertices/1, + edges/1, data/1, otp_3522/1, otp_3630/1, otp_8066/1]). -export([spawn_graph/2]). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -all(suite) -> {req, [stdlib], [opts, degree, path, cycle, misc, tickets]}. +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [opts, degree, path, cycle, {group, misc}, + {group, tickets}]. + +groups() -> + [{misc, [], [vertices, edges, data]}, + {tickets, [], [otp_3522, otp_3630, otp_8066]}]. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -147,7 +169,6 @@ cycle(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -misc(suite) -> [vertices, edges, data]. vertices(doc) -> []; vertices(suite) -> []; @@ -210,7 +231,6 @@ data(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -tickets(suite) -> [otp_3522, otp_3630, otp_8066]. otp_3522(doc) -> []; otp_3522(suite) -> []; diff --git a/lib/stdlib/test/digraph_utils_SUITE.erl b/lib/stdlib/test/digraph_utils_SUITE.erl index d6d477b388..12c486c25f 100644 --- a/lib/stdlib/test/digraph_utils_SUITE.erl +++ b/lib/stdlib/test/digraph_utils_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2009. All Rights Reserved. +%% Copyright Ericsson AB 2000-2011. 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,10 +22,11 @@ -ifdef(debug). -define(line, put(line, ?LINE), ). -else. --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). -endif. --export([all/1]). +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2]). -export([simple/1, loop/1, isolated/1, topsort/1, subgraph/1, condensation/1, tree/1]). @@ -33,8 +34,27 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -all(suite) -> {req, [stdlib], [simple, loop, isolated, topsort, - subgraph, condensation, tree]}. +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [simple, loop, isolated, topsort, subgraph, + condensation, tree]. + +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/lib/stdlib/test/dummy1_h.erl b/lib/stdlib/test/dummy1_h.erl index 4377d774a3..5b503d5984 100644 --- a/lib/stdlib/test/dummy1_h.erl +++ b/lib/stdlib/test/dummy1_h.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1996-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(dummy1_h). @@ -21,7 +21,7 @@ %% Test event handler for gen_event_SUITE.erl -export([init/1, handle_event/2, handle_call/2, handle_info/2, - terminate/2]). + terminate/2, format_status/2]). init(make_error) -> {error, my_error}; @@ -67,4 +67,5 @@ terminate(remove_handler, Parent) -> terminate(_Reason, _State) -> ok. - +format_status(_Opt, [_PDict, _State]) -> + "dummy1_h handler state". diff --git a/lib/stdlib/test/dummy_h.erl b/lib/stdlib/test/dummy_h.erl index 01eb790a75..7546fe78a0 100644 --- a/lib/stdlib/test/dummy_h.erl +++ b/lib/stdlib/test/dummy_h.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-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 diff --git a/lib/stdlib/test/edlin_expand_SUITE.erl b/lib/stdlib/test/edlin_expand_SUITE.erl index 613bfd000e..a0e198ce09 100644 --- a/lib/stdlib/test/edlin_expand_SUITE.erl +++ b/lib/stdlib/test/edlin_expand_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010. All Rights Reserved. +%% Copyright Ericsson AB 2010-2011. 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,13 +17,14 @@ %% %CopyrightEnd% %% -module(edlin_expand_SUITE). --export([all/1]). +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2]). -export([normal/1, quoted_fun/1, quoted_module/1, quoted_both/1]). --export([init_per_testcase/2, fin_per_testcase/2]). +-export([init_per_testcase/2, end_per_testcase/2]). --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). % Default timetrap timeout (set in init_per_testcase). -define(default_timeout, ?t:minutes(1)). @@ -31,16 +32,36 @@ init_per_testcase(_Case, Config) -> ?line Dog = ?t:timetrap(?default_timeout), [{watchdog, Dog} | Config]. -fin_per_testcase(_Case, Config) -> +end_per_testcase(_Case, Config) -> Dog = ?config(watchdog, Config), test_server:timetrap_cancel(Dog), ok. -all(doc) -> - ["Test cases for edlin_expand."]; -all(suite) -> +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> [normal, quoted_fun, quoted_module, quoted_both]. +groups() -> + []. + +init_per_suite(Config) -> + true = code:delete(expand_test), + true = code:delete(expand_test1), + true = code:delete('ExpandTestCaps'), + true = code:delete('ExpandTestCaps1'), + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + normal(doc) -> [""]; normal(suite) -> diff --git a/lib/stdlib/test/epp_SUITE.erl b/lib/stdlib/test/epp_SUITE.erl index 9a3ae0baf5..9b024a5b49 100644 --- a/lib/stdlib/test/epp_SUITE.erl +++ b/lib/stdlib/test/epp_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2010. All Rights Reserved. +%% Copyright Ericsson AB 1998-2011. 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,13 +17,15 @@ %% %CopyrightEnd% -module(epp_SUITE). --export([all/1]). +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2]). --export([rec_1/1, predef_mac/1, - upcase_mac/1, upcase_mac_1/1, upcase_mac_2/1, - variable/1, variable_1/1, otp_4870/1, otp_4871/1, otp_5362/1, +-export([rec_1/1, predef_mac/1, + upcase_mac_1/1, upcase_mac_2/1, + 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_8130/1, overload_mac/1, otp_8388/1, otp_8470/1, otp_8503/1, + otp_8562/1, otp_8665/1, otp_8911/1]). -export([epp_parse_erl_form/2]). @@ -38,13 +40,13 @@ -define(config(A,B),config(A,B)). %% -define(t, test_server). -define(t, io). -config(priv_dir, _) -> +config(priv_dir, _) -> filename:absname("./epp_SUITE_priv"); config(data_dir, _) -> filename:absname("./epp_SUITE_data"). -else. --include("test_server.hrl"). --export([init_per_testcase/2, fin_per_testcase/2]). +-include_lib("test_server/include/test_server.hrl"). +-export([init_per_testcase/2, end_per_testcase/2]). % Default timetrap timeout (set in init_per_testcase). -define(default_timeout, ?t:minutes(1)). @@ -52,18 +54,36 @@ config(data_dir, _) -> init_per_testcase(_, Config) -> ?line Dog = ?t:timetrap(?default_timeout), [{watchdog, Dog} | Config]. -fin_per_testcase(_, Config) -> +end_per_testcase(_, Config) -> Dog = ?config(watchdog, Config), test_server:timetrap_cancel(Dog), ok. -endif. -all(doc) -> - ["Test cases for epp."]; -all(suite) -> - [rec_1, upcase_mac, predef_mac, variable, otp_4870, otp_4871, otp_5362, - pmod, not_circular, skip_header, otp_6277, otp_7702, otp_8130, - overload_mac, otp_8388]. +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [rec_1, {group, upcase_mac}, predef_mac, + {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]. + +groups() -> + [{upcase_mac, [], [upcase_mac_1, upcase_mac_2]}, + {variable, [], [variable_1]}]. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. rec_1(doc) -> ["Recursive macros hang or crash epp (OTP-1398)."]; @@ -126,10 +146,6 @@ check_errors([{error, Info} | Rest]) -> check_errors([_ | Rest]) -> check_errors(Rest). -upcase_mac(doc) -> - ["Check that uppercase macro names are implicitly quoted (OTP-2608)"]; -upcase_mac(suite) -> - [upcase_mac_1, upcase_mac_2]. upcase_mac_1(doc) -> []; @@ -175,10 +191,6 @@ predef_mac(Config) when is_list(Config) -> end, ok. -variable(doc) -> - ["Check variable as first file component of the include directives."]; -variable(suite) -> - [variable_1]. variable_1(doc) -> []; @@ -191,7 +203,7 @@ variable_1(Config) when is_list(Config) -> %% variable_1.erl includes variable_1_include.hrl and %% variable_1_include_dir.hrl. ?line {ok, List} = epp:parse_file(File, [], []), - ?line {value, {attribute,_,a,{value1,value2}}} = + ?line {value, {attribute,_,a,{value1,value2}}} = lists:keysearch(a,3,List), ok. @@ -218,13 +230,13 @@ otp_4871(Config) when is_list(Config) -> %% Testing crash in erl_scan. Unfortunately there currently is %% no known way to crash erl_scan so it is emulated by killing the %% file io server. This assumes lots of things about how - %% the processes are started and how monitors are set up, + %% the processes are started and how monitors are set up, %% so there are some sanity checks before killing. ?line {ok,Epp} = epp:open(File, []), timer:sleep(1), ?line {current_function,{epp,_,_}} = process_info(Epp, current_function), ?line {monitored_by,[Io]} = process_info(Epp, monitored_by), - ?line {current_function,{file_io_server,_,_}} = + ?line {current_function,{file_io_server,_,_}} = process_info(Io, current_function), ?line exit(Io, emulate_crash), timer:sleep(1), @@ -301,7 +313,7 @@ otp_5362(Config) when is_list(Config) -> Back_hrl = [<<" -file(\"">>,File_Back,<<"\", 2). ">>], - + ?line ok = file:write_file(File_Back, Back), ?line ok = file:write_file(File_Back_hrl, list_to_binary(Back_hrl)), @@ -332,7 +344,7 @@ otp_5362(Config) when is_list(Config) -> ?line ok = file:write_file(File_Change, list_to_binary(Change)), - ?line {ok, change_5362, ChangeWarnings} = + ?line {ok, change_5362, ChangeWarnings} = compile:file(File_Change, Copts), ?line true = message_compare( [{File_Change,[{{1002,21},erl_lint,{unused_var,'B'}}]}, @@ -440,9 +452,9 @@ skip_header(Config) when is_list(Config) -> that should be skipped -module(epp_test_skip_header). -export([main/1]). - + main(_) -> ?MODULE. - + ">>), ?line {ok, Fd} = file:open(File, [read]), ?line io:get_line(Fd, ''), @@ -493,9 +505,9 @@ otp_7702(Config) when is_list(Config) -> t() -> ?RECEIVE(foo, bar).">>, ?line ok = file:write_file(File, Contents), - ?line {ok, file_7702, []} = + ?line {ok, file_7702, []} = compile:file(File, [debug_info,return,{outdir,Dir}]), - + BeamFile = filename:join(Dir, "file_7702.beam"), {ok, AC} = beam_lib:chunks(BeamFile, [abstract_code]), @@ -505,7 +517,7 @@ otp_7702(Config) when is_list(Config) -> L end, Forms2 = [erl_lint:modify_line(Form, Fun) || Form <- Forms], - ?line + ?line [{attribute,1,file,_}, _, _, @@ -616,9 +628,9 @@ otp_8130(Config) when is_list(Config) -> "t() -> 14 = (#file_info{size = 14})#file_info.size, ok.\n">>, ok}, - {otp_8130_7, + {otp_8130_7_new, <<"-record(b, {b}).\n" - "-define(A, {{a,#b.b.\n" + "-define(A, {{a,#b.b).\n" "t() -> {{a,2}} = ?A}}, ok.">>, ok}, @@ -636,7 +648,7 @@ otp_8130(Config) when is_list(Config) -> ], ?line [] = run(Config, Ts), - + Cs = [{otp_8130_c1, <<"-define(M1(A), if\n" "A =:= 1 -> B;\n" @@ -680,7 +692,7 @@ otp_8130(Config) when is_list(Config) -> <<"\n-include_lib(\"$apa/foo.hrl\").\n">>, {errors,[{{2,2},epp,{include,lib,"$apa/foo.hrl"}}],[]}}, - + {otp_8130_c9, <<"-define(S, ?S).\n" "t() -> ?S.\n">>, @@ -750,7 +762,14 @@ otp_8130(Config) when is_list(Config) -> {otp_8130_c24, <<"\n-include(\"no such file.erl\").\n">>, - {errors,[{{2,2},epp,{include,file,"no such file.erl"}}],[]}} + {errors,[{{2,2},epp,{include,file,"no such file.erl"}}],[]}}, + + {otp_8130_7, + <<"-record(b, {b}).\n" + "-define(A, {{a,#b.b.\n" + "t() -> {{a,2}} = ?A}}, ok.">>, + {errors,[{{2,20},epp,missing_parenthesis}, + {{3,19},epp,{undefined,'A',none}}],[]}} ], ?line [] = compile(Config, Cs), @@ -767,7 +786,7 @@ otp_8130(Config) when is_list(Config) -> ?line Dir = ?config(priv_dir, Config), ?line File = filename:join(Dir, "otp_8130.erl"), - ?line ok = file:write_file(File, + ?line ok = file:write_file(File, "-module(otp_8130).\n" "-define(a, 3.14).\n" "t() -> ?a.\n"), @@ -780,7 +799,7 @@ otp_8130(Config) when is_list(Config) -> ?line {eof,_} = epp:scan_erl_form(Epp), ?line ['BASE_MODULE','BASE_MODULE_STRING','BEAM','FILE','LINE', 'MACHINE','MODULE','MODULE_STRING',a] = macs(Epp), - ?line epp:close(Epp), + ?line epp:close(Epp), %% escript ModuleStr = "any_name", @@ -807,7 +826,7 @@ otp_8130(Config) when is_list(Config) -> PreDefMacros = [{a,1},a], ?line {error,{redefine,a}} = epp:open(File, [], PreDefMacros) end(), - + ?line {error,enoent} = epp:open("no such file", []), ?line {error,enoent} = epp:parse_file("no such file", [], []), @@ -933,7 +952,7 @@ ifdef(Config) -> <<"\n-if.\n" "-endif.\n">>, {errors,[{{2,2},epp,{'NYI','if'}}],[]}}, - + {define_c7, <<"-ifndef(a).\n" "-elif.\n" @@ -1047,13 +1066,13 @@ overload_mac(Config) when is_list(Config) -> "-undef(A).\n" "t1() -> ?A.\n", "t2() -> ?A(1).">>, - {errors,[{{4,9},epp,{undefined,'A', none}}, - {{5,9},epp,{undefined,'A', 1}}],[]}}, + {errors,[{{4,10},epp,{undefined,'A', none}}, + {{5,10},epp,{undefined,'A', 1}}],[]}}, %% cannot overload predefined macros {overload_mac_c2, <<"-define(MODULE(X), X).">>, - {errors,[{{1,9},epp,{redefine_predef,'MODULE'}}],[]}}, + {errors,[{{1,50},epp,{redefine_predef,'MODULE'}}],[]}}, %% cannot overload macros with same arity {overload_mac_c3, @@ -1120,25 +1139,121 @@ otp_8388(Config) when is_list(Config) -> {macro_1, <<"-define(m(A), A).\n" "t() -> ?m(,).\n">>, - {errors,[{{2,11},epp,{arg_error,m}}],[]}}, + {errors,[{{2,9},epp,{arg_error,m}}],[]}}, {macro_2, <<"-define(m(A), A).\n" "t() -> ?m(a,).\n">>, - {errors,[{{2,12},epp,{arg_error,m}}],[]}}, + {errors,[{{2,9},epp,{arg_error,m}}],[]}}, {macro_3, <<"-define(LINE, a).\n">>, - {errors,[{{1,9},epp,{redefine_predef,'LINE'}}],[]}}, + {errors,[{{1,50},epp,{redefine_predef,'LINE'}}],[]}}, {macro_4, <<"-define(A(B, C, D), {B,C,D}).\n" "t() -> ?A(a,,3).\n">>, - {errors,[{{2,8},epp,{mismatch,'A'}}],[]}}, + {errors,[{{2,9},epp,{mismatch,'A'}}],[]}}, {macro_5, <<"-define(Q, {?F0(), ?F1(,,4)}).\n">>, - {errors,[{{1,24},epp,{arg_error,'F1'}}],[]}} + {errors,[{{1,62},epp,{arg_error,'F1'}}],[]}}, + {macro_6, + <<"-define(FOO(X), ?BAR(X)).\n" + "-define(BAR(X), ?FOO(X)).\n" + "-undef(FOO).\n" + "test() -> ?BAR(1).\n">>, + {errors,[{{4,12},epp,{undefined,'FOO',1}}],[]}} ], ?line [] = compile(Config, Ts), ok. +otp_8470(doc) -> + ["OTP-8470. Bugfix (one request - two replies)."]; +otp_8470(suite) -> + []; +otp_8470(Config) when is_list(Config) -> + Dir = ?config(priv_dir, Config), + C = <<"-file(\"erl_parse.yrl\", 486).\n" + "-file(\"erl_parse.yrl\", 488).\n">>, + ?line File = filename:join(Dir, "otp_8470.erl"), + ?line ok = file:write_file(File, C), + ?line {ok, _List} = epp:parse_file(File, [], []), + file:delete(File), + ?line receive _ -> fail() after 0 -> ok end, + ok. + +otp_8503(doc) -> + ["OTP-8503. Record with no fields is considered typed."]; +otp_8503(suite) -> + []; +otp_8503(Config) when is_list(Config) -> + Dir = ?config(priv_dir, Config), + C = <<"-record(r, {}).">>, + ?line File = filename:join(Dir, "otp_8503.erl"), + ?line ok = file:write_file(File, C), + ?line {ok, List} = epp:parse_file(File, [], []), + ?line [_] = [F || {attribute,_,type,{{record,r},[],[]}}=F <- List], + file:delete(File), + ?line receive _ -> fail() after 0 -> ok end, + ok. + +otp_8562(doc) -> + ["OTP-8503. Record with no fields is considered typed."]; +otp_8562(suite) -> + []; +otp_8562(Config) when is_list(Config) -> + Cs = [{otp_8562, + <<"-define(P(), {a,b}.\n" + "-define(P3, .\n">>, + {errors,[{{1,60},epp,missing_parenthesis}, + {{2,13},epp,missing_parenthesis}], []}} + ], + ?line [] = compile(Config, Cs), + ok. + +otp_8911(doc) -> + ["OTP-8911. -file and file inclusion bug"]; +otp_8911(suite) -> + []; +otp_8911(Config) when is_list(Config) -> + ?line {ok, CWD} = file:get_cwd(), + ?line ok = file:set_cwd(?config(priv_dir, Config)), + + File = "i.erl", + Cont = <<"-module(i). + -compile(export_all). + -file(\"fil1\", 100). + -include(\"i1.erl\"). + t() -> + a. + ">>, + ?line ok = file:write_file(File, Cont), + Incl = <<"-file(\"fil2\", 35). + t1() -> + b. + ">>, + File1 = "i1.erl", + ?line ok = file:write_file(File1, Incl), + + ?line {ok, i} = cover:compile(File), + ?line a = i:t(), + ?line {ok,[{{i,6},1}]} = cover:analyse(i, calls, line), + ?line cover:stop(), + + file:delete(File), + file:delete(File1), + ?line file:set_cwd(CWD), + ok. + +otp_8665(doc) -> + ["OTP-8665. Bugfix premature end."]; +otp_8665(suite) -> + []; +otp_8665(Config) when is_list(Config) -> + Cs = [{otp_8562, + <<"-define(A, a)\n">>, + {errors,[{{1,54},epp,premature_end}],[]}} + ], + ?line [] = compile(Config, Cs), + ok. + check(Config, Tests) -> eval_tests(Config, fun check_test/2, Tests). @@ -1155,7 +1270,7 @@ eval_tests(Config, Fun, Tests) -> case message_compare(E, Return) of true -> BadL; - false -> + false -> ?t:format("~nTest ~p failed. Expected~n ~p~n" "but got~n ~p~n", [N, E, Return]), fail() @@ -1170,9 +1285,9 @@ check_test(Config, Test) -> ?line File = filename:join(PrivDir, Filename), ?line ok = file:write_file(File, Test), ?line case epp:parse_file(File, [PrivDir], []) of - {ok,Forms} -> + {ok,Forms} -> [E || E={error,_} <- Forms]; - {error,Error} -> + {error,Error} -> Error end. @@ -1187,7 +1302,7 @@ compile_test(Config, Test0) -> {ok, Ws} -> warnings(File, Ws); Else -> Else end. - + warnings(File, Ws) -> case lists:append([W || {F, W} <- Ws, F =:= File]) of [] -> []; @@ -1231,7 +1346,7 @@ message_compare(T, T) -> message_compare(T1, T2) -> ln(T1) =:= T2. -%% Replaces locations like {Line,Column} with Line. +%% Replaces locations like {Line,Column} with Line. ln({warnings,L}) -> {warnings,ln0(L)}; ln({errors,EL,WL}) -> diff --git a/lib/stdlib/test/erl_eval_SUITE.erl b/lib/stdlib/test/erl_eval_SUITE.erl index c60a558fa1..0bcf3c5b71 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-2009. All Rights Reserved. +%% Copyright Ericsson AB 1998-2011. 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 +17,8 @@ %% %CopyrightEnd% -module(erl_eval_SUITE). --export([all/1]). +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2]). -export([guard_1/1, guard_2/1, match_pattern/1, @@ -38,7 +39,8 @@ otp_8133/1, funs/1, try_catch/1, - eval_expr_5/1]). + eval_expr_5/1, + zero_width/1]). %% %% Define to run outside of test server @@ -57,26 +59,42 @@ config(priv_dir,_) -> ".". -else. --include("test_server.hrl"). --export([init_per_testcase/2, fin_per_testcase/2]). +-include_lib("test_server/include/test_server.hrl"). +-export([init_per_testcase/2, end_per_testcase/2]). % Default timetrap timeout (set in init_per_testcase). -define(default_timeout, ?t:minutes(1)). init_per_testcase(_Case, Config) -> ?line Dog = ?t:timetrap(?default_timeout), [{watchdog, Dog} | Config]. -fin_per_testcase(_Case, Config) -> +end_per_testcase(_Case, Config) -> Dog = ?config(watchdog, Config), test_server:timetrap_cancel(Dog), ok. -endif. -all(doc) -> - ["Test cases for the 'erl_eval' module."]; -all(suite) -> - [guard_1, guard_2, match_pattern, string_plusplus, 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]. +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [guard_1, guard_2, match_pattern, string_plusplus, + 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]. + +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. guard_1(doc) -> ["(OTP-2405)"]; @@ -553,6 +571,17 @@ otp_5269(Config) when is_list(Config) -> B:A>> <- [<<16:8,19:16>>], <<X:8>> <- [<<B:8>>]].", [19]), + ?line check(fun() -> + (fun (<<A:1/binary, B:8/integer, _C:B/binary>>) -> + case A of + B -> wrong; + _ -> ok + end + end)(<<1,2,3,4>>) end, + "(fun(<<A:1/binary, B:8/integer, _C:B/binary>>) ->" + " case A of B -> wrong; _ -> ok end" + " end)(<<1, 2, 3, 4>>).", + ok), ok. otp_6539(doc) -> @@ -1170,7 +1199,7 @@ local_func(F, As0, Bs0) when is_atom(F) -> lfh_value_extra() -> %% Not documented. - {value, fun(F, As) -> local_func_value(F, As) end, []}. + {value, fun(F, As, a1, a2) -> local_func_value(F, As) end, [a1, a2]}. lfh_value() -> {value, fun(F, As) -> local_func_value(F, As) end}. @@ -1326,6 +1355,14 @@ eval_expr_5(Config) when is_list(Config) -> ok end. +zero_width(Config) when is_list(Config) -> + ?line check(fun() -> + {'EXIT',{badarg,_}} = (catch <<not_a_number:0>>), + ok + end, "begin {'EXIT',{badarg,_}} = (catch <<not_a_number:0>>), " + "ok end.", ok), + ok. + %% Check the string in different contexts: as is; in fun; from compiled code. check(F, String, Result) -> check1(F, String, Result), diff --git a/lib/stdlib/test/erl_eval_helper.erl b/lib/stdlib/test/erl_eval_helper.erl index 7fdbabcb17..6863b40108 100644 --- a/lib/stdlib/test/erl_eval_helper.erl +++ b/lib/stdlib/test/erl_eval_helper.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. +%% 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 diff --git a/lib/stdlib/test/erl_expand_records_SUITE.erl b/lib/stdlib/test/erl_expand_records_SUITE.erl index 1d621c65df..f8c1ad783c 100644 --- a/lib/stdlib/test/erl_expand_records_SUITE.erl +++ b/lib/stdlib/test/erl_expand_records_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. +%% Copyright Ericsson AB 2005-2011. 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,15 +27,17 @@ -define(privdir, "erl_expand_records_SUITE_priv"). -define(t, test_server). -else. --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). -define(privdir, ?config(priv_dir, Config)). -endif. --export([all/1, init_per_testcase/2, fin_per_testcase/2]). +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + 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, init/1, pattern/1, strict/1, update/1, - tickets/1, otp_5915/1, otp_7931/1, otp_5990/1, + otp_5915/1, otp_7931/1, otp_5990/1, otp_7078/1, otp_7101/1]). % Default timetrap timeout (set in init_per_testcase). @@ -45,14 +47,33 @@ init_per_testcase(_Case, Config) -> ?line Dog = ?t:timetrap(?default_timeout), [{watchdog, Dog} | Config]. -fin_per_testcase(_Case, _Config) -> +end_per_testcase(_Case, _Config) -> Dog = ?config(watchdog, _Config), test_server:timetrap_cancel(Dog), ok. -all(suite) -> - [abstract_module, attributes, expr, guard, init, pattern, - strict, update, tickets]. +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [abstract_module, attributes, expr, guard, init, + pattern, strict, update, {group, tickets}]. + +groups() -> + [{tickets, [], + [otp_5915, otp_7931, otp_5990, otp_7078, otp_7101]}]. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + abstract_module(doc) -> "Compile an abstract module."; @@ -399,8 +420,6 @@ update(Config) when is_list(Config) -> ?line run(Config, Ts), ok. -tickets(suite) -> - [otp_5915, otp_7931, otp_5990, otp_7078, otp_7101]. otp_5915(doc) -> "Strict record tests in guards."; diff --git a/lib/stdlib/test/erl_internal_SUITE.erl b/lib/stdlib/test/erl_internal_SUITE.erl index 8f675c94ec..b6b3c004ea 100644 --- a/lib/stdlib/test/erl_internal_SUITE.erl +++ b/lib/stdlib/test/erl_internal_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% Copyright Ericsson AB 1999-2011. 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,15 +18,35 @@ %% -module(erl_internal_SUITE). --export([all/1]). +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2]). -export([behav/1]). --export([init_per_testcase/2, fin_per_testcase/2]). +-export([init_per_testcase/2, end_per_testcase/2]). --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [behav]. + +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. -all(suite) -> [behav]. -define(default_timeout, ?t:minutes(2)). @@ -34,7 +54,7 @@ init_per_testcase(_Case, Config) -> ?line Dog = test_server:timetrap(?default_timeout), [{watchdog, Dog}|Config]. -fin_per_testcase(_Case, Config) -> +end_per_testcase(_Case, Config) -> Dog=?config(watchdog, Config), test_server:timetrap_cancel(Dog), ok. diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl index 8581b496aa..f980d52e4e 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-2010. All Rights Reserved. +%% Copyright Ericsson AB 1999-2011. 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,34 +27,37 @@ -define(privdir, "erl_lint_SUITE_priv"). -define(t, test_server). -else. --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). -define(datadir, ?config(data_dir, Conf)). -define(privdir, ?config(priv_dir, Conf)). -endif. --export([all/1, init_per_testcase/2, fin_per_testcase/2]). - --export([unused_vars_warn/1, - unused_vars_warn_basic/1, - unused_vars_warn_lc/1, - unused_vars_warn_rec/1, - unused_vars_warn_fun/1, - unused_vars_OTP_4858/1, - export_vars_warn/1, - shadow_vars/1, - unused_import/1, - unused_function/1, - unsafe_vars/1,unsafe_vars2/1, - 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, - bif_clash/1, - behaviour_basic/1, behaviour_multiple/1, - otp_7550/1, - otp_8051/1, - format_warn/1, - on_load/1, on_load_successful/1, on_load_failing/1 +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2, + init_per_testcase/2, end_per_testcase/2]). + +-export([ + unused_vars_warn_basic/1, + unused_vars_warn_lc/1, + unused_vars_warn_rec/1, + unused_vars_warn_fun/1, + unused_vars_OTP_4858/1, + export_vars_warn/1, + shadow_vars/1, + unused_import/1, + unused_function/1, + unsafe_vars/1,unsafe_vars2/1, + 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, + 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 ]). % Default timetrap timeout (set in init_per_testcase). @@ -64,24 +67,44 @@ init_per_testcase(_Case, Config) -> ?line Dog = ?t:timetrap(?default_timeout), [{watchdog, Dog} | Config]. -fin_per_testcase(_Case, _Config) -> +end_per_testcase(_Case, _Config) -> Dog = ?config(watchdog, _Config), test_server:timetrap_cancel(Dog), ok. -all(suite) -> - [unused_vars_warn, export_vars_warn, +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [{group, unused_vars_warn}, export_vars_warn, shadow_vars, unused_import, unused_function, - 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, bif_clash, - behaviour_basic, behaviour_multiple, otp_7550, otp_8051, format_warn, - on_load]. + 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, + bif_clash, behaviour_basic, behaviour_multiple, + otp_7550, otp_8051, format_warn, {group, on_load}, + too_many_arguments]. + +groups() -> + [{unused_vars_warn, [], + [unused_vars_warn_basic, unused_vars_warn_lc, + unused_vars_warn_rec, unused_vars_warn_fun, + unused_vars_OTP_4858]}, + {on_load, [], [on_load_successful, on_load_failing]}]. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + -unused_vars_warn(suite) -> - [unused_vars_warn_basic, unused_vars_warn_lc, unused_vars_warn_rec, - unused_vars_warn_fun, unused_vars_OTP_4858]. unused_vars_warn_basic(doc) -> "Warnings for unused variables in some simple cases."; @@ -1784,6 +1807,9 @@ otp_5362(Config) when is_list(Config) -> {15,erl_lint,{undefined_field,ok,nix}}, {16,erl_lint,{field_name_is_variable,ok,'Var'}}]}}, + %% Nowarn_bif_clash has changed behaviour as local functions + %% nowdays supersede auto-imported BIFs, why nowarn_bif_clash in itself generates an error + %% (OTP-8579) /PaN {otp_5362_4, <<"-compile(nowarn_deprecated_function). -compile(nowarn_bif_clash). @@ -1795,9 +1821,8 @@ otp_5362(Config) when is_list(Config) -> warn_deprecated_function, warn_bif_clash]}, {error, - [{5,erl_lint,{call_to_redefined_bif,{spawn,1}}}], - [{3,erl_lint,{redefine_bif,{spawn,1}}}, - {4,erl_lint,{deprecated,{erlang,hash,2},{erlang,phash2,2}, + [{5,erl_lint,{call_to_redefined_old_bif,{spawn,1}}}], + [{4,erl_lint,{deprecated,{erlang,hash,2},{erlang,phash2,2}, "in a future release"}}]}}, {otp_5362_5, @@ -1808,8 +1833,8 @@ otp_5362(Config) when is_list(Config) -> spawn(A). ">>, {[nowarn_unused_function]}, - {warnings, - [{3,erl_lint,{redefine_bif,{spawn,1}}}]}}, + {errors, + [{2,erl_lint,disallowed_nowarn_bif_clash}],[]}}, %% The special nowarn_X are not affected by general warn_X. {otp_5362_6, @@ -1822,8 +1847,8 @@ otp_5362(Config) when is_list(Config) -> {[nowarn_unused_function, warn_deprecated_function, warn_bif_clash]}, - {warnings, - [{3,erl_lint,{redefine_bif,{spawn,1}}}]}}, + {errors, + [{2,erl_lint,disallowed_nowarn_bif_clash}],[]}}, {otp_5362_7, <<"-export([spawn/1]). @@ -1838,7 +1863,9 @@ otp_5362(Config) when is_list(Config) -> spawn(A). ">>, {[nowarn_unused_function]}, - {error,[{4,erl_lint,{bad_nowarn_bif_clash,{spawn,2}}}], + {error,[{3,erl_lint,disallowed_nowarn_bif_clash}, + {4,erl_lint,disallowed_nowarn_bif_clash}, + {4,erl_lint,{bad_nowarn_bif_clash,{spawn,2}}}], [{5,erl_lint,{bad_nowarn_deprecated_function,{3,hash,-1}}}, {5,erl_lint,{bad_nowarn_deprecated_function,{erlang,hash,-1}}}, {5,erl_lint,{bad_nowarn_deprecated_function,{{a,b,c},hash,-1}}}]} @@ -1865,7 +1892,21 @@ otp_5362(Config) when is_list(Config) -> t() -> #a{}. ">>, {[]}, - []} + []}, + + {otp_5362_10, + <<"-compile({nowarn_deprecated_function,{erlang,hash,2}}). + -compile({nowarn_bif_clash,{spawn,1}}). + -import(x,[spawn/1]). + spin(A) -> + erlang:hash(A, 3000), + spawn(A). + ">>, + {[nowarn_unused_function, + warn_deprecated_function, + warn_bif_clash]}, + {errors, + [{2,erl_lint,disallowed_nowarn_bif_clash}],[]}} ], @@ -2234,7 +2275,7 @@ otp_5878(Config) when is_list(Config) -> {15,erl_lint,{undefined_field,r3,q}}, {17,erl_lint,{undefined_field,r,q}}, {21,erl_lint,illegal_guard_expr}, - {23,erl_lint,illegal_guard_expr}], + {23,erl_lint,{illegal_guard_local_call,{l,0}}}], []} = run_test2(Config, Ill1, [warn_unused_record]), @@ -2389,9 +2430,9 @@ bif_clash(Config) when is_list(Config) -> N. ">>, [], - {errors,[{2,erl_lint,{call_to_redefined_bif,{size,1}}}],[]}}, + {errors,[{2,erl_lint,{call_to_redefined_old_bif,{size,1}}}],[]}}, - %% Verify that (some) warnings can be turned off. + %% Verify that warnings can not be turned off in the old way. {clash2, <<"-export([t/1,size/1]). t(X) -> @@ -2400,17 +2441,198 @@ bif_clash(Config) when is_list(Config) -> size({N,_}) -> N. - %% My own abs/1 function works on lists too. - %% Unfortunately, it is not exported, so there will - %% be a warning that can't be turned off. + %% My own abs/1 function works on lists too. From R14 this really works. abs([H|T]) when $a =< H, H =< $z -> [H-($a-$A)|abs(T)]; abs([H|T]) -> [H|abs(T)]; abs([]) -> []; abs(X) -> erlang:abs(X). ">>, - {[nowarn_bif_clash]}, - {warnings,[{11,erl_lint,{redefine_bif,{abs,1}}}, - {11,erl_lint,{unused_function,{abs,1}}}]}}], + {[nowarn_unused_function,nowarn_bif_clash]}, + {errors,[{erl_lint,disallowed_nowarn_bif_clash}],[]}}, + %% As long as noone calls an overridden BIF, it's totally OK + {clash3, + <<"-export([size/1]). + size({N,_}) -> + N; + size(X) -> + erlang:size(X). + ">>, + [], + []}, + %% But this is totally wrong - meaning of the program changed in R14, so this is an error + {clash4, + <<"-export([size/1]). + size({N,_}) -> + N; + size(X) -> + size(X). + ">>, + [], + {errors,[{5,erl_lint,{call_to_redefined_old_bif,{size,1}}}],[]}}, + %% For a post R14 bif, its only a warning + {clash5, + <<"-export([binary_part/2]). + binary_part({B,_},{X,Y}) -> + binary_part(B,{X,Y}); + binary_part(B,{X,Y}) -> + binary:part(B,X,Y). + ">>, + [], + {warnings,[{3,erl_lint,{call_to_redefined_bif,{binary_part,2}}}]}}, + %% If you really mean to call yourself here, you can "unimport" size/1 + {clash6, + <<"-export([size/1]). + -compile({no_auto_import,[size/1]}). + size([]) -> + 0; + size({N,_}) -> + N; + size([_|T]) -> + 1+size(T). + ">>, + [], + []}, + %% Same for the post R14 autoimport warning + {clash7, + <<"-export([binary_part/2]). + -compile({no_auto_import,[binary_part/2]}). + binary_part({B,_},{X,Y}) -> + binary_part(B,{X,Y}); + binary_part(B,{X,Y}) -> + binary:part(B,X,Y). + ">>, + [], + []}, + %% but this doesn't mean the local function is allowed in a guard... + {clash8, + <<"-export([x/1]). + -compile({no_auto_import,[binary_part/2]}). + x(X) when binary_part(X,{1,2}) =:= <<1,2>> -> + hej. + binary_part({B,_},{X,Y}) -> + binary_part(B,{X,Y}); + binary_part(B,{X,Y}) -> + binary:part(B,X,Y). + ">>, + [], + {errors,[{3,erl_lint,{illegal_guard_local_call,{binary_part,2}}}],[]}}, + %% no_auto_import is not like nowarn_bif_clash, it actually removes the autoimport + {clash9, + <<"-export([x/1]). + -compile({no_auto_import,[binary_part/2]}). + x(X) -> + binary_part(X,{1,2}) =:= <<1,2>>. + ">>, + [], + {errors,[{4,erl_lint,{undefined_function,{binary_part,2}}}],[]}}, + %% but we could import it again... + {clash10, + <<"-export([x/1]). + -compile({no_auto_import,[binary_part/2]}). + -import(erlang,[binary_part/2]). + x(X) -> + binary_part(X,{1,2}) =:= <<1,2>>. + ">>, + [], + []}, + %% and actually use it in a guard... + {clash11, + <<"-export([x/1]). + -compile({no_auto_import,[binary_part/2]}). + -import(erlang,[binary_part/2]). + x(X) when binary_part(X,{0,1}) =:= <<0>> -> + binary_part(X,{1,2}) =:= <<1,2>>. + ">>, + [], + []}, + %% but for non-obvious historical reasons, imported functions cannot be used in + %% fun construction without the module name... + {clash12, + <<"-export([x/1]). + -compile({no_auto_import,[binary_part/2]}). + -import(erlang,[binary_part/2]). + x(X) when binary_part(X,{0,1}) =:= <<0>> -> + binary_part(X,{1,2}) =:= fun binary_part/2. + ">>, + [], + {errors,[{5,erl_lint,{undefined_function,{binary_part,2}}}],[]}}, + %% Not from erlang and not from anywhere else + {clash13, + <<"-export([x/1]). + -compile({no_auto_import,[binary_part/2]}). + -import(x,[binary_part/2]). + x(X) -> + binary_part(X,{1,2}) =:= fun binary_part/2. + ">>, + [], + {errors,[{5,erl_lint,{undefined_function,{binary_part,2}}}],[]}}, + %% ...while real auto-import is OK. + {clash14, + <<"-export([x/1]). + x(X) when binary_part(X,{0,1}) =:= <<0>> -> + binary_part(X,{1,2}) =:= fun binary_part/2. + ">>, + [], + []}, + %% Import directive clashing with old bif is an error, regardless of if it's called or not + {clash15, + <<"-export([x/1]). + -import(x,[abs/1]). + x(X) -> + binary_part(X,{1,2}). + ">>, + [], + {errors,[{2,erl_lint,{redefine_old_bif_import,{abs,1}}}],[]}}, + %% For a new BIF, it's only a warning + {clash16, + <<"-export([x/1]). + -import(x,[binary_part/3]). + x(X) -> + abs(X). + ">>, + [], + {warnings,[{2,erl_lint,{redefine_bif_import,{binary_part,3}}}]}}, + %% And, you cannot redefine already imported things that aren't auto-imported + {clash17, + <<"-export([x/1]). + -import(x,[binary_port/3]). + -import(y,[binary_port/3]). + x(X) -> + abs(X). + ">>, + [], + {errors,[{3,erl_lint,{redefine_import,{{binary_port,3},x}}}],[]}}, + %% Not with local functions either + {clash18, + <<"-export([x/1]). + -import(x,[binary_port/3]). + binary_port(A,B,C) -> + binary_part(A,B,C). + x(X) -> + abs(X). + ">>, + [], + {errors,[{3,erl_lint,{define_import,{binary_port,3}}}],[]}}, + %% Like clash8: Dont accept a guard if it's explicitly module-name called either + {clash19, + <<"-export([binary_port/3]). + -compile({no_auto_import,[binary_part/3]}). + -import(x,[binary_part/3]). + binary_port(A,B,C) when x:binary_part(A,B,C) -> + binary_part(A,B,C+1). + ">>, + [], + {errors,[{4,erl_lint,illegal_guard_expr}],[]}}, + %% Not with local functions either + {clash20, + <<"-export([binary_port/3]). + -import(x,[binary_part/3]). + binary_port(A,B,C) -> + binary_part(A,B,C). + ">>, + [warn_unused_import], + {warnings,[{2,erl_lint,{redefine_bif_import,{binary_part,3}}}]}} + ], ?line [] = run(Config, Ts), ok. @@ -2632,8 +2854,6 @@ format_level(Level, Count, Config) -> %% Test the -on_load(Name/0) directive. -on_load(suite) -> - [on_load_successful, on_load_failing]. on_load_successful(Config) when is_list(Config) -> Ts = [{on_load_1, @@ -2714,6 +2934,21 @@ on_load_failing(Config) when is_list(Config) -> ?line [] = run(Config, Ts), ok. +too_many_arguments(doc) -> + "Test that too many arguments is not accepted."; +too_many_arguments(suite) -> []; +too_many_arguments(Config) when is_list(Config) -> + Ts = [{too_many_1, + <<"f(_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_) -> ok.">>, + [], + {errors, + [{1,erl_lint,{too_many_arguments,256}}],[]}} + ], + + ?line [] = 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 0a119d1e38..280c95b1aa 100644 --- a/lib/stdlib/test/erl_pp_SUITE.erl +++ b/lib/stdlib/test/erl_pp_SUITE.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2006-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2006-2011. 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% %% %%%---------------------------------------------------------------- @@ -30,22 +30,25 @@ -define(privdir, "erl_pp_SUITE_priv"). -define(t, test_server). -else. --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). -define(datadir, ?config(data_dir, Config)). -define(privdir, ?config(priv_dir, Config)). -endif. --export([all/1, init_per_testcase/2, fin_per_testcase/2]). +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2, + init_per_testcase/2, end_per_testcase/2]). --export([expr/1, func/1, call/1, recs/1, try_catch/1, if_then/1, - receive_after/1, bits/1, head_tail/1, package/1, - cond1/1, block/1, case1/1, ops/1, messages/1, - old_mnemosyne_syntax/1, - attributes/1, import_export/1, misc_attrs/1, - hook/1, - neg_indent/1, - tickets/1, - otp_6321/1, otp_6911/1, otp_6914/1, otp_8150/1, otp_8238/1]). +-export([ func/1, call/1, recs/1, try_catch/1, if_then/1, + receive_after/1, bits/1, head_tail/1, package/1, + cond1/1, block/1, case1/1, ops/1, messages/1, + old_mnemosyne_syntax/1, + import_export/1, misc_attrs/1, + hook/1, + 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]). %% Internal export. -export([ehook/6]). @@ -57,17 +60,40 @@ init_per_testcase(_Case, Config) -> ?line Dog = ?t:timetrap(?default_timeout), [{watchdog, Dog} | Config]. -fin_per_testcase(_Case, _Config) -> +end_per_testcase(_Case, _Config) -> Dog = ?config(watchdog, _Config), test_server:timetrap_cancel(Dog), ok. -all(suite) -> - [expr, attributes, hook, neg_indent, tickets]. +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [{group, expr}, {group, attributes}, hook, neg_indent, + {group, tickets}]. + +groups() -> + [{expr, [], + [func, call, recs, try_catch, if_then, receive_after, + bits, head_tail, package, 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]}]. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + -expr(suite) -> - [func, call, recs, try_catch, if_then, receive_after, bits, head_tail, - package, cond1, block, case1, ops, messages, old_mnemosyne_syntax]. func(suite) -> []; @@ -149,15 +175,15 @@ recs(Config) when is_list(Config) -> (A#r1.a)#r.a > 3 -> 3 end(#r1{a = #r{a = 4}}), 7 = fun(A) when record(A#r3.a, r1) -> 7 end(#r3{}), - [#r1{a = 2,b = 1}] = + [#r1{a = 2,b = 1}] = fun() -> - [A || A <- [#r1{a = 1, b = 3}, - #r2{a = 2,b = 1}, + [A || A <- [#r1{a = 1, b = 3}, + #r2{a = 2,b = 1}, #r1{a = 2, b = 1}], - A#r1.a > + A#r1.a > A#r1.b] end(), - {[_],b} = + {[_],b} = fun(L) -> %% A is checked only once: R1 = [{A,B} || A <- L, A#r1.a, B <- L, A#r1.b], @@ -176,7 +202,7 @@ recs(Config) when is_list(Config) -> end(#r1{a = 2}), %% The test done twice (an effect of doing the test as soon as possible). - 3 = fun(A) when A#r1.a > 3, + 3 = fun(A) when A#r1.a > 3, record(A, r1) -> 3 end(#r1{a = 5}), @@ -250,18 +276,18 @@ recs(Config) when is_list(Config) -> ok end(), - both = fun(A) when A#r.a, A#r.b -> both + both = fun(A) when A#r.a, A#r.b -> both end(#r{a = true, b = true}), ok = fun() -> - F = fun(A, B) when ((A#r1.a) orelse (B#r2.a)) + F = fun(A, B) when ((A#r1.a) orelse (B#r2.a)) or (B#r2.b) or (A#r1.b) -> true; (_, _) -> false end, - true = F(#r1{a = false, b = false}, + true = F(#r1{a = false, b = false}, #r2{a = false, b = true}), - false = F(#r1{a = true, b = true}, + false = F(#r1{a = true, b = true}, #r1{a = false, b = true}), ok end(), @@ -272,7 +298,7 @@ recs(Config) when is_list(Config) -> <<"-record(r1, {a, b = foo:bar(kljlfjsdlf, kjlksdjf)}). -record(r2, {c = #r1{}, d = #r1{a = bar:foo(kljklsjdf)}}). - t() -> + t() -> R = #r2{}, R#r2{c = R, d = #r1{}}.">>} ], @@ -303,10 +329,10 @@ try_catch(Config) when is_list(Config) -> {try_6, <<"t() -> try 1=2 catch throw:{badmatch,2} -> 3 end.">>}, {try_7, - <<"t() -> try 1=2 of 3 -> 4 + <<"t() -> try 1=2 of 3 -> 4 catch error:{badmatch,2} -> 5 end.">>}, {try_8, - <<"t() -> try 1=2 + <<"t() -> try 1=2 catch error:{badmatch,2} -> 3 after put(try_catch, 4) end.">>}, {try_9, @@ -370,7 +396,7 @@ receive_after(Config) when is_list(Config) -> {X,Y}; Z -> Z - after + after foo:bar() -> {3,4} end.">>} @@ -428,7 +454,7 @@ head_tail(Config) when is_list(Config) -> {list_4, <<"t() -> [a].">>}, {list_5, - <<"t() -> + <<"t() -> [foo:bar(lkjljlskdfj, klsdajflds, sdafkljsdlfkjdas, kjlsdadjl), bar:foo(kljlkjsdf, lkjsdlfj, [kljsfj, sdfdsfsad])].">>} ], @@ -461,7 +487,7 @@ cond1(Config) when is_list(Config) -> " true ->\n" " {x,y}\n" "end" = CChars, -% ?line ok = pp_expr(<<"cond +% ?line ok = pp_expr(<<"cond % {foo,bar} -> % [a,b]; % true -> @@ -543,7 +569,7 @@ old_mnemosyne_syntax(Config) when is_list(Config) -> " X <- table(tab),\n" " X.foo = bar\n" " ]\n" - "end" = + "end" = lists:flatten(erl_pp:expr(Q)), R = {rule,12,sales,2, @@ -558,13 +584,11 @@ old_mnemosyne_syntax(Config) when is_list(Config) -> {atom,14,sales}}]}]}, ?line "sales(E, employee) :-\n" " E <- table(employee),\n" - " E.salary = sales.\n" = + " E.salary = sales.\n" = lists:flatten(erl_pp:form(R)), ok. -attributes(suite) -> - [misc_attrs, import_export]. import_export(suite) -> []; @@ -659,7 +683,7 @@ hook(Config) when is_list(Config) -> ?line "INVALID-FORM:{foo,bar}:" = lists:flatten(erl_pp:expr({foo,bar})), - %% A list (as before R6), not a list of lists. + %% A list (as before R6), not a list of lists. G = [{op,1,'>',{atom,1,a},{foo,{atom,1,b}}}], % not a proper guard GChars = lists:flatten(erl_pp:guard(G, H)), G2 = [{op,1,'>',{atom,1,a}, @@ -676,23 +700,23 @@ hook(Config) when is_list(Config) -> %% Note: no leading spaces before "begin". Block = {block,0,[{match,0,{var,0,'A'},{integer,0,3}}, {atom,0,true}]}, - ?line "begin\n A =" ++ _ = + ?line "begin\n A =" ++ _ = lists:flatten(erl_pp:expr(Block, 17, none)), %% Special... - ?line true = + ?line true = "{some,value}" =:= lists:flatten(erl_pp:expr({value,0,{some,value}})), %% Silly... ?line true = - "if true -> 0 end" =:= + "if true -> 0 end" =:= flat_expr({'if',0,[{clause,0,[],[],[{atom,0,0}]}]}), %% More compatibility: before R6 OldIf = {'if',0,[{clause,0,[],[{atom,0,true}],[{atom,0,b}]}]}, NewIf = {'if',0,[{clause,0,[],[[{atom,0,true}]],[{atom,0,b}]}]}, OldIfChars = lists:flatten(erl_pp:expr(OldIf)), - NewIfChars = lists:flatten(erl_pp:expr(NewIf)), + NewIfChars = lists:flatten(erl_pp:expr(NewIf)), ?line true = OldIfChars =:= NewIfChars, ok. @@ -705,7 +729,7 @@ remove_indentation(S) -> ehook(HE, I, P, H, foo, bar) -> hook(HE, I, P, H). -hook({foo,E}, I, P, H) -> +hook({foo,E}, I, P, H) -> erl_pp:expr({call,0,{atom,0,foo},[E]}, I, P, H). neg_indent(suite) -> @@ -721,14 +745,14 @@ neg_indent(Config) when is_list(Config) -> end">>), ?line ok = pp_expr( <<"fun() -> - F = fun(A, B) when ((A#r1.a) orelse (B#r2.a)) + F = fun(A, B) when ((A#r1.a) orelse (B#r2.a)) or (B#r2.b) or (A#r1.b) -> true; (_, _) -> false end, - true = F(#r1{a = false, b = false}, + true = F(#r1{a = false, b = false}, #r2{a = false, b = true}), - false = F(#r1{a = true, b = true}, + false = F(#r1{a = true, b = true}, #r1{a = false, b = true}), ok end()">>), @@ -762,8 +786,6 @@ neg_indent(Config) when is_list(Config) -> ok. -tickets(suite) -> - [otp_6321, otp_6911, otp_6914, otp_8150, otp_8238]. otp_6321(doc) -> "OTP_6321. Bug fix of exprs()."; @@ -812,7 +834,7 @@ otp_8150(doc) -> "OTP_8150. Types."; otp_8150(suite) -> []; otp_8150(Config) when is_list(Config) -> - ?line _ = [{N,ok} = {N,pp_forms(B)} || + ?line _ = [{N,ok} = {N,pp_forms(B)} || {N,B} <- type_examples() ], ok. @@ -846,7 +868,7 @@ type_examples() -> {ex4,<<"-type t1() :: atom(). ">>}, {ex5,<<"-type t2() :: [t1()]. ">>}, {ex6,<<"-type t3(Atom) :: integer(Atom). ">>}, - {ex7,<<"-type t4() :: t3(foobar). ">>}, + {ex7,<<"-type '\\'t::4'() :: t3('\\'foobar'). ">>}, {ex8,<<"-type t5() :: {t1(), t3(foo)}. ">>}, {ex9,<<"-type t6() :: 1 | 2 | 3 | 'foo' | 'bar'. ">>}, {ex10,<<"-type t7() :: []. ">>}, @@ -882,16 +904,16 @@ type_examples() -> "1|2|3|4|a|b|c|d| " "nonempty_maybe_improper_list(integer, any())]}. ">>}, {ex30,<<"-type t99() ::" - "{t2(),t4(),t5(),t6(),t7(),t8(),t10(),t14()," + "{t2(),'\\'t::4'(),t5(),t6(),t7(),t8(),t10(),t14()," "t15(),t20(),t21(), t22(),t25()}. ">>}, {ex31,<<"-spec t1(FooBar :: t99()) -> t99();" "(t2()) -> t2();" - "(t4()) -> t4() when is_subtype(t4(), t24);" + "('\\'t::4'()) -> '\\'t::4'() when is_subtype('\\'t::4'(), t24);" "(t23()) -> t23() when is_subtype(t23(), atom())," " is_subtype(t23(), t14());" "(t24()) -> t24() when is_subtype(t24(), atom())," " is_subtype(t24(), t14())," - " is_subtype(t24(), t4()).">>}, + " is_subtype(t24(), '\\'t::4'()).">>}, {ex32,<<"-spec mod:t2() -> any(). ">>}, {ex33,<<"-opaque attributes_data() :: " "[{'column', column()} | {'line', info_line()} |" @@ -911,12 +933,146 @@ type_examples() -> "f19 = 3 :: integer()|undefined," "f5 = 3 :: undefined|integer()}). ">>}]. +otp_8473(doc) -> + "OTP_8473. Bugfix abstract type 'fun'."; +otp_8473(suite) -> []; +otp_8473(Config) when is_list(Config) -> + Ex = [{ex1,<<"-type 'fun'(A) :: A.\n" + "-type funkar() :: 'fun'(fun((integer()) -> atom())).\n">>}], + ?line _ = [{N,ok} = {N,pp_forms(B)} || + {N,B} <- Ex], + ok. + +otp_8522(doc) -> + "OTP_8522. Avoid duplicated 'undefined' in record field types."; +otp_8522(suite) -> []; +otp_8522(Config) when is_list(Config) -> + FileName = filename('otp_8522.erl', Config), + C = <<"-module(otp_8522).\n" + "-record(r, {f1 :: undefined,\n" + " f2 :: A :: undefined,\n" + " f3 :: (undefined),\n" + " f4 :: x | y | undefined | z,\n" + " f5 :: a}).\n">>, + ?line ok = file:write_file(FileName, C), + ?line {ok, _} = compile:file(FileName, [{outdir,?privdir},debug_info]), + BF = filename("otp_8522", Config), + ?line {ok, A} = beam_lib:chunks(BF, [abstract_code]), + ?line 5 = count_atom(A, undefined), + ok. + +count_atom(A, A) -> + 1; +count_atom(T, A) when is_tuple(T) -> + count_atom(tuple_to_list(T), A); +count_atom(L, A) when is_list(L) -> + lists:sum([count_atom(T, A) || T <- L]); +count_atom(_, _) -> + 0. + +otp_8567(doc) -> + "OTP_8567. Avoid duplicated 'undefined' in record field types."; +otp_8567(suite) -> []; +otp_8567(Config) when is_list(Config) -> + FileName = filename('otp_8567.erl', Config), + C = <<"-module otp_8567.\n" + "-compile export_all.\n" + "-spec(a).\n" + "-record r, {a}.\n" + "-record s, {a :: integer()}.\n" + "-type t() :: {#r{},#s{}}.\n">>, + ?line ok = file:write_file(FileName, C), + ?line {error,[{_,[{3,erl_parse,["syntax error before: ","')'"]}]}],_} = + compile:file(FileName, [return]), + + F = <<"-module(otp_8567).\n" + "-compile(export_all).\n" + "-record(t, {a}).\n" + "-record(u, {a :: integer()}).\n" + "-opaque ot() :: {#t{}, #u{}}.\n" + "-opaque(ot1() :: atom()).\n" + "-type a() :: integer().\n" + "-spec t() -> a().\n" + "t() ->\n" + " 3.\n" + "\n" + "-spec(t1/1 :: (ot()) -> ot1()).\n" + "t1(A) ->\n" + " A.\n" + "\n" + "-spec(t2 (ot()) -> ot1()).\n" + "t2(A) ->\n" + " A.\n" + "\n" + "-spec(otp_8567:t3/1 :: (ot()) -> ot1()).\n" + "t3(A) ->\n" + " A.\n" + "\n" + "-spec(otp_8567:t4 (ot()) -> ot1()).\n" + "t4(A) ->\n" + " A.\n">>, + ?line ok = pp_forms(F), + + ok. + +otp_8664(doc) -> + "OTP_8664. Types with integer expressions."; +otp_8664(suite) -> []; +otp_8664(Config) when is_list(Config) -> + FileName = filename('otp_8664.erl', Config), + C1 = <<"-module(otp_8664).\n" + "-export([t/0]).\n" + "-define(A, -3).\n" + "-define(B, (?A*(-1 band (((2)))))).\n" + "-type t1() :: ?B | ?A.\n" + "-type t2() :: ?B-1 .. -?B.\n" + "-type t3() :: 9 band (8 - 3) | 1+2 | 5 band 3.\n" + "-type b1() :: <<_:_*(3-(-1))>>\n" + " | <<_:(-(?B))>>\n" + " | <<_:4>>.\n" + "-type u() :: 1 .. 2 | 3.. 4 | (8-3) ..6 | 5+0..6.\n" + "-type t() :: t1() | t2() | t3() | b1() | u().\n" + "-spec t() -> t().\n" + "t() -> 3.\n">>, + ?line ok = file:write_file(FileName, C1), + ?line {ok, _, []} = compile:file(FileName, [return]), + + C2 = <<"-module(otp_8664).\n" + "-export([t/0]).\n" + "-spec t() -> 9 and 4.\n" + "t() -> 0.\n">>, + ?line ok = file:write_file(FileName, C2), + ?line {error,[{_,[{3,erl_lint,{type_syntax,integer}}]}],_} = + compile:file(FileName, [return]), + + ok. + +otp_9147(doc) -> + "OTP_9147. Create well-formed types when adding 'undefined'."; +otp_9147(suite) -> []; +otp_9147(Config) when is_list(Config) -> + FileName = filename('otp_9147.erl', Config), + C1 = <<"-module(otp_9147).\n" + "-export_type([undef/0]).\n" + "-record(undef, {f1 :: F1 :: a | b}).\n" + "-type undef() :: #undef{}.\n">>, + ?line ok = file:write_file(FileName, C1), + ?line {ok, _, []} = + compile:file(FileName, [return,'P',{outdir,?privdir}]), + PFileName = filename('otp_9147.P', Config), + ?line {ok, Bin} = file:read_file(PFileName), + %% The parentheses around "F1 :: a | b" are new (bugfix). + ?line true = + lists:member("-record(undef,{f1 :: undefined | (F1 :: a | b)}).", + string:tokens(binary_to_list(Bin), "\n")), + ok. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% compile(Config, Tests) -> F = fun({N,P}, BadL) -> case catch compile_file(Config, P) of - ok -> + ok -> case pp_forms(P) of ok -> BadL; @@ -924,8 +1080,8 @@ compile(Config, Tests) -> ?t:format("~nTest ~p failed.~n", [N]), fail() end; - Bad -> - ?t:format("~nTest ~p failed. got~n ~p~n", + Bad -> + ?t:format("~nTest ~p failed. got~n ~p~n", [N, Bad]), fail() end @@ -955,7 +1111,7 @@ compile_file(Config, Test0) -> Error -> Error end. - + compile_file(Config, Test0, Opts0) -> FileName = filename('erl_pp_test.erl', Config), Test = list_to_binary(["-module(erl_pp_test). " @@ -964,7 +1120,7 @@ compile_file(Config, Test0, Opts0) -> Opts = [export_all,return,nowarn_unused_record,{outdir,?privdir} | Opts0], ok = file:write_file(FileName, Test), case compile:file(FileName, Opts) of - {ok, _M, _Ws} -> + {ok, _M, _Ws} -> {ok, filename:rootname(FileName)}; Error -> Error end. @@ -991,7 +1147,7 @@ pp_forms(Bin, Hook) -> end. parse_and_pp_forms(String, Hook) -> - lists:append(lists:map(fun(AF) -> erl_pp:form(AF, Hook) + lists:append(lists:map(fun(AF) -> erl_pp:form(AF, Hook) end, parse_forms(String))). parse_forms(Chars) -> @@ -999,13 +1155,13 @@ parse_forms(Chars) -> parse_forms2(String, [], 1, []). parse_forms2([], _Cont, _Line, Forms) -> - lists:reverse(Forms); + lists:reverse(Forms); parse_forms2(String, Cont0, Line, Forms) -> case erl_scan:tokens(Cont0, String, Line) of {done, {ok, Tokens, EndLine}, Chars} -> {ok, Form} = erl_parse:parse_form(Tokens), parse_forms2(Chars, [], EndLine, [Form | Forms]); - {more, Cont} when element(3, Cont) =:= [] -> + {more, Cont} when element(4, Cont) =:= [] -> %% extra spaces after forms... parse_forms2([], Cont, Line, Forms); {more, Cont} -> @@ -1023,10 +1179,10 @@ pp_expr(Bin, Hook) -> PP2 = (catch parse_and_pp_expr(PPneg, 0, Hook)), if PP1 =:= PP2 -> % same line numbers - case + case (test_max_line(PP1) =:= ok) and (test_new_line(PPneg) =:= ok) of - true -> + true -> ok; false -> not_ok @@ -1059,7 +1215,7 @@ test_max_line(String) -> end. max_line(String) -> - lists:max([0 | [length(Sub) || + lists:max([0 | [length(Sub) || Sub <- string:tokens(String, "\n"), string:substr(Sub, 1, 5) =/= "-file"]]). diff --git a/lib/stdlib/test/erl_scan_SUITE.erl b/lib/stdlib/test/erl_scan_SUITE.erl index 32a06d15c7..31a4f94294 100644 --- a/lib/stdlib/test/erl_scan_SUITE.erl +++ b/lib/stdlib/test/erl_scan_SUITE.erl @@ -1,25 +1,26 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1998-2011. 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_scan_SUITE). --export([all/1]). +-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, error_1/1, error_2/1, iso88591/1, otp_7810/1]). +-export([ error_1/1, error_2/1, iso88591/1, otp_7810/1]). -import(lists, [nth/2,flatten/1]). -import(io_lib, [print/1]). @@ -34,19 +35,19 @@ -define(line, put(line, ?LINE), ). -define(config(A,B),config(A,B)). -define(t, test_server). -%% config(priv_dir, _) -> +%% config(priv_dir, _) -> %% "."; %% config(data_dir, _) -> %% ".". -else. --include("test_server.hrl"). --export([init_per_testcase/2, fin_per_testcase/2]). +-include_lib("test_server/include/test_server.hrl"). +-export([init_per_testcase/2, end_per_testcase/2]). init_per_testcase(_Case, Config) when is_list(Config) -> ?line Dog=test_server:timetrap(test_server:seconds(1200)), [{watchdog, Dog}|Config]. - -fin_per_testcase(_Case, Config) -> + +end_per_testcase(_Case, Config) -> Dog=?config(watchdog, Config), test_server:timetrap_cancel(Dog), ok. @@ -55,15 +56,27 @@ fin_per_testcase(_Case, Config) -> % Default timetrap timeout (set in init_per_testcase). -define(default_timeout, ?t:minutes(1)). -all(doc) -> - ["Test cases for the 'erl_scan' module."]; -all(suite) -> - [error,iso88591,otp_7810]. +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [{group, error}, iso88591, otp_7810]. + +groups() -> + [{error, [], [error_1, error_2]}]. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + -error(doc) -> - ["Error cases"]; -error(suite) -> - [error_1, error_2]. error_1(doc) -> ["(OTP-2347)"]; @@ -97,7 +110,7 @@ assert_type(N, integer) when is_integer(N) -> ok; assert_type(N, atom) when is_atom(N) -> ok. - + check(String) -> Error = erl_scan:string(String), check_error(Error, erl_scan). @@ -146,7 +159,7 @@ iso88591(Config) when is_list(Config) -> ok -> ok %Aok end. -otp_7810(doc) -> +otp_7810(doc) -> ["OTP-7810. White spaces, comments, and more.."]; otp_7810(suite) -> []; @@ -185,7 +198,7 @@ reserved_words() -> 'andalso', 'orelse', 'end', 'fun', 'if', 'let', 'of', 'query', 'receive', 'when', 'bnot', 'not', 'div', 'rem', 'band', 'and', 'bor', 'bxor', 'bsl', 'bsr', - 'or', 'xor', 'spec'] , % 'spec' shouldn't be there... + 'or', 'xor'], [begin ?line {RW, true} = {RW, erl_scan:reserved_word(RW)}, S = atom_to_list(RW), @@ -203,7 +216,7 @@ atoms() -> ?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 {ok,[{atom,_,'$a'}],{1,6}} = + ?line {ok,[{atom,_,'$a'}],{1,6}} = erl_scan:string("'$\\a'", {1,1}), ?line test("'$\\a'"), ok. @@ -221,8 +234,8 @@ punctuations() -> Three = ["/=:=", "<=:=", "==:=", ">=:="], % three tokens... No = Three ++ L, SL0 = [{S1++S2,{-length(S1),S1,S2}} || - S1 <- L, - S2 <- L, + S1 <- L, + S2 <- L, not lists:member(S1++S2, No)], SL = family_list(SL0), %% Two tokens. When there are several answers, the one with @@ -244,21 +257,24 @@ punctuations() -> {'\\',1},{'^',1},{'`',1},{'~',1}], ?line test_string("#&*+/:<>?@\\^`~", PTs2), + ?line test_string(".. ", [{'..',1}]), + ?line test("1 .. 2"), + ?line test_string("...", [{'...',1}]), ok. comments() -> ?line test("a %%\n b"), ?line {ok,[],1} = erl_scan:string("%"), ?line test("a %%\n b"), - ?line {ok,[{atom,_,a},{atom,_,b}],{2,3}} = + ?line {ok,[{atom,_,a},{atom,_,b}],{2,3}} = erl_scan:string("a %%\n b",{1,1}), - ?line {ok,[{atom,_,a},{comment,_,"%%"},{atom,_,b}],{2,3}} = + ?line {ok,[{atom,_,a},{comment,_,"%%"},{atom,_,b}],{2,3}} = erl_scan:string("a %%\n b",{1,1}, [return_comments]), ?line {ok,[{atom,_,a}, {white_space,_," "}, {white_space,_,"\n "}, {atom,_,b}], - {2,3}} = + {2,3}} = erl_scan:string("a %%\n b",{1,1},[return_white_spaces]), ?line {ok,[{atom,_,a}, {white_space,_," "}, @@ -275,14 +291,14 @@ errors() -> ?line {error,{1,erl_scan,char},1} = erl_scan:string("$"), ?line test_string([34,65,200,34], [{string,1,"A�"}]), ?line test_string("\\", [{'\\',1}]), - ?line {'EXIT',_} = + ?line {'EXIT',_} = (catch {foo, erl_scan:string('$\\a', {1,1})}), % type error - ?line {'EXIT',_} = + ?line {'EXIT',_} = (catch {foo, erl_scan:tokens([], '$\\a', {1,1})}), % type error ?line "{a,tuple}" = erl_scan:format_error({a,tuple}), ok. - + integers() -> [begin I = list_to_integer(S), @@ -299,14 +315,14 @@ base_integers() -> ?line test_string(BS++"#"++S, Ts) end || {BS,S} <- [{"2","11"}, {"5","23234"}, {"12","05a"}, {"16","abcdef"}, {"16","ABCDEF"}] ], - + ?line {error,{1,erl_scan,{base,1}},1} = erl_scan:string("1#000"), - + ?line test_string("12#bc", [{integer,1,11},{atom,1,c}]), - + [begin Str = BS ++ "#" ++ S, - ?line {error,{1,erl_scan,{illegal,integer}},1} = + ?line {error,{1,erl_scan,{illegal,integer}},1} = erl_scan:string(Str) end || {BS,S} <- [{"3","3"},{"15","f"}, {"12","c"}] ], @@ -323,8 +339,8 @@ floats() -> end || FS <- ["1.0","001.17","3.31200","1.0e0","1.0E17", "34.21E-18", "17.0E+14"]], ?line test_string("1.e2", [{integer,1,1},{'.',1},{atom,1,e2}]), - - ?line {error,{1,erl_scan,{illegal,float}},1} = + + ?line {error,{1,erl_scan,{illegal,float}},1} = erl_scan:string("1.0e400"), [begin ?line {error,{1,erl_scan,{illegal,float}},1} = erl_scan:string(S) @@ -345,16 +361,16 @@ dots() -> {".a", {ok,[{'.',1},{atom,1,a}],1}} ], ?line [R = erl_scan:string(S) || {S, R} <- Dot], - + ?line {ok,[{dot,_}=T1],{1,2}} = erl_scan:string(".", {1,1}, text), - ?line [{column,1},{length,1},{line,1},{text,"."}] = + ?line [{column,1},{length,1},{line,1},{text,"."}] = erl_scan:token_info(T1, [column, length, line, text]), ?line {ok,[{dot,_}=T2],{1,3}} = erl_scan:string(".%", {1,1}, text), - ?line [{column,1},{length,1},{line,1},{text,"."}] = + ?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), - ?line [{column,1},{length,1},{line,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}} = erl_scan:string(".$", {1,1}), @@ -376,10 +392,10 @@ dots() -> ?line {done,{ok,[{comment,_,"%. "}, {white_space,_,"\n"}, {dot,_}], - {2,3}}, ""} = + {2,3}}, ""} = erl_scan:tokens(C, "\n. ", {1,1}, return), % any loc, any options - ?line [test_string(S, R) || + ?line [test_string(S, R) || {S, R} <- [{".$\n", [{'.',1},{char,1,$\n}]}, {"$\\\n", [{char,1,$\n}]}, {"'\\\n'", [{atom,1,'\n'}]}, @@ -392,7 +408,7 @@ chars() -> Ts = [{char,1,C}], ?line test_string(L, Ts) end || C <- lists:seq(0, 255)], - + %% Leading zeroes... [begin L = lists:flatten(io_lib:format("$\\~3.8.0b", [C])), @@ -406,13 +422,13 @@ chars() -> Ts = [{char,1,C band 2#11111}], ?line test_string(L, Ts) end || C <- lists:seq(0, 255)], - + [begin L = "$\\" ++ [C], Ts = [{char,1,V}], ?line test_string(L, Ts) - end || {C,V} <- [{$n,$\n}, {$r,$\r}, {$t,$\t}, {$v,$\v}, - {$b,$\b}, {$f,$\f}, {$e,$\e}, {$s,$\s}, + end || {C,V} <- [{$n,$\n}, {$r,$\r}, {$t,$\t}, {$v,$\v}, + {$b,$\b}, {$f,$\f}, {$e,$\e}, {$s,$\s}, {$d,$\d}]], EC = [$\n,$\r,$\t,$\v,$\b,$\f,$\e,$\s,$\d], @@ -445,7 +461,7 @@ chars() -> end || C <- lists:seq(0, 255) -- (No ++ [$\\])], ?line test_string("$\n", [{char,1,$\n}]), - ?line {error,{{1,1},erl_scan,char},{1,4}} = + ?line {error,{{1,1},erl_scan,char},{1,4}} = erl_scan:string("$\\^",{1,1}), ?line test_string("$\\\n", [{char,1,$\n}]), %% Robert's scanner returns line 1: @@ -453,7 +469,7 @@ chars() -> ?line test_string("$\n\n", [{char,1,$\n}]), ?line test("$\n\n"), ok. - + variables() -> ?line test_string(" \237_Aou�eiy��", [{var,1,'_Aou�eiy��'}]), @@ -469,8 +485,8 @@ eof() -> ?line {done,{eof,2},eof} = erl_scan:tokens(C1, eof, 1), {more, C2} = erl_scan:tokens([], "abra", 1), %% An error before R13A. - %% ?line {done,Err={error,{1,erl_scan,scan},1},eof} = - ?line {done,{ok,[{atom,1,abra}],1},eof} = + %% ?line {done,Err={error,{1,erl_scan,scan},1},eof} = + ?line {done,{ok,[{atom,1,abra}],1},eof} = erl_scan:tokens(C2, eof, 1), %% With column. @@ -478,7 +494,7 @@ eof() -> ?line {done,{eof,{2,1}},eof} = erl_scan:tokens(C3, eof, 1), {more, C4} = erl_scan:tokens([], "abra", {1,1}), %% An error before R13A. - %% ?line {done,{error,{{1,1},erl_scan,scan},{1,5}},eof} = + %% ?line {done,{error,{{1,1},erl_scan,scan},{1,5}},eof} = ?line {done,{ok,[{atom,_,abra}],{1,5}},eof} = erl_scan:tokens(C4, eof, 1), @@ -486,7 +502,7 @@ eof() -> %% the R12B scanner returns eof as LeftoverChars: (eof is correct) ?line {more, C5} = erl_scan:tokens([], "a", 1), %% An error before R13A. - %% ?line {done,{error,{1,erl_scan,scan},1},eof} = + %% ?line {done,{error,{1,erl_scan,scan},1},eof} = ?line {done,{ok,[{atom,1,a}],1},eof} = erl_scan:tokens(C5,eof,1), @@ -528,7 +544,7 @@ illegal() -> erl_scan:tokens([], "foo "++Atom++". ", {1,1}), ?line {error,{{1,1},erl_scan,{illegal,atom}},{1,1003}} = erl_scan:string(QAtom, {1,1}), - ?line {done,{error,{{1,5},erl_scan,{illegal,atom}},{1,1007}},". "} = + ?line {done,{error,{{1,5},erl_scan,{illegal,atom}},{1,1007}},". "} = erl_scan:tokens([], "foo "++QAtom++". ", {1,1}), ?line {error,{{1,1},erl_scan,{illegal,var}},{1,1001}} = erl_scan:string(Var, {1,1}), @@ -553,7 +569,7 @@ crashes() -> ?line {'EXIT',_} = (catch {foo, erl_scan:string("\"\\v"++[-1,$"])}), %$" ?line {'EXIT',_} = (catch {foo, erl_scan:string([$",-1,$"])}), ?line {'EXIT',_} = (catch {foo, erl_scan:string("% foo"++[-1])}), - ?line {'EXIT',_} = + ?line {'EXIT',_} = (catch {foo, erl_scan:string("% foo"++[-1],{1,1})}), ?line {'EXIT',_} = (catch {foo, erl_scan:string([a])}), % type error @@ -564,7 +580,7 @@ crashes() -> ?line {'EXIT',_} = (catch {foo, erl_scan:string("\"\\v"++[a,$"])}), %$" ?line {'EXIT',_} = (catch {foo, erl_scan:string([$",a,$"])}), ?line {'EXIT',_} = (catch {foo, erl_scan:string("% foo"++[a])}), - ?line {'EXIT',_} = + ?line {'EXIT',_} = (catch {foo, erl_scan:string("% foo"++[a],{1,1})}), ?line {'EXIT',_} = (catch {foo, erl_scan:string([3.0])}), % type error @@ -573,26 +589,26 @@ crashes() -> options() -> %% line and column are not options, but tested here - ?line {ok,[{atom,1,foo},{white_space,1," "},{comment,1,"% bar"}], 1} = + ?line {ok,[{atom,1,foo},{white_space,1," "},{comment,1,"% bar"}], 1} = erl_scan:string("foo % bar", 1, return), - ?line {ok,[{atom,1,foo},{white_space,1," "}],1} = + ?line {ok,[{atom,1,foo},{white_space,1," "}],1} = erl_scan:string("foo % bar", 1, return_white_spaces), - ?line {ok,[{atom,1,foo},{comment,1,"% bar"}],1} = + ?line {ok,[{atom,1,foo},{comment,1,"% bar"}],1} = erl_scan:string("foo % bar", 1, return_comments), - ?line {ok,[{atom,17,foo}],17} = + ?line {ok,[{atom,17,foo}],17} = erl_scan:string("foo % bar", 17), - ?line {'EXIT',{function_clause,_}} = - (catch {foo, + ?line {'EXIT',{function_clause,_}} = + (catch {foo, erl_scan:string("foo % bar", {a,1}, [])}), % type error - ?line {ok,[{atom,_,foo}],{17,18}} = + ?line {ok,[{atom,_,foo}],{17,18}} = erl_scan:string("foo % bar", {17,9}, []), - ?line {'EXIT',{function_clause,_}} = + ?line {'EXIT',{function_clause,_}} = (catch {foo, erl_scan:string("foo % bar", {1,0}, [])}), % type error - ?line {ok,[{foo,1}],1} = + ?line {ok,[{foo,1}],1} = erl_scan:string("foo % bar",1, [{reserved_word_fun, fun(W) -> W =:= foo end}]), - ?line {'EXIT',{badarg,_}} = + ?line {'EXIT',{badarg,_}} = (catch {foo, erl_scan:string("foo % bar",1, % type error [{reserved_word_fun, @@ -618,14 +634,14 @@ more_options() -> token_info() -> ?line {ok,[T1],_} = erl_scan:string("foo", {1,18}, [text]), - {'EXIT',{badarg,_}} = + {'EXIT',{badarg,_}} = (catch {foo, erl_scan:token_info(T1, foo)}), % type error ?line {line,1} = erl_scan:token_info(T1, line), ?line {column,18} = erl_scan:token_info(T1, column), ?line {length,3} = erl_scan:token_info(T1, length), ?line {text,"foo"} = erl_scan:token_info(T1, text), ?line [{category,atom},{column,18},{length,3},{line,1}, - {symbol,foo},{text,"foo"}] = + {symbol,foo},{text,"foo"}] = erl_scan:token_info(T1), ?line [{length,3},{column,18}] = erl_scan:token_info(T1, [length, column]), @@ -648,9 +664,9 @@ token_info() -> ?line {category,'='} = erl_scan:token_info(T3, category), ?line [{symbol,'='}] = erl_scan:token_info(T3, [symbol]), ok. - + attributes_info() -> - ?line {'EXIT',_} = + ?line {'EXIT',_} = (catch {foo,erl_scan:attributes_info(foo)}), % type error ?line [{line,18}] = erl_scan:attributes_info(18), ?line {location,19} = erl_scan:attributes_info(19, location), @@ -717,9 +733,9 @@ set_attribute() -> ?line [{line,{17,11}},{text,"foo"}] = erl_scan:attributes_info(A7, [line,column,text]), - ?line {'EXIT',_} = + ?line {'EXIT',_} = (catch {foo, erl_scan:set_attribute(line, [], F2)}), % type error - ?line {'EXIT',{badarg,_}} = + ?line {'EXIT',{badarg,_}} = (catch {foo, erl_scan:set_attribute(column, [], F2)}), % type error ok. @@ -790,14 +806,14 @@ unicode() -> ?line {ok,[{char,1,83},{integer,1,45}],1} = erl_scan:string("$\\12345"), % not unicode - ?line {error,{1,erl_scan,{illegal,character}},1} = + ?line {error,{1,erl_scan,{illegal,character}},1} = erl_scan:string([1089]), - ?line {error,{{1,1},erl_scan,{illegal,character}},{1,2}} = + ?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} = + %% ?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,3},erl_scan,{illegal,character}},{1,4}} = erl_scan:string("'a"++[1089]++"b'", {1,1}), ?line test("\"a"++[1089]++"b\""), ?line {ok,[{char,1,1}],1} = erl_scan:string([$$,$\\,$^,1089]), @@ -822,7 +838,7 @@ unicode() -> ?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,1},{symbol,16#aaa},{text,Qs}] = + {line,1},{symbol,16#aaa},{text,Qs}] = erl_scan:token_info(Q2), U1 = "\"\\x{aaa}\"", @@ -830,11 +846,11 @@ unicode() -> ?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}"}] = + {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} = + ?line {ok,[{'[',1},{integer,1,16#aaa},{']',1}],1} = erl_scan:string(U1, 1), U2 = "\"\\x41\\x{fff}\\x42\"", @@ -844,7 +860,7 @@ unicode() -> 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} = + {']',3}],3} = erl_scan:string(U3, 1), U4 = "\"\\^\n\\x{aaa}\\^\n\"", @@ -867,10 +883,10 @@ unicode() -> {char,_,$d},{']',_}],{1,8}} = erl_scan:string(Str1, {1,1}), ?line test(Str1), Comment = "%% "++[1089], - ?line {ok,[{comment,1,[$%,$%,$\s,1089]}],1} = + ?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), + ?line {ok,[{comment,_,[$%,$%,$\s,1089]}],{1,5}} = + erl_scan:string(Comment, {1,1}, return), ok. more_chars() -> @@ -885,7 +901,7 @@ more_chars() -> erl_scan:tokens(C1, eof, 1), ?line {ok,[{char,1,123},{atom,1,a},{'}',1}],1} = erl_scan:string("$\\{a}"), - + ?line {error,{{1,1},erl_scan,char},{1,4}} = erl_scan:string("$\\x", {1,1}), ?line {error,{{1,1},erl_scan,char},{1,5}} = @@ -893,12 +909,12 @@ more_chars() -> ?line {more, C3} = erl_scan:tokens([], "$\\x", {1,1}), ?line {done,{error,{{1,1},erl_scan,char},{1,4}},eof} = erl_scan:tokens(C3, eof, 1), - ?line {error,{{1,1},erl_scan,char},{1,5}} = + ?line {error,{{1,1},erl_scan,char},{1,5}} = erl_scan:string("$\\x{",{1,1}), ?line {more, C2} = erl_scan:tokens([], "$\\x{", {1,1}), - ?line {done,{error,{{1,1},erl_scan,char},{1,5}},eof} = + ?line {done,{error,{{1,1},erl_scan,char},{1,5}},eof} = erl_scan:tokens(C2, eof, 1), - ?line {error,{1,erl_scan,{illegal,character}},1} = + ?line {error,{1,erl_scan,{illegal,character}},1} = erl_scan:string("$\\x{g}"), ?line {error,{{1,1},erl_scan,{illegal,character}},{1,5}} = erl_scan:string("$\\x{g}", {1,1}), @@ -924,12 +940,12 @@ more_chars() -> ?line test("$\\x{10FFFF}"), ?line test("$\\x{10ffff}"), ?line test("\"$\n \\{1}\""), - ?line {error,{1,erl_scan,{illegal,character}},1} = + ?line {error,{1,erl_scan,{illegal,character}},1} = erl_scan:string("$\\x{110000}"), - ?line {error,{{1,1},erl_scan,{illegal,character}},{1,12}} = + ?line {error,{{1,1},erl_scan,{illegal,character}},{1,12}} = erl_scan:string("$\\x{110000}", {1,1}), - ?line {error,{{1,1},erl_scan,{illegal,character}},{1,4}} = + ?line {error,{{1,1},erl_scan,{illegal,character}},{1,4}} = erl_scan:string("$\\xfg", {1,1}), ?line test("$\\xffg"), @@ -953,11 +969,11 @@ test(String) -> {Wtokens, Wend}, {Ctokens, Cend}, {CWtokens, CWend}, - {CWtokens2, _}] = + {CWtokens2, _}] = [scan_string_with_column(String, X) || - X <- [[], - [return_white_spaces], - [return_comments], + X <- [[], + [return_white_spaces], + [return_comments], [return], [return]]], % for white space compaction test @@ -969,7 +985,7 @@ test(String) -> {none,Tokens} = {none, filter_tokens(CWtokens, [white_space,comment])}, {comments,Ctokens} = {comments,filter_tokens(CWtokens, [white_space])}, - {white_spaces,Wtokens} = + {white_spaces,Wtokens} = {white_spaces,filter_tokens(CWtokens, [comment])}, %% Use token attributes to extract parts from the original string, @@ -991,9 +1007,9 @@ test(String) -> %% Line attribute only: [Simple,Wsimple,Csimple,WCsimple] = Simples = [element(2, erl_scan:string(String, 1, Opts)) || - Opts <- [[], - [return_white_spaces], - [return_comments], + Opts <- [[], + [return_white_spaces], + [return_comments], [return]]], {consistent,true} = {consistent,consistent_attributes(Simples)}, {simple_wc,WCsimple} = {simple_wc,simplify(CWtokens)}, @@ -1004,19 +1020,19 @@ test(String) -> %% Line attribute only, with text: [SimpleTxt,WsimpleTxt,CsimpleTxt,WCsimpleTxt] = SimplesTxt = [element(2, erl_scan:string(String, 1, [text|Opts])) || - Opts <- [[], - [return_white_spaces], - [return_comments], + Opts <- [[], + [return_white_spaces], + [return_comments], [return]]], TextTxt = get_text(WCsimpleTxt), {text_txt,TextTxt,String} = {text_txt,String,TextTxt}, - {consistent_txt,true} = + {consistent_txt,true} = {consistent_txt,consistent_attributes(SimplesTxt)}, - {simple_txt,SimpleTxt} = + {simple_txt,SimpleTxt} = {simple_txt,filter_tokens(WCsimpleTxt, [white_space,comment])}, - {simple_c_txt,CsimpleTxt} = + {simple_c_txt,CsimpleTxt} = {simple_c_txt,filter_tokens(WCsimpleTxt, [white_space])}, - {simple_w_txt,WsimpleTxt} = + {simple_w_txt,WsimpleTxt} = {simple_w_txt,filter_tokens(WCsimpleTxt, [comment])}, ok. @@ -1024,18 +1040,18 @@ test(String) -> test_white_space_compaction(Tokens, Tokens2) when Tokens =:= Tokens2 -> [WS, WS2] = [select_tokens(Ts, [white_space]) || Ts <- [Tokens, Tokens2]], test_wsc(WS, WS2). - + test_wsc([], []) -> ok; test_wsc([Token|Tokens], [Token2|Tokens2]) -> - [Text, Text2] = [Text || - {text, Text} <- + [Text, Text2] = [Text || + {text, Text} <- [erl_scan:token_info(T, text) || T <- [Token, Token2]]], Sz = erts_debug:size(Text), Sz2 = erts_debug:size({Text, Text2}), IsCompacted = Sz2 < 2*Sz+erts_debug:size({a,a}), - ToBeCompacted = is_compacted(Text), + ToBeCompacted = is_compacted(Text), if IsCompacted =:= ToBeCompacted -> test_wsc(Tokens, Tokens2); @@ -1050,14 +1066,14 @@ is_compacted("\n\r") -> is_compacted("\n\f") -> true; is_compacted([$\n|String]) -> - all_spaces(String) + all_spaces(String) orelse all_tabs(String); is_compacted(String) -> all_spaces(String) orelse all_tabs(String). - + all_spaces(L) -> all_same(L, $\s). @@ -1078,7 +1094,7 @@ newlines_first([Token|Tokens]) -> _ -> Nnls =:= 0 end, - if + if OK -> newlines_first(Tokens); true -> OK end. @@ -1097,7 +1113,7 @@ simplify([]) -> get_text(Tokens) -> lists:flatten( - [T || + [T || Token <- Tokens, ({text,T} = erl_scan:token_info(Token, text)) =/= []]). @@ -1108,7 +1124,7 @@ test_decorated_tokens(String, Tokens) -> token_attrs(Tokens) -> [{L,C,Len,T} || Token <- Tokens, - ([{line,L},{column,C},{length,Len},{text,T}] = + ([{line,L},{column,C},{length,Len},{text,T}] = erl_scan:token_info(Token, [line,column,length,text])) =/= []]. test_strings([], _S, Line, Column) -> @@ -1150,7 +1166,7 @@ scan_string_with_column(String, Options0) -> {ok, Ts1, End1} = erl_scan:string(String, StartLoc, Options), TString = String ++ ". ", {ok,Ts2,End2} = scan_tokens(TString, Options, [], StartLoc), - {ok, Ts3, End3} = + {ok, Ts3, End3} = scan_tokens_1({more, []}, TString, Options, [], StartLoc), {end_2,End2,End3} = {end_2,End3,End2}, {EndLine1,EndColumn1} = End1, @@ -1190,8 +1206,8 @@ consistent_attributes([Ts | TsL]) -> L = [T || T <- Ts, is_integer(element(2, T))], case L of [] -> - TagsL = [[Tag || {Tag,_} <- - erl_scan:attributes_info(element(2, T))] || + TagsL = [[Tag || {Tag,_} <- + erl_scan:attributes_info(element(2, T))] || T <- Ts], case lists:usort(TagsL) of [_] -> diff --git a/lib/stdlib/test/error_logger_forwarder.erl b/lib/stdlib/test/error_logger_forwarder.erl index 7d99d07860..5703ac769a 100644 --- a/lib/stdlib/test/error_logger_forwarder.erl +++ b/lib/stdlib/test/error_logger_forwarder.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009. All Rights Reserved. +%% Copyright Ericsson AB 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 diff --git a/lib/stdlib/test/escript_SUITE.erl b/lib/stdlib/test/escript_SUITE.erl index 70aae54d62..9f95df062b 100644 --- a/lib/stdlib/test/escript_SUITE.erl +++ b/lib/stdlib/test/escript_SUITE.erl @@ -1,55 +1,71 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2007-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2007-2011. 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(escript_SUITE). -export([ - all/1, + all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2, init_per_testcase/2, - fin_per_testcase/2, + end_per_testcase/2, basic/1, - errors/1, + errors/1, strange_name/1, emulator_flags/1, module_script/1, beam_script/1, archive_script/1, - epp/1 + epp/1, + create_and_extract/1, + foldl/1, + overflow/1, + verify_sections/3 ]). --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). +-include_lib("kernel/include/file.hrl"). + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [basic, errors, strange_name, emulator_flags, + module_script, beam_script, archive_script, epp, + create_and_extract, foldl, overflow]. + +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. -all(suite) -> - [ - basic, - errors, - strange_name, - emulator_flags, - module_script, - beam_script, - archive_script, - epp - ]. +end_per_group(_GroupName, Config) -> + Config. init_per_testcase(_Case, Config) -> ?line Dog = ?t:timetrap(?t:minutes(1)), [{watchdog,Dog}|Config]. -fin_per_testcase(_Case, Config) -> +end_per_testcase(_Case, Config) -> Dog = ?config(watchdog, Config), test_server:timetrap_cancel(Dog), ok. @@ -68,11 +84,11 @@ basic(Config) when is_list(Config) -> ?line run(Dir, "factorial_warning 20", [data_dir,<<"factorial_warning:12: Warning: function bar/0 is unused\n" "factorial 20 = 2432902008176640000\nExitCode:0">>]), - ?line run(Dir, "-s", "factorial_warning", + ?line run_with_opts(Dir, "-s", "factorial_warning", [data_dir,<<"factorial_warning:12: Warning: function bar/0 is unused\nExitCode:0">>]), - ?line run(Dir, "-s -i", "factorial_warning", + ?line run_with_opts(Dir, "-s -i", "factorial_warning", [data_dir,<<"factorial_warning:12: Warning: function bar/0 is unused\nExitCode:0">>]), - ?line run(Dir, "-c -s", "factorial_warning", + ?line run_with_opts(Dir, "-c -s", "factorial_warning", [data_dir,<<"factorial_warning:12: Warning: function bar/0 is unused\nExitCode:0">>]), ?line run(Dir, "filesize "++filename:join(?config(data_dir, Config),"filesize"), [data_dir,<<"filesize:11: Warning: function id/1 is unused\n324\nExitCode:0">>]), @@ -100,7 +116,7 @@ errors(Config) when is_list(Config) -> [data_dir,<<"lint_error:6: function main/1 already defined\n">>, data_dir,"lint_error:8: variable 'ExitCode' is unbound\n", <<"escript: There were compilation errors.\nExitCode:127">>]), - ?line run(Dir, "-s", "lint_error", + ?line run_with_opts(Dir, "-s", "lint_error", [data_dir,<<"lint_error:6: function main/1 already defined\n">>, data_dir,"lint_error:8: variable 'ExitCode' is unbound\n", <<"escript: There were compilation errors.\nExitCode:127">>]), @@ -140,31 +156,31 @@ module_script(Config) when is_list(Config) -> OrigFile = filename:join([Data,"emulator_flags"]), {ok, OrigBin} = file:read_file(OrigFile), ?line [Shebang, Mode, Flags | Source] = string:tokens(binary_to_list(OrigBin), "\n"), - ?line {ok, OrigFI} = file:read_file_info(OrigFile), + ?line {ok, OrigFI} = file:read_file_info(OrigFile), %% Write source file Priv = ?config(priv_dir, Config), Dir = filename:absname(Priv), % Get rid of trailing slash. Base = "module_script", ErlFile = filename:join([Priv, Base ++ ".erl"]), - ErlCode = ["-module(", Base, ").\n", + ErlCode = ["\n-module(", Base, ").\n", "-export([main/1]).\n\n", string:join(Source, "\n"), "\n"], ?line ok = file:write_file(ErlFile, ErlCode), - + %%%%%%% %% Create and run scripts without emulator flags %% With shebang NoArgsBase = Base ++ "_no_args_with_shebang", NoArgsFile = filename:join([Priv, NoArgsBase]), - ?line ok = file:write_file(NoArgsFile, + ?line ok = file:write_file(NoArgsFile, [Shebang, "\n", ErlCode]), ?line ok = file:write_file_info(NoArgsFile, OrigFI), - - ?line run(Dir, NoArgsBase ++ " -arg1 arg2 arg3", + + ?line run(Dir, NoArgsBase ++ " -arg1 arg2 arg3", [<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n" "nostick:[]\n" "mnesia:[]\n" @@ -172,7 +188,7 @@ module_script(Config) when is_list(Config) -> "unknown:[]\n" "ExitCode:0">>]), - ?line run(Dir, "", NoArgsBase ++ " -arg1 arg2 arg3", + ?line run_with_opts(Dir, "", NoArgsBase ++ " -arg1 arg2 arg3", [<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n" "nostick:[]\n" "mnesia:[]\n" @@ -183,33 +199,33 @@ module_script(Config) when is_list(Config) -> %% Without shebang NoArgsBase2 = Base ++ "_no_args_without_shebang", NoArgsFile2 = filename:join([Priv, NoArgsBase2]), - ?line ok = file:write_file(NoArgsFile2, + ?line ok = file:write_file(NoArgsFile2, ["Something else than shebang!!!", "\n", ErlCode]), ?line ok = file:write_file_info(NoArgsFile2, OrigFI), - - ?line run(Dir, "", NoArgsBase2 ++ " -arg1 arg2 arg3", + + ?line run_with_opts(Dir, "", NoArgsBase2 ++ " -arg1 arg2 arg3", [<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n" "nostick:[]\n" "mnesia:[]\n" "ERL_FLAGS=false\n" "unknown:[]\n" "ExitCode:0">>]), - + %% Plain module without header NoArgsBase3 = Base ++ "_no_args_without_header", NoArgsFile3 = filename:join([Priv, NoArgsBase3]), ?line ok = file:write_file(NoArgsFile3, [ErlCode]), ?line ok = file:write_file_info(NoArgsFile3, OrigFI), - - ?line run(Dir, "", NoArgsBase3 ++ " -arg1 arg2 arg3", + + ?line run_with_opts(Dir, "", NoArgsBase3 ++ " -arg1 arg2 arg3", [<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n" "nostick:[]\n" "mnesia:[]\n" "ERL_FLAGS=false\n" "unknown:[]\n" "ExitCode:0">>]), - + %%%%%%% %% Create and run scripts with emulator flags @@ -217,12 +233,12 @@ module_script(Config) when is_list(Config) -> ArgsBase = Base ++ "_args_with_shebang", ArgsFile = filename:join([Priv, ArgsBase]), ?line ok = file:write_file(ArgsFile, - [Shebang, "\n", + [Shebang, "\n", Mode, "\n", Flags, "\n", ErlCode]), - ?line ok = file:write_file_info(ArgsFile, OrigFI), - + ?line ok = file:write_file_info(ArgsFile, OrigFI), + ?line run(Dir, ArgsBase ++ " -arg1 arg2 arg3", [<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n" "nostick:[{nostick,[]}]\n" @@ -242,32 +258,32 @@ beam_script(Config) when is_list(Config) -> OrigFile = filename:join([Data,"emulator_flags"]), {ok, OrigBin} = file:read_file(OrigFile), ?line [Shebang, Mode, Flags | Source] = string:tokens(binary_to_list(OrigBin), "\n"), - ?line {ok, OrigFI} = file:read_file_info(OrigFile), + ?line {ok, OrigFI} = file:read_file_info(OrigFile), %% Write source file Priv = ?config(priv_dir, Config), Dir = filename:absname(Priv), % Get rid of trailing slash. Base = "beam_script", ErlFile = filename:join([Priv, Base ++ ".erl"]), - ?line ok = file:write_file(ErlFile, - ["-module(", Base, ").\n", + ?line ok = file:write_file(ErlFile, + ["\n-module(", Base, ").\n", "-export([main/1]).\n\n", string:join(Source, "\n"), "\n"]), %% Compile the code ?line {ok, _Mod, BeamCode} = compile:file(ErlFile, [binary]), - + %%%%%%% %% Create and run scripts without emulator flags %% With shebang NoArgsBase = Base ++ "_no_args_with_shebang", NoArgsFile = filename:join([Priv, NoArgsBase]), - ?line ok = file:write_file(NoArgsFile, + ?line ok = file:write_file(NoArgsFile, [Shebang, "\n", BeamCode]), - ?line ok = file:write_file_info(NoArgsFile, OrigFI), + ?line ok = file:write_file_info(NoArgsFile, OrigFI), ?line run(Dir, NoArgsBase ++ " -arg1 arg2 arg3", [<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n" @@ -277,7 +293,7 @@ beam_script(Config) when is_list(Config) -> "unknown:[]\n" "ExitCode:0">>]), - ?line run(Dir, "", NoArgsBase ++ " -arg1 arg2 arg3", + ?line run_with_opts(Dir, "", NoArgsBase ++ " -arg1 arg2 arg3", [<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n" "nostick:[]\n" "mnesia:[]\n" @@ -288,12 +304,12 @@ beam_script(Config) when is_list(Config) -> %% Without shebang NoArgsBase2 = Base ++ "_no_args_without_shebang", NoArgsFile2 = filename:join([Priv, NoArgsBase2]), - ?line ok = file:write_file(NoArgsFile2, + ?line ok = file:write_file(NoArgsFile2, ["Something else than shebang!!!", "\n", BeamCode]), - ?line ok = file:write_file_info(NoArgsFile2, OrigFI), + ?line ok = file:write_file_info(NoArgsFile2, OrigFI), - ?line run(Dir, "", NoArgsBase2 ++ " -arg1 arg2 arg3", + ?line run_with_opts(Dir, "", NoArgsBase2 ++ " -arg1 arg2 arg3", [<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n" "nostick:[]\n" "mnesia:[]\n" @@ -305,9 +321,9 @@ beam_script(Config) when is_list(Config) -> NoArgsBase3 = Base ++ "_no_args_without_header", NoArgsFile3 = filename:join([Priv, NoArgsBase3]), ?line ok = file:write_file(NoArgsFile3, [BeamCode]), - ?line ok = file:write_file_info(NoArgsFile3, OrigFI), + ?line ok = file:write_file_info(NoArgsFile3, OrigFI), - ?line run(Dir, "", NoArgsBase3 ++ " -arg1 arg2 arg3", + ?line run_with_opts(Dir, "", NoArgsBase3 ++ " -arg1 arg2 arg3", [<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n" "nostick:[]\n" "mnesia:[]\n" @@ -322,12 +338,12 @@ beam_script(Config) when is_list(Config) -> ArgsBase = Base ++ "_args", ArgsFile = filename:join([Priv, ArgsBase]), ?line ok = file:write_file(ArgsFile, - [Shebang, "\n", + [Shebang, "\n", Mode, "\n", Flags, "\n", BeamCode]), - ?line ok = file:write_file_info(ArgsFile, OrigFI), - + ?line ok = file:write_file_info(ArgsFile, OrigFI), + ?line run(Dir, ArgsBase ++ " -arg1 arg2 arg3", [<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n" "nostick:[{nostick,[]}]\n" @@ -356,13 +372,13 @@ archive_script(Config) when is_list(Config) -> ?line ok = compile_app(TopDir, "archive_script_dict"), ?line ok = compile_app(TopDir, "archive_script_dummy"), ?line {ok, MainFiles} = file:list_dir(TopDir), - ?line ok = compile_files(MainFiles, TopDir, TopDir), - + ?line ok = compile_files(MainFiles, TopDir, TopDir), + %% Create the archive {ok, TopFiles} = file:list_dir(TopDir), ?line {ok, {_, ArchiveBin}} = zip:create(Archive, TopFiles, [memory, {compress, []}, {cwd, TopDir}]), - + %% Read the source script OrigFile = filename:join([DataDir, "emulator_flags"]), {ok, OrigBin} = file:read_file(OrigFile), @@ -371,73 +387,73 @@ archive_script(Config) when is_list(Config) -> Flags = "%%! -archive_script_dict foo bar" " -archive_script_dict foo" " -archive_script_dummy bar", - ?line {ok, OrigFI} = file:read_file_info(OrigFile), - + ?line {ok, OrigFI} = file:read_file_info(OrigFile), + %%%%%%% %% Create and run scripts without emulator flags - MainBase = "archive_script_main", - MainScript = filename:join([PrivDir, MainBase]), + MainBase = "archive_script_main", + MainScript = filename:join([PrivDir, MainBase]), %% With shebang - ?line ok = file:write_file(MainScript, + ?line ok = file:write_file(MainScript, [Shebang, "\n", Flags, "\n", ArchiveBin]), - ?line ok = file:write_file_info(MainScript, OrigFI), - + ?line ok = file:write_file_info(MainScript, OrigFI), + ?line run(PrivDir, MainBase ++ " -arg1 arg2 arg3", - [<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n" + [<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n" "dict:[{archive_script_dict,[\"foo\",\"bar\"]},{archive_script_dict,[\"foo\"]}]\n" "dummy:[{archive_script_dummy,[\"bar\"]}]\n" "priv:{ok,<<\"Some private data...\\n\">>}\n" "ExitCode:0">>]), - ?line run(PrivDir, "", MainBase ++ " -arg1 arg2 arg3", - [<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n" + ?line run_with_opts(PrivDir, "", MainBase ++ " -arg1 arg2 arg3", + [<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n" "dict:[{archive_script_dict,[\"foo\",\"bar\"]},{archive_script_dict,[\"foo\"]}]\n" "dummy:[{archive_script_dummy,[\"bar\"]}]\n" "priv:{ok,<<\"Some private data...\\n\">>}\n" "ExitCode:0">>]), - + ?line ok = file:rename(MainScript, MainScript ++ "_with_shebang"), %% Without shebang (no flags) - ?line ok = file:write_file(MainScript, + ?line ok = file:write_file(MainScript, ["Something else than shebang!!!", "\n", ArchiveBin]), - ?line ok = file:write_file_info(MainScript, OrigFI), - - ?line run(PrivDir, "", MainBase ++ " -arg1 arg2 arg3", - [<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n" + ?line ok = file:write_file_info(MainScript, OrigFI), + + ?line run_with_opts(PrivDir, "", MainBase ++ " -arg1 arg2 arg3", + [<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n" "dict:[]\n" "dummy:[]\n" "priv:{ok,<<\"Some private data...\\n\">>}\n" "ExitCode:0">>]), ?line ok = file:rename(MainScript, MainScript ++ "_without_shebang"), - + %% Plain archive without header (no flags) - + ?line ok = file:write_file(MainScript, [ArchiveBin]), - ?line ok = file:write_file_info(MainScript, OrigFI), - - ?line run(PrivDir, "", MainBase ++ " -arg1 arg2 arg3", - [<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n" + ?line ok = file:write_file_info(MainScript, OrigFI), + + ?line run_with_opts(PrivDir, "", MainBase ++ " -arg1 arg2 arg3", + [<<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n" "dict:[]\n" "dummy:[]\n" "priv:{ok,<<\"Some private data...\\n\">>}\n" "ExitCode:0">>]), ?line ok = file:rename(MainScript, MainScript ++ "_without_header"), - + %%%%%%% %% Create and run scripts with emulator flags AltBase = "archive_script_alternate_main", AltScript = filename:join([PrivDir, AltBase]), - ?line ok = file:write_file(AltScript, + ?line ok = file:write_file(AltScript, [Shebang, "\n", Mode, "\n", Flags, " -escript main archive_script_main2\n", ArchiveBin]), - ?line ok = file:write_file_info(AltScript, OrigFI), + ?line ok = file:write_file_info(AltScript, OrigFI), ?line run(PrivDir, AltBase ++ " -arg1 arg2 arg3", [<<"main2:[\"-arg1\",\"arg2\",\"arg3\"]\n" @@ -445,7 +461,7 @@ archive_script(Config) when is_list(Config) -> "dummy:[{archive_script_dummy,[\"bar\"]}]\n" "priv:{ok,<<\"Some private data...\\n\">>}\n" "ExitCode:0">>]), - + ok. compile_app(TopDir, AppName) -> @@ -482,6 +498,265 @@ epp(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +create_and_extract(Config) when is_list(Config) -> + {NewFile, FileInfo, + EmuArg, Source, + _ErlBase, ErlCode, + _BeamBase, BeamCode, + ArchiveBin} = + prepare_creation("create_and_extract", Config), + + Bodies = + [[{source, ErlCode}], + [{beam, BeamCode}], + [{archive, ArchiveBin}]], + + %% Verify all combinations of scripts with shebangs + [verify_sections(NewFile, FileInfo, S ++ C ++ E ++ B) || + S <- [[{shebang, default}], + [{shebang, "/usr/bin/env escript"}]], + C <- [[], + [{comment, undefined}], + [{comment, default}], + [{comment, "This is a nonsense comment"}]], + E <- [[], + [{emu_args, undefined}], + [{emu_args, EmuArg}]], + B <- [[{source, Source}] | Bodies]], + + %% Verify all combinations of scripts without shebangs + [verify_sections(NewFile, FileInfo, S ++ C ++ E ++ B) || + S <- [[], [{shebang, undefined}]], + C <- [[], [{comment, undefined}]], + E <- [[], [{emu_args, undefined}]], + B <- Bodies], + + %% Verify the compile_source option + file:delete(NewFile), + ?line ok = escript:create(NewFile, [{source, Source}]), + ?line {ok, [_, _, _, {source, Source}]} = escript:extract(NewFile, []), + ?line {ok, [_, _, _, {source, BeamCode2}]} = + escript:extract(NewFile, [compile_source]), + verify_sections(NewFile, FileInfo, + [{shebang, default}, + {comment, default}, + {beam, BeamCode2}]), + + file:delete(NewFile), + ok. + +prepare_creation(Base, Config) -> + %% Read the source + PrivDir = ?config(priv_dir, Config), + DataDir = ?config(data_dir, Config), + OrigFile = filename:join([DataDir,"emulator_flags"]), + ?line {ok, FileInfo} = file:read_file_info(OrigFile), + NewFile = filename:join([PrivDir, Base]), + ?line {ok, [{shebang, default}, + {comment, _}, + {emu_args, EmuArg}, + {source, Source}]} = + escript:extract(OrigFile, []), + + %% Compile the code + ErlFile = NewFile ++ ".erl", + ErlCode = list_to_binary(["\n-module(", Base, ").\n", + "-export([main/1]).\n\n", + Source, "\n\n"]), + ?line ok = file:write_file(ErlFile, ErlCode), + + %% Compile the code + ?line {ok, _Mod, BeamCode} = + compile:file(ErlFile, [binary, debug_info]), + + %% Create an archive + ?line {ok, {_, ArchiveBin}} = + zip:create("dummy_archive_name", + [{Base ++ ".erl", ErlCode}, + {Base ++ ".beam", BeamCode}], + [{compress, []}, memory]), + {NewFile, FileInfo, + EmuArg, Source, + Base ++ ".erl", ErlCode, + Base ++ ".beam", BeamCode, + ArchiveBin}. + +verify_sections(File, FileInfo, Sections) -> + io:format("~p:verify_sections(\n\t~p,\n\t~p,\n\t~p).\n", + [?MODULE, File, FileInfo, Sections]), + + %% Create + file:delete(File), + ?line ok = escript:create(File, Sections), + ?line ok = file:write_file_info(File, FileInfo), + + %% Run + Dir = filename:absname(filename:dirname(File)), + Base = filename:basename(File), + + HasArg = fun(Tag) -> + case lists:keysearch(Tag, 1, Sections) of + false -> false; + {value, {_, undefined}} -> false; + {value, _} -> true + end + end, + ExpectedMain = <<"main:[\"-arg1\",\"arg2\",\"arg3\"]\n">>, + ExpectedOutput = + case HasArg(emu_args) of + true -> + <<"nostick:[{nostick,[]}]\n" + "mnesia:[{mnesia,[\"dir\",\"a/directory\"]},{mnesia,[\"debug\",\"verbose\"]}]\n" + "ERL_FLAGS=false\n" + "unknown:[]\n" + "ExitCode:0">>; + false -> + <<"nostick:[]\nmnesia:[]\nERL_FLAGS=false\nunknown:[]\nExitCode:0">> + end, + + InputArgs = Base ++ " -arg1 arg2 arg3", + Expected = <<ExpectedMain/binary, ExpectedOutput/binary>>, + case HasArg(shebang) of + true -> + ?line run(Dir, InputArgs, [Expected]); + false -> + ?line run_with_opts(Dir, [], InputArgs, [Expected]) + end, + + %% Verify + ?line {ok, Bin} = escript:create(binary, Sections), + ?line {ok, Read} = file:read_file(File), + ?line Bin = Read, % Assert + + Normalized = normalize_sections(Sections), + ?line {ok, Extracted} = escript:extract(File, []), + io:format("Normalized; ~p\n", [Normalized]), + io:format("Extracted ; ~p\n", [Extracted]), + ?line Normalized = Extracted, % Assert + ok. + +normalize_sections(Sections) -> + AtomToTuple = + fun(Val) -> + if + is_atom(Val) -> {Val, default}; + true -> Val + end + end, + case lists:map(AtomToTuple, [{K, V} || {K, V} <- Sections, V =/= undefined]) of + [{shebang, Shebang} | Rest] -> + [{shebang, Shebang} | + case Rest of + [{comment, Comment} | Rest2] -> + [{comment, Comment} | + case Rest2 of + [{emu_args, EmuArgs}, Body] -> + [{emu_args, EmuArgs}, Body]; + [Body] -> + [{emu_args, undefined}, Body] + end + ]; + [{emu_args, EmuArgs}, Body] -> + [{comment, undefined}, {emu_args, EmuArgs}, Body]; + [Body] -> + [{comment, undefined}, {emu_args, undefined}, Body] + end + ]; + [Body] -> + [{shebang, undefined}, {comment, undefined}, {emu_args, undefined}, Body] + end. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +foldl(Config) when is_list(Config) -> + {NewFile, _FileInfo, + _EmuArg, _Source, + ErlBase, ErlCode, + BeamBase, _BeamCode, + ArchiveBin} = + prepare_creation("foldl", Config), + + Collect = fun(Name, GetInfo, GetBin, Acc) -> + [{Name, GetInfo(), GetBin()} | Acc] + end, + + %% Get line numbers and the file attribute right + SourceFile = NewFile ++ ".erl", + <<_:1/binary, ErlCode2/binary>> = ErlCode, + ?line ok = file:write_file(SourceFile, ErlCode2), + ?line {ok, _Mod, BeamCode} = + compile:file(SourceFile, [binary, debug_info]), + + %% Verify source script + ?line ok = escript:create(SourceFile, [{source, ErlCode}]), + ?line {ok, [{".", _, BeamCode2}]} + = escript_foldl(Collect, [], SourceFile), + + ?line {ok, Abstr} = beam_lib:chunks(BeamCode, [abstract_code]), + ?line {ok, Abstr2} = beam_lib:chunks(BeamCode2, [abstract_code]), + %% io:format("abstr1=~p\n", [Abstr]), + %% io:format("abstr2=~p\n", [Abstr2]), + ?line Abstr = Abstr2, % Assert + + %% Verify beam script + ?line ok = escript:create(NewFile, [{beam, BeamCode}]), + ?line {ok, [{".", _, BeamCode}]} + = escript_foldl(Collect, [], NewFile), + + %% Verify archive scripts + ?line ok = escript:create(NewFile, [{archive, ArchiveBin}]), + ?line {ok, [{BeamBase, #file_info{}, _}, + {ErlBase, #file_info{}, _}]} + = escript_foldl(Collect, [], NewFile), + + ArchiveFiles = [{ErlBase, ErlCode}, {BeamBase, BeamCode}], + ?line ok = escript:create(NewFile, [{archive, ArchiveFiles, []}]), + ?line {ok, [{BeamBase, _, _}, + {ErlBase, _, _}]} + = escript_foldl(Collect, [], NewFile), + + ok. + +escript_foldl(Fun, Acc, File) -> + code:ensure_loaded(zip), + case erlang:function_exported(zip, foldl, 3) of + true -> + emulate_escript_foldl(Fun, Acc, File); + false -> + escript:foldl(Fun, Acc, File) + end. + +emulate_escript_foldl(Fun, Acc, File) -> + case escript:extract(File, [compile_source]) of + {ok, [_Shebang, _Comment, _EmuArgs, Body]} -> + case Body of + {source, BeamCode} -> + GetInfo = fun() -> file:read_file_info(File) end, + GetBin = fun() -> BeamCode end, + {ok, Fun(".", GetInfo, GetBin, Acc)}; + {beam, BeamCode} -> + GetInfo = fun() -> file:read_file_info(File) end, + GetBin = fun() -> BeamCode end, + {ok, Fun(".", GetInfo, GetBin, Acc)}; + {archive, ArchiveBin} -> + zip:foldl(Fun, Acc, {File, ArchiveBin}) + end; + {error, Reason} -> + {error, Reason} + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +overflow(Config) when is_list(Config) -> + Data = ?config(data_dir, Config), + Dir = filename:absname(Data), %Get rid of trailing slash. + ?line run(Dir, "arg_overflow", + [<<"ExitCode:0">>]), + ?line run(Dir, "linebuf_overflow", + [<<"ExitCode:0">>]), + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + run(Dir, Cmd0, Expected0) -> Expected = iolist_to_binary(expected_output(Expected0, Dir)), Cmd = case os:type() of @@ -490,7 +765,7 @@ run(Dir, Cmd0, Expected0) -> end, do_run(Dir, Cmd, Expected). -run(Dir, Opts, Cmd0, Expected) -> +run_with_opts(Dir, Opts, Cmd0, Expected) -> Cmd = case os:type() of {win32,_} -> "escript " ++ Opts ++ " " ++ filename:nativename(Dir) ++ "\\" ++ Cmd0; _ -> "escript " ++ Opts ++ " " ++ Dir ++ "/" ++ Cmd0 @@ -533,8 +808,8 @@ expected_output([data_dir|T], Data) -> [filename:nativename(Data)++Slash|expected_output(T, Data)]; expected_output([H|T], Data) -> [H|expected_output(T, Data)]; -expected_output([], _) -> +expected_output([], _) -> []; -expected_output(Bin, _) when is_binary(Bin) -> +expected_output(Bin, _) when is_binary(Bin) -> Bin. diff --git a/lib/stdlib/test/escript_SUITE_data/arg_overflow b/lib/stdlib/test/escript_SUITE_data/arg_overflow new file mode 100755 index 0000000000..dd5accc051 --- /dev/null +++ b/lib/stdlib/test/escript_SUITE_data/arg_overflow @@ -0,0 +1,5 @@ +#! /usr/bin/env escript +%% -*- erlang -*- +%%!x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x +main(_) -> + halt(0). diff --git a/lib/stdlib/test/escript_SUITE_data/linebuf_overflow b/lib/stdlib/test/escript_SUITE_data/linebuf_overflow new file mode 100755 index 0000000000..33133c1ce9 --- /dev/null +++ b/lib/stdlib/test/escript_SUITE_data/linebuf_overflow @@ -0,0 +1,5 @@ +#! /usr/bin/env escript +%% -*- erlang -*- +%%!xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +main(_) -> + halt(0). diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl index 13c87ca005..c9d82ec5f5 100644 --- a/lib/stdlib/test/ets_SUITE.erl +++ b/lib/stdlib/test/ets_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. 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,24 +18,25 @@ %% -module(ets_SUITE). --export([all/1]). --export([new/1,default/1,setbag/1,badnew/1,verybadnew/1,named/1,keypos2/1, +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2]). +-export([default/1,setbag/1,badnew/1,verybadnew/1,named/1,keypos2/1, privacy/1,privacy_owner/2]). --export([insert/1,empty/1,badinsert/1]). --export([lookup/1,time_lookup/1,badlookup/1,lookup_order/1]). --export([delete/1,delete_elem/1,delete_tab/1,delete_large_tab/1, +-export([empty/1,badinsert/1]). +-export([time_lookup/1,badlookup/1,lookup_order/1]). +-export([delete_elem/1,delete_tab/1,delete_large_tab/1, delete_large_named_table/1, evil_delete/1,baddelete/1,match_delete/1,table_leak/1]). -export([match_delete3/1]). -export([firstnext/1,firstnext_concurrent/1]). -export([slot/1]). --export([match/1, match1/1, match2/1, match_object/1, match_object2/1]). --export([misc/1, dups/1, misc1/1, safe_fixtable/1, info/1, tab2list/1]). --export([files/1, tab2file/1, tab2file2/1, tab2file3/1, tabfile_ext1/1, +-export([ match1/1, match2/1, match_object/1, match_object2/1]). +-export([ dups/1, misc1/1, safe_fixtable/1, info/1, tab2list/1]). +-export([ tab2file/1, tab2file2/1, tabfile_ext1/1, tabfile_ext2/1, tabfile_ext3/1, tabfile_ext4/1]). --export([heavy/1, heavy_lookup/1, heavy_lookup_element/1]). --export([lookup_element/1, lookup_element_mult/1]). --export([fold/1]). +-export([ heavy_lookup/1, heavy_lookup_element/1, heavy_concurrent/1]). +-export([ lookup_element_mult/1]). +-export([]). -export([foldl_ordered/1, foldr_ordered/1, foldl/1, foldr/1, fold_empty/1]). -export([t_delete_object/1, t_init_table/1, t_whitebox/1, t_delete_all_objects/1, t_insert_list/1, t_test_ms/1, @@ -59,25 +60,26 @@ -export([otp_7665/1]). -export([meta_wb/1]). -export([grow_shrink/1, grow_pseudo_deleted/1, shrink_pseudo_deleted/1]). --export([meta_smp/1, +-export([ meta_lookup_unnamed_read/1, meta_lookup_unnamed_write/1, meta_lookup_named_read/1, meta_lookup_named_write/1, meta_newdel_unnamed/1, meta_newdel_named/1]). --export([smp_insert/1, smp_fixed_delete/1, smp_unfix_fix/1, smp_select_delete/1, otp_8166/1]). +-export([smp_insert/1, smp_fixed_delete/1, smp_unfix_fix/1, smp_select_delete/1, + otp_8166/1, otp_8732/1]). -export([exit_large_table_owner/1, exit_many_large_table_owner/1, exit_many_tables_owner/1, exit_many_many_tables_owner/1]). -export([write_concurrency/1, heir/1, give_away/1, setopts/1]). --export([bad_table/1]). +-export([bad_table/1, types/1]). --export([init_per_testcase/2, fin_per_testcase/2, end_per_suite/1]). +-export([init_per_testcase/2, end_per_testcase/2]). %% Convenience for manual testing -export([random_test/0]). % internal exports -export([dont_make_worse_sub/0, make_better_sub1/0, make_better_sub2/0]). --export([t_repair_continuation_do/1, default_do/1, t_bucket_disappears_do/1, +-export([t_repair_continuation_do/1, t_bucket_disappears_do/1, select_fail_do/1, whitebox_1/1, whitebox_2/1, t_delete_all_objects_do/1, t_delete_object_do/1, t_init_table_do/1, t_insert_list_do/1, update_element_opts/1, update_element_opts/4, update_element/4, update_element_do/4, @@ -89,10 +91,17 @@ match_delete_do/1, match_delete3_do/1, firstnext_do/1, slot_do/1, match1_do/1, match2_do/1, match_object_do/1, match_object2_do/1, 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 + 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, + ms_tracee_dummy/1, ms_tracee_dummy/2, ms_tracee_dummy/3, ms_tracee_dummy/4 ]). --include("test_server.hrl"). +-export([t_select_reverse/1]). + +-include_lib("test_server/include/test_server.hrl"). + +-define(m(A,B), ?line assert_eq(A,B)). init_per_testcase(Case, Config) -> Seed = {S1,S2,S3} = random:seed0(), %now(), @@ -103,44 +112,80 @@ init_per_testcase(Case, Config) -> Dog=test_server:timetrap(test_server:minutes(20)), [{watchdog, Dog}, {test_case, Case} | Config]. -fin_per_testcase(_Func, Config) -> +end_per_testcase(_Func, Config) -> Dog=?config(watchdog, Config), wait_for_test_procs(true), test_server:timetrap_cancel(Dog). - + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [{group, new}, {group, insert}, {group, lookup}, + {group, delete}, firstnext, firstnext_concurrent, slot, + {group, match}, t_match_spec_run, + {group, lookup_element}, {group, misc}, {group, files}, + {group, heavy}, ordered, ordered_match, + interface_equality, fixtable_next, fixtable_insert, + rename, rename_unnamed, evil_rename, update_element, + update_counter, evil_update_counter, partly_bound, + match_heavy, {group, fold}, member, t_delete_object, + t_init_table, t_whitebox, t_delete_all_objects, + t_insert_list, t_test_ms, t_select_delete, t_ets_dets, + memory, t_select_reverse, t_bucket_disappears, + select_fail, t_insert_new, t_repair_continuation, + otp_5340, otp_6338, otp_6842_select_1000, otp_7665, + otp_8732, meta_wb, grow_shrink, grow_pseudo_deleted, + shrink_pseudo_deleted, {group, meta_smp}, smp_insert, + smp_fixed_delete, smp_unfix_fix, smp_select_delete, + otp_8166, exit_large_table_owner, + exit_many_large_table_owner, exit_many_tables_owner, + exit_many_many_tables_owner, write_concurrency, heir, + give_away, setopts, bad_table, types]. + +groups() -> + [{new, [], + [default, setbag, badnew, verybadnew, named, keypos2, + privacy]}, + {insert, [], [empty, badinsert]}, + {lookup, [], [time_lookup, badlookup, lookup_order]}, + {lookup_element, [], [lookup_element_mult]}, + {delete, [], + [delete_elem, delete_tab, delete_large_tab, + delete_large_named_table, evil_delete, table_leak, + baddelete, match_delete, match_delete3]}, + {match, [], + [match1, match2, match_object, match_object2]}, + {misc, [], + [misc1, safe_fixtable, info, dups, tab2list]}, + {files, [], + [tab2file, tab2file2, tabfile_ext1, + tabfile_ext2, tabfile_ext3, tabfile_ext4]}, + {heavy, [], + [heavy_lookup, heavy_lookup_element, heavy_concurrent]}, + {fold, [], + [foldl_ordered, foldr_ordered, foldl, foldr, + fold_empty]}, + {meta_smp, [], + [meta_lookup_unnamed_read, meta_lookup_unnamed_write, + meta_lookup_named_read, meta_lookup_named_write, + meta_newdel_unnamed, meta_newdel_named]}]. + +init_per_suite(Config) -> + Config. end_per_suite(_Config) -> stop_spawn_logger(), catch erts_debug:set_internal_state(available_internal_state, false). -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. -all(suite) -> - [ - new,insert,lookup,delete,firstnext,firstnext_concurrent,slot,match, - t_match_spec_run, - lookup_element, misc,files, heavy, - ordered, ordered_match, interface_equality, - fixtable_next, fixtable_insert, rename, rename_unnamed, evil_rename, - update_element, update_counter, evil_update_counter, partly_bound, - match_heavy, fold, member, - t_delete_object, t_init_table, t_whitebox, - t_delete_all_objects, t_insert_list, t_test_ms, - t_select_delete, t_ets_dets, memory, - t_bucket_disappears, - select_fail,t_insert_new, t_repair_continuation, otp_5340, otp_6338, - otp_6842_select_1000, otp_7665, - meta_wb, - grow_shrink, grow_pseudo_deleted, shrink_pseudo_deleted, - meta_smp, - smp_insert, smp_fixed_delete, smp_unfix_fix, smp_select_delete, otp_8166, - exit_large_table_owner, - exit_many_large_table_owner, - exit_many_tables_owner, - exit_many_many_tables_owner, - write_concurrency, heir, give_away, setopts, - bad_table - ]. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -153,7 +198,7 @@ t_bucket_disappears(Config) when is_list(Config) -> t_bucket_disappears_do(Opts) -> ?line EtsMem = etsmem(), - ?line ets:new(abcd, [named_table, public, {keypos, 2} | Opts]), + ?line ets_new(abcd, [named_table, public, {keypos, 2} | Opts]), ?line ets:insert(abcd, {abcd,1,2}), ?line ets:insert(abcd, {abcd,2,2}), ?line ets:insert(abcd, {abcd,3,2}), @@ -171,29 +216,180 @@ t_match_spec_run(suite) -> t_match_spec_run(doc) -> ["Check ets:match_spec_run/2."]; t_match_spec_run(Config) when is_list(Config) -> + init_externals(), ?line EtsMem = etsmem(), - ?line [2,3] = ets:match_spec_run([{1},{2},{3}], - ets:match_spec_compile( - [{{'$1'},[{'>','$1',1}],['$1']}])), + + t_match_spec_run_test([{1},{2},{3}], + [{{'$1'},[{'>','$1',1}],['$1']}], + [2,3]), + ?line Huge = [{X} || X <- lists:seq(1,2500)], ?line L = lists:seq(2476,2500), - ?line L = ets:match_spec_run(Huge, - ets:match_spec_compile( - [{{'$1'},[{'>','$1',2475}],['$1']}])), + t_match_spec_run_test(Huge, [{{'$1'},[{'>','$1',2475}],['$1']}], L), + ?line L2 = [{X*16#FFFFFFF} || X <- L], - ?line L2 = ets:match_spec_run(Huge, - ets:match_spec_compile( - [{{'$1'}, - [{'>','$1',2475}], - [{{{'*','$1',16#FFFFFFF}}}]}])), - ?line [500,1000,1500,2000,2500] = - ets:match_spec_run(Huge, - ets:match_spec_compile( - [{{'$1'}, - [{'=:=',{'rem','$1',500},0}], - ['$1']}])), + t_match_spec_run_test(Huge, + [{{'$1'}, [{'>','$1',2475}], [{{{'*','$1',16#FFFFFFF}}}]}], + L2), + + t_match_spec_run_test(Huge, [{{'$1'}, [{'=:=',{'rem','$1',500},0}], ['$1']}], + [500,1000,1500,2000,2500]), + + %% More matching fun with several match clauses and guards, + %% applied to a variety of terms. + Fun = fun(Term) -> + CTerm = {const, Term}, + + N_List = [{Term, "0", "v-element"}, + {"=hidden_node", "0", Term}, + {"0", Term, Term}, + {"something", Term, "something else"}, + {"guard and res", Term, 872346}, + {Term, {'and',Term,'again'}, 3.14}, + {Term, {'and',Term,'again'}, "m&g"}, + {Term, {'and',Term,'again'}, "m&g&r"}, + {[{second,Term}, 'and', "tail"], Term, ['and',"tail"]}], + + N_MS = [{{'$1','$2','$3'}, + [{'=:=','$1',CTerm}, {'=:=','$2',{const,"0"}}], + [{{"Guard only for $1",'$3'}}]}, + + {{'$3','$1','$4'}, + [{'=:=','$3',"=hidden_node"}, {'=:=','$1',{const,"0"}}], + [{{"Result only for $4",'$4'}}]}, + + {{'$2','$1','$1'}, + [{'=:=','$2',{const,"0"}}], + [{{"Match only for $1",'$2'}}]}, + + {{'$2',Term,['$3'|'_']}, + [{is_list,'$2'},{'=:=','$3',$s}], + [{{"Matching term",'$2'}}]}, + + {{'$1','$2',872346}, + [{'=:=','$2',CTerm}, {is_list,'$1'}], + [{{"Guard and result",'$2'}}]}, + + {{'$1', {'and','$1','again'}, '$2'}, + [{is_float,'$2'}], + [{{"Match and result",'$1'}}]}, + + {{'$1', {'and','$1','again'}, '$2'}, + [{'=:=','$1',CTerm}, {'=:=', '$2', "m&g"}], + [{{"Match and guard",'$2'}}]}, + + {{'$1', {'and','$1','again'}, "m&g&r"}, + [{'=:=','$1',CTerm}], + [{{"Match, guard and result",'$1'}}]}, + + {{'$1', '$2', '$3'}, + [{'=:=','$1',[{{second,'$2'}} | '$3']}], + [{{"Building guard"}}]} + ], + + N_Result = [{"Guard only for $1", "v-element"}, + {"Result only for $4", Term}, + {"Match only for $1", "0"}, + {"Matching term","something"}, + {"Guard and result",Term}, + {"Match and result",Term}, + {"Match and guard","m&g"}, + {"Match, guard and result",Term}, + {"Building guard"}], + + F = fun(N_MS_Perm) -> + t_match_spec_run_test(N_List, N_MS_Perm, N_Result) + end, + repeat_for_permutations(F, N_MS) + end, + + test_terms(Fun, skip_refc_check), + ?line verify_etsmem(EtsMem). +t_match_spec_run_test(List, MS, Result) -> + + %%io:format("ms = ~p\n",[MS]), + + ?m(Result, ets:match_spec_run(List, ets:match_spec_compile(MS))), + + %% Check that ets:select agree + Tab = ets:new(xxx, [bag]), + ets:insert(Tab, List), + SRes = lists:sort(Result), + ?m(SRes, lists:sort(ets:select(Tab, MS))), + ets:delete(Tab), + + %% Check that tracing agree + Self = self(), + {Tracee, MonRef} = spawn_monitor(fun() -> ms_tracee(Self, List) end), + receive {Tracee, ready} -> ok end, + + MST = lists:map(fun(Clause) -> ms_clause_ets_to_trace(Clause) end, MS), + + %%io:format("MS = ~p\nMST= ~p\n",[MS,MST]), + + erlang:trace_pattern({?MODULE,ms_tracee_dummy,'_'}, MST , [local]), + erlang:trace(Tracee, true, [call]), + Tracee ! start, + TRes = ms_tracer_collect(Tracee, MonRef, []), + %erlang:trace(Tracee, false, [call]), + %Tracee ! stop, + case TRes of + SRes -> ok; + _ -> + io:format("TRACE MATCH FAILED\n"), + io:format("Input = ~p\nMST = ~p\nExpected = ~p\nGot = ~p\n", [List, MST, SRes, TRes]), + ?t:fail("TRACE MATCH FAILED") + end, + ok. + + + +ms_tracer_collect(Tracee, Ref, Acc) -> + receive + {trace, Tracee, call, _Args, [Msg]} -> + %io:format("trace Args=~p Msg=~p\n", [_Args, Msg]), + ms_tracer_collect(Tracee, Ref, [Msg | Acc]); + + {'DOWN', Ref, process, Tracee, _} -> + %io:format("monitor DOWN for ~p\n", [Tracee]), + TDRef = erlang:trace_delivered(Tracee), + ms_tracer_collect(Tracee, TDRef, Acc); + + {trace_delivered, Tracee, Ref} -> + %%io:format("trace delivered for ~p\n", [Tracee]), + lists:sort(Acc); + + Other -> + io:format("Unexpected message = ~p\n", [Other]), + ?t:fail("Unexpected tracer msg") + end. + + +ms_tracee(Parent, CallArgList) -> + %io:format("ms_tracee ~p started with ArgList = ~p\n", [self(), CallArgList]), + Parent ! {self(), ready}, + receive start -> ok end, + lists:foreach(fun(Args) -> + erlang:apply(?MODULE, ms_tracee_dummy, tuple_to_list(Args)) + end, CallArgList). + %%receive stop -> ok end. + + + +ms_tracee_dummy(_) -> ok. +ms_tracee_dummy(_,_) -> ok. +ms_tracee_dummy(_,_,_) -> ok. +ms_tracee_dummy(_,_,_,_) -> ok. + +ms_clause_ets_to_trace({Head, Guard, Body}) -> + {tuple_to_list(Head), Guard, [{message, Body}]}. + +assert_eq(A,A) -> ok; +assert_eq(A,B) -> + io:format("FAILED MATCH:\n~p\n =/=\n~p\n",[A,B]), + ?t:fail("assert_eq failed"). t_repair_continuation(suite) -> @@ -209,7 +405,7 @@ t_repair_continuation_do(Opts) -> ?line MS = [{'_',[],[true]}], ?line MS2 = [{{{'$1','_'},'_'},[],['$1']}], (fun() -> - ?line T = ets:new(x,[ordered_set|Opts]), + ?line T = ets_new(x,[ordered_set|Opts]), ?line F = fun(0,_)->ok;(N,F) -> ets:insert(T,{N,N}), F(N-1,F) end, ?line F(1000,F), ?line {_,C} = ets:select(T,MS,5), @@ -221,7 +417,7 @@ t_repair_continuation_do(Opts) -> ?line true = ets:delete(T) end)(), (fun() -> - ?line T = ets:new(x,[ordered_set|Opts]), + ?line T = ets_new(x,[ordered_set|Opts]), ?line F = fun(0,_)->ok;(N,F) -> ets:insert(T,{N,N}), F(N-1,F) end, ?line F(1000,F), ?line {_,C} = ets:select(T,MS,1001), @@ -233,7 +429,7 @@ t_repair_continuation_do(Opts) -> end)(), (fun() -> - ?line T = ets:new(x,[ordered_set|Opts]), + ?line T = ets_new(x,[ordered_set|Opts]), ?line F = fun(0,_)->ok;(N,F) -> ets:insert(T,{integer_to_list(N),N}), F(N-1,F) @@ -248,7 +444,7 @@ t_repair_continuation_do(Opts) -> ?line true = ets:delete(T) end)(), (fun() -> - ?line T = ets:new(x,[ordered_set|Opts]), + ?line T = ets_new(x,[ordered_set|Opts]), ?line F = fun(0,_)->ok;(N,F) -> ets:insert(T,{{integer_to_list(N),N},N}), F(N-1,F) @@ -264,7 +460,7 @@ t_repair_continuation_do(Opts) -> end)(), (fun() -> - ?line T = ets:new(x,[set|Opts]), + ?line T = ets_new(x,[set|Opts]), ?line F = fun(0,_)->ok;(N,F) -> ets:insert(T,{N,N}), F(N-1,F) @@ -279,7 +475,7 @@ t_repair_continuation_do(Opts) -> ?line true = ets:delete(T) end)(), (fun() -> - ?line T = ets:new(x,[set|Opts]), + ?line T = ets_new(x,[set|Opts]), ?line F = fun(0,_)->ok;(N,F) -> ets:insert(T,{integer_to_list(N),N}), F(N-1,F) @@ -294,7 +490,7 @@ t_repair_continuation_do(Opts) -> ?line true = ets:delete(T) end)(), (fun() -> - ?line T = ets:new(x,[bag|Opts]), + ?line T = ets_new(x,[bag|Opts]), ?line F = fun(0,_)->ok;(N,F) -> ets:insert(T,{integer_to_list(N),N}), F(N-1,F) @@ -309,7 +505,7 @@ t_repair_continuation_do(Opts) -> ?line true = ets:delete(T) end)(), (fun() -> - ?line T = ets:new(x,[duplicate_bag|Opts]), + ?line T = ets_new(x,[duplicate_bag|Opts]), ?line F = fun(0,_)->ok;(N,F) -> ets:insert(T,{integer_to_list(N),N}), F(N-1,F) @@ -327,21 +523,22 @@ t_repair_continuation_do(Opts) -> ?line true = ets:is_compiled_ms(ets:match_spec_compile(MS)), ?line verify_etsmem(EtsMem). -new(suite) -> [default,setbag,badnew,verybadnew,named,keypos2,privacy]. default(doc) -> - ["Test case to check that a new ets table is defined as a `set' and " - "`protected'"]; + ["Check correct default vaules of a new ets table"]; default(suite) -> []; default(Config) when is_list(Config) -> %% Default should be set,protected - repeat_for_opts(default_do). - -default_do(Opts) -> ?line EtsMem = etsmem(), - ?line Def = ets:new(def,Opts), + ?line Def = ets_new(def,[]), ?line set = ets:info(Def,type), ?line protected = ets:info(Def,protection), + Compressed = erlang:system_info(ets_always_compress), + ?line Compressed = ets:info(Def,compressed), + Self = self(), + ?line Self = ets:info(Def,owner), + ?line none = ets:info(Def, heir), + ?line false = ets:info(Def,named_table), ?line ets:delete(Def), ?line verify_etsmem(EtsMem). @@ -355,7 +552,7 @@ select_fail(Config) when is_list(Config) -> ?line verify_etsmem(EtsMem). select_fail_do(Opts) -> - ?line T = ets:new(x,Opts), + ?line T = ets_new(x,Opts), ?line ets:insert(T,{a,a}), ?line case (catch ets:select(T,[{{a,'_'},[],[{snuffla}]}])) of @@ -378,20 +575,27 @@ select_fail_do(Opts) -> -define(S(T),ets:info(T,memory)). -define(TAB_STRUCT_SZ, erts_debug:get_internal_state('DbTable_words')). --define(NORMAL_TAB_STRUCT_SZ, 26). %% SunOS5.8, 32-bit, non smp, private heap +%%-define(NORMAL_TAB_STRUCT_SZ, 26). %% SunOS5.8, 32-bit, non smp, private heap %% %% The hardcoded expected memory sizes (in words) are the ones we expect on: %% SunOS5.8, 32-bit, non smp, private heap %% -memory(doc) -> - ["Whitebox test of ets:info(X,memory)"]; -memory(suite) -> - []; +memory(doc) -> ["Whitebox test of ets:info(X,memory)"]; +memory(suite) -> []; memory(Config) when is_list(Config) -> ?line erts_debug:set_internal_state(available_internal_state, true), ?line ok = chk_normal_tab_struct_size(), - ?line L = [T1,T2,T3,T4] = fill_sets_int(1000), - ?line XRes1 = adjust_xmem(L, {16862,16072,16072,16078}), + repeat_for_opts(memory_do,[compressed]), + ?line catch erts_debug:set_internal_state(available_internal_state, false). + +memory_do(Opts) -> + ?line L = [T1,T2,T3,T4] = fill_sets_int(1000,Opts), + XR1 = case mem_mode(T1) of + {normal,_} -> {13836,13046,13046,13052}; %{13862,13072,13072,13078}; + {compressed,4} -> {11041,10251,10251,10252}; %{11067,10277,10277,10278}; + {compressed,8} -> {10050,9260,9260,9260} %{10076,9286,9286,9286} + end, + ?line XRes1 = adjust_xmem(L, XR1), ?line Res1 = {?S(T1),?S(T2),?S(T3),?S(T4)}, ?line lists:foreach(fun(T) -> Before = ets:info(T,size), @@ -402,7 +606,12 @@ memory(Config) when is_list(Config) -> [Key, ets:info(T,type), Before, ets:info(T,size), Objs]) end, L), - ?line XRes2 = adjust_xmem(L, {16849,16060,16048,16054}), + XR2 = case mem_mode(T1) of + {normal,_} -> {13826,13037,13028,13034}; %{13852,13063,13054,13060}; + {compressed,4} -> {11031,10242,10233,10234}; %{11057,10268,10259,10260}; + {compressed,8} -> {10040,9251,9242,9242} %10066,9277,9268,9268} + end, + ?line XRes2 = adjust_xmem(L, XR2), ?line Res2 = {?S(T1),?S(T2),?S(T3),?S(T4)}, ?line lists:foreach(fun(T) -> Before = ets:info(T,size), @@ -413,13 +622,18 @@ memory(Config) when is_list(Config) -> [Key, ets:info(T,type), Before, ets:info(T,size), Objs]) end, L), - ?line XRes3 = adjust_xmem(L, {16836,16048,16024,16030}), + XR3 = case mem_mode(T1) of + {normal,_} -> {13816,13028,13010,13016}; %{13842,13054,13036,13042}; + {compressed,4} -> {11021,10233,10215,10216}; %{11047,10259,10241,10242}; + {compressed,8} -> {10030,9242,9224,9224} %{10056,9268,9250,9250} + end, + ?line XRes3 = adjust_xmem(L, XR3), ?line Res3 = {?S(T1),?S(T2),?S(T3),?S(T4)}, ?line lists:foreach(fun(T) -> ?line ets:delete_all_objects(T) end, L), - ?line XRes4 = adjust_xmem(L, {76,286,286,286}), + ?line XRes4 = adjust_xmem(L, {50,260,260,260}), %{76,286,286,286}), ?line Res4 = {?S(T1),?S(T2),?S(T3),?S(T4)}, lists:foreach(fun(T) -> ?line ets:delete(T) @@ -430,9 +644,9 @@ memory(Config) when is_list(Config) -> ?line ets:select_delete(T,[{'_',[],[true]}]) end, L2), - ?line XRes5 = adjust_xmem(L2, {76,286,286,286}), + ?line XRes5 = adjust_xmem(L2, {50,260,260,260}), %{76,286,286,286}), ?line Res5 = {?S(T11),?S(T12),?S(T13),?S(T14)}, - ?line ?t:format("XRes1 = ~p~n" + ?line io:format("XRes1 = ~p~n" " Res1 = ~p~n~n" "XRes2 = ~p~n" " Res2 = ~p~n~n" @@ -452,9 +666,15 @@ memory(Config) when is_list(Config) -> ?line XRes3 = Res3, ?line XRes4 = Res4, ?line XRes5 = Res5, - ?line catch erts_debug:set_internal_state(available_internal_state, false), ?line ok. +mem_mode(T) -> + {case ets:info(T,compressed) of + true -> compressed; + false -> normal + end, + erlang:system_info(wordsize)}. + chk_normal_tab_struct_size() -> ?line System = {os:type(), os:version(), @@ -462,36 +682,58 @@ chk_normal_tab_struct_size() -> erlang:system_info(smp_support), erlang:system_info(heap_type)}, ?line ?t:format("System = ~p~n", [System]), - ?line ?t:format("?NORMAL_TAB_STRUCT_SZ=~p~n", [?NORMAL_TAB_STRUCT_SZ]), + %%?line ?t:format("?NORMAL_TAB_STRUCT_SZ=~p~n", [?NORMAL_TAB_STRUCT_SZ]), ?line ?t:format("?TAB_STRUCT_SZ=~p~n", [?TAB_STRUCT_SZ]), - ?line case System of - {{unix, sunos}, {5, 8, 0}, 4, false, private} -> - ?line ?NORMAL_TAB_STRUCT_SZ = ?TAB_STRUCT_SZ, - ?line ok; - _ -> - ?line ok - end. - -adjust_xmem([T1,T2,T3,T4], {A0,B0,C0,D0} = Mem0) -> + ok. +% ?line case System of +% {{unix, sunos}, {5, 8, 0}, 4, false, private} -> +% ?line ?NORMAL_TAB_STRUCT_SZ = ?TAB_STRUCT_SZ, +% ?line ok; +% _ -> +% ?line ok +% end. + +-define(DB_TREE_STACK_NEED,50). % The static stack for a tree, in halfword pointers are two internal words + % so the stack gets twice as big +-define(DB_HASH_SIZEOF_EXTSEG,260). % The segment size in words, in halfword this will be twice as large. + +adjust_xmem([T1,T2,T3,T4], {A0,B0,C0,D0} = _Mem0) -> %% Adjust for 64-bit, smp, and os: %% Table struct size may differ. - Mem1 = case ?TAB_STRUCT_SZ of - ?NORMAL_TAB_STRUCT_SZ -> - Mem0; - TabStructSz -> - TabDiff = TabStructSz - ?NORMAL_TAB_STRUCT_SZ, - {A0+TabDiff, B0+TabDiff, C0+TabDiff, D0+TabDiff} - end, + +% Mem1 = case ?TAB_STRUCT_SZ of +% ?NORMAL_TAB_STRUCT_SZ -> +% Mem0; +% TabStructSz -> +% TabDiff = TabStructSz - ?NORMAL_TAB_STRUCT_SZ, +% {A0+TabDiff, B0+TabDiff, C0+TabDiff, D0+TabDiff} +% end, + + TabDiff = ?TAB_STRUCT_SZ, + Mem1 = {A0+TabDiff, B0+TabDiff, C0+TabDiff, D0+TabDiff}, + + Mem2 = case {erlang:system_info({wordsize,internal}),erlang:system_info({wordsize,external})} of + %% Halfword, corrections for regular pointers occupying two internal words. + {4,8} -> + {A1,B1,C1,D1} = Mem1, + {A1+4*ets:info(T1, size)+?DB_TREE_STACK_NEED, + B1+3*ets:info(T2, size)+?DB_HASH_SIZEOF_EXTSEG, + C1+3*ets:info(T3, size)+?DB_HASH_SIZEOF_EXTSEG, + D1+3*ets:info(T4, size)+?DB_HASH_SIZEOF_EXTSEG}; + _ -> + Mem1 + end, + %% Adjust for hybrid and shared heaps: %% Each record is one word smaller. - Mem2 = case erlang:system_info(heap_type) of - private -> - Mem1; - _ -> - {A1,B1,C1,D1} = Mem1, - {A1-ets:info(T1, size),B1-ets:info(T2, size), - C1-ets:info(T3, size),D1-ets:info(T4, size)} - end, + %%Mem2 = case erlang:system_info(heap_type) of + %% private -> + %% Mem1; + %% _ -> + %% {A1,B1,C1,D1} = Mem1, + %% {A1-ets:info(T1, size),B1-ets:info(T2, size), + %% C1-ets:info(T3, size),D1-ets:info(T4, size)} + %% end, %%{Mem2,{ets:info(T1,stats),ets:info(T2,stats),ets:info(T3,stats),ets:info(T4,stats)}}. Mem2. @@ -510,7 +752,7 @@ t_whitebox(Config) when is_list(Config) -> ?line verify_etsmem(EtsMem). whitebox_1(Opts) -> - ?line T=ets:new(x,[bag | Opts]), + ?line T=ets_new(x,[bag | Opts]), ?line ets:insert(T,[{du,glade},{ta,en}]), ?line ets:insert(T,[{hej,hopp2},{du,glade2},{ta,en2}]), ?line {_,C}=ets:match(T,{ta,'$1'},1), @@ -520,8 +762,8 @@ whitebox_1(Opts) -> ok. whitebox_2(Opts) -> - ?line T=ets:new(x,[ordered_set, {keypos,2} | Opts]), - ?line T2=ets:new(x,[set, {keypos,2}| Opts]), + ?line T=ets_new(x,[ordered_set, {keypos,2} | Opts]), + ?line T2=ets_new(x,[set, {keypos,2}| Opts]), ?line 0 = ets:select_delete(T,[{{hej},[],[true]}]), ?line 0 = ets:select_delete(T,[{{hej,hopp},[],[true]}]), ?line 0 = ets:select_delete(T2,[{{hej},[],[true]}]), @@ -543,7 +785,7 @@ t_ets_dets(Config, Opts) -> ?line (catch file:delete(Fname)), ?line {ok,DTab} = dets:open_file(testdets_1, [{file, Fname}]), - ?line ETab = ets:new(x,Opts), + ?line ETab = ets_new(x,Opts), ?line filltabint(ETab,3000), ?line DTab = ets:to_dets(ETab,DTab), ?line ets:delete_all_objects(ETab), @@ -555,7 +797,7 @@ t_ets_dets(Config, Opts) -> (catch ets:to_dets(ETab,DTab)), ?line {'EXIT',{badarg,[{ets,from_dets,[ETab,DTab]}|_]}} = (catch ets:from_dets(ETab,DTab)), - ?line ETab2 = ets:new(x,Opts), + ?line ETab2 = ets_new(x,Opts), ?line filltabint(ETab2,3000), ?line dets:close(DTab), ?line {'EXIT',{badarg,[{ets,to_dets,[ETab2,DTab]}|_]}} = @@ -576,7 +818,7 @@ t_delete_all_objects(Config) when is_list(Config) -> ?line verify_etsmem(EtsMem). t_delete_all_objects_do(Opts) -> - ?line T=ets:new(x,Opts), + ?line T=ets_new(x,Opts), ?line filltabint(T,4000), ?line O=ets:first(T), ?line ets:next(T,O), @@ -605,7 +847,7 @@ t_delete_object(Config) when is_list(Config) -> ?line verify_etsmem(EtsMem). t_delete_object_do(Opts) -> - ?line T = ets:new(x,Opts), + ?line T = ets_new(x,Opts), ?line filltabint(T,4000), ?line del_one_by_one_set(T,1,4001), ?line filltabint(T,4000), @@ -622,19 +864,19 @@ t_delete_object_do(Opts) -> ?line 3999 = ets:info(T,size), ?line 0 = ets:info(T,kept_objects), ?line ets:delete(T), - ?line T1 = ets:new(x,[ordered_set | Opts]), + ?line T1 = ets_new(x,[ordered_set | Opts]), ?line filltabint(T1,4000), ?line del_one_by_one_set(T1,1,4001), ?line filltabint(T1,4000), ?line del_one_by_one_set(T1,4000,0), ?line ets:delete(T1), - ?line T2 = ets:new(x,[bag | Opts]), + ?line T2 = ets_new(x,[bag | Opts]), ?line filltabint2(T2,4000), ?line del_one_by_one_bag(T2,1,4001), ?line filltabint2(T2,4000), ?line del_one_by_one_bag(T2,4000,0), ?line ets:delete(T2), - ?line T3 = ets:new(x,[duplicate_bag | Opts]), + ?line T3 = ets_new(x,[duplicate_bag | Opts]), ?line filltabint3(T3,4000), ?line del_one_by_one_dbag_1(T3,1,4001), ?line filltabint3(T3,4000), @@ -681,7 +923,7 @@ t_init_table(Config) when is_list(Config)-> ?line verify_etsmem(EtsMem). t_init_table_do(Opts) -> - ?line T = ets:new(x,[duplicate_bag | Opts]), + ?line T = ets_new(x,[duplicate_bag | Opts]), ?line filltabint(T,4000), ?line ets:init_table(T, make_init_fun(1)), ?line del_one_by_one_dbag_1(T,4000,0), @@ -763,7 +1005,7 @@ t_insert_list(Config) when is_list(Config) -> ?line verify_etsmem(EtsMem). t_insert_list_do(Opts) -> - ?line T = ets:new(x,[duplicate_bag | Opts]), + ?line T = ets_new(x,[duplicate_bag | Opts]), ?line do_fill_dbag_using_lists(T,4000), ?line del_one_by_one_dbag_2(T,4000,0), ?line ets:delete(T). @@ -786,6 +1028,67 @@ t_test_ms(Config) when is_list(Config) -> ?line true = (if is_list(String) -> true; true -> false end), ?line verify_etsmem(EtsMem). +t_select_reverse(doc) -> + ["Test the select reverse BIF's"]; +t_select_reverse(suite) -> + []; +t_select_reverse(Config) when is_list(Config) -> + ?line Table = ets_new(xxx, [ordered_set]), + ?line filltabint(Table,1000), + ?line A = lists:reverse(ets:select(Table,[{{'$1', '_'}, + [{'>', + {'rem', + '$1', 5}, + 2}], + ['$_']}])), + ?line A = ets:select_reverse(Table,[{{'$1', '_'}, + [{'>', + {'rem', + '$1', 5}, + 2}], + ['$_']}]), + ?line A = reverse_chunked(Table,[{{'$1', '_'}, + [{'>', + {'rem', + '$1', 5}, + 2}], + ['$_']}],3), + % A set/bag/duplicate_bag should get the same result regardless + % of select or select_reverse + ?line Table2 = ets_new(xxx, [set]), + ?line filltabint(Table2,1000), + ?line Table3 = ets_new(xxx, [bag]), + ?line filltabint(Table3,1000), + ?line Table4 = ets_new(xxx, [duplicate_bag]), + ?line filltabint(Table4,1000), + ?line lists:map(fun(Tab) -> + B = ets:select(Tab,[{{'$1', '_'}, + [{'>', + {'rem', + '$1', 5}, + 2}], + ['$_']}]), + B = ets:select_reverse(Tab,[{{'$1', '_'}, + [{'>', + {'rem', + '$1', 5}, + 2}], + ['$_']}]) + end,[Table2, Table3, Table4]), + ok. + + + +reverse_chunked(T,MS,N) -> + do_reverse_chunked(ets:select_reverse(T,MS,N),[]). + +do_reverse_chunked('$end_of_table',Acc) -> + lists:reverse(Acc); +do_reverse_chunked({L,C},Acc) -> + NewAcc = lists:reverse(L)++Acc, + do_reverse_chunked(ets:select_reverse(C), NewAcc). + + t_select_delete(doc) -> ["Test the ets:select_delete/2 and ets:select_count/2 BIF's"]; t_select_delete(suite) -> @@ -1064,8 +1367,8 @@ random_test() -> do_random_test() -> ?line EtsMem = etsmem(), - ?line OrdSet = ets:new(xxx,[ordered_set]), - ?line Set = ets:new(xxx,[]), + ?line OrdSet = ets_new(xxx,[ordered_set]), + ?line Set = ets_new(xxx,[]), ?line do_n_times(fun() -> ?line Key = create_random_string(25), ?line Value = create_random_tuple(25), @@ -1269,8 +1572,8 @@ update_element_opts(Opts) -> update_element_opts(Tuple,KeyPos,UpdPos,Opts) -> - Set = ets:new(set,[{keypos,KeyPos} | Opts]), - OrdSet = ets:new(ordered_set,[ordered_set,{keypos,KeyPos} | Opts]), + Set = ets_new(set,[{keypos,KeyPos} | Opts]), + OrdSet = ets_new(ordered_set,[ordered_set,{keypos,KeyPos} | Opts]), update_element(Set,Tuple,KeyPos,UpdPos), update_element(OrdSet,Tuple,KeyPos,UpdPos), true = ets:delete(Set), @@ -1278,7 +1581,7 @@ update_element_opts(Tuple,KeyPos,UpdPos,Opts) -> ok. update_element(T,Tuple,KeyPos,UpdPos) -> - KeyList = [Key || Key <- lists:seq(1,100)], + KeyList = [17,"seventeen",<<"seventeen">>,{17},list_to_binary(lists:seq(1,100)),make_ref(), self()], lists:foreach(fun(Key) -> TupleWithKey = setelement(KeyPos,Tuple,Key), update_element_do(T,TupleWithKey,Key,UpdPos) @@ -1292,6 +1595,8 @@ update_element_do(Tab,Tuple,Key,UpdPos) -> % This will try all combinations of {fromValue,toValue} % % IMPORTANT: size(Values) must be a prime number for this to work!!! + + %io:format("update_element_do for key=~p\n",[Key]), Big32 = 16#12345678, Big64 = 16#123456789abcdef0, Values = { 623, -27, 0, Big32, -Big32, Big64, -Big64, Big32*Big32, @@ -1312,14 +1617,6 @@ update_element_do(Tab,Tuple,Key,UpdPos) -> (ToIx, [], Pos, _Rand, _MeF) -> {Pos, element(ToIx+1,Values)} % single {pos,value} arg end, - - NewTupleF = fun({Pos,Val}, Tpl, _MeF) -> - setelement(Pos, Tpl, Val); - ([{Pos,Val} | Tail], Tpl, MeF) -> - MeF(Tail,setelement(Pos, Tpl, Val),MeF); - ([], Tpl, _MeF) -> - Tpl - end, UpdateF = fun(ToIx,Rand) -> PosValArg = PosValArgF(ToIx,[],UpdPos,Rand,PosValArgF), @@ -1327,7 +1624,7 @@ update_element_do(Tab,Tuple,Key,UpdPos) -> ArgHash = erlang:phash2({Tab,Key,PosValArg}), ?line true = ets:update_element(Tab, Key, PosValArg), ?line ArgHash = erlang:phash2({Tab,Key,PosValArg}), - NewTuple = NewTupleF(PosValArg,Tuple,NewTupleF), + NewTuple = update_tuple(PosValArg,Tuple), ?line [NewTuple] = ets:lookup(Tab,Key) end, @@ -1355,9 +1652,18 @@ update_element_do(Tab,Tuple,Key,UpdPos) -> ?line Checksum = (Length-1)*Length*(Length+1) div 2, % if Length is a prime ok. +update_tuple({Pos,Val}, Tpl) -> + setelement(Pos, Tpl, Val); +update_tuple([{Pos,Val} | Tail], Tpl) -> + update_tuple(Tail,setelement(Pos, Tpl, Val)); +update_tuple([], Tpl) -> + Tpl. + + + update_element_neg(Opts) -> - Set = ets:new(set,Opts), - OrdSet = ets:new(ordered_set,[ordered_set | Opts]), + Set = ets_new(set,Opts), + OrdSet = ets_new(ordered_set,[ordered_set | Opts]), update_element_neg_do(Set), update_element_neg_do(OrdSet), ets:delete(Set), @@ -1365,8 +1671,8 @@ update_element_neg(Opts) -> ets:delete(OrdSet), ?line {'EXIT',{badarg,_}} = (catch ets:update_element(OrdSet,key,{2,1})), - ?line Bag = ets:new(bag,[bag | Opts]), - ?line DBag = ets:new(duplicate_bag,[duplicate_bag | Opts]), + ?line Bag = ets_new(bag,[bag | Opts]), + ?line DBag = ets_new(duplicate_bag,[duplicate_bag | Opts]), ?line {'EXIT',{badarg,_}} = (catch ets:update_element(Bag,key,{2,1})), ?line {'EXIT',{badarg,_}} = (catch ets:update_element(DBag,key,{2,1})), true = ets:delete(Bag), @@ -1416,8 +1722,8 @@ update_counter(Config) when is_list(Config) -> ?line verify_etsmem(EtsMem). update_counter_do(Opts) -> - Set = ets:new(set,Opts), - OrdSet = ets:new(ordered_set,[ordered_set | Opts]), + Set = ets_new(set,Opts), + OrdSet = ets_new(ordered_set,[ordered_set | Opts]), update_counter_for(Set), update_counter_for(OrdSet), ets:delete(Set), @@ -1438,6 +1744,7 @@ update_counter_for(T) -> (Obj, Times, Arg3, Myself) -> ?line {NewObj, Ret} = uc_mimic(Obj,Arg3), ArgHash = erlang:phash2({T,a,Arg3}), + %%io:format("update_counter(~p, ~p, ~p) expecting ~p\n",[T,a,Arg3,Ret]), ?line Ret = ets:update_counter(T,a,Arg3), ?line ArgHash = erlang:phash2({T,a,Arg3}), %%io:format("NewObj=~p~n ",[NewObj]), @@ -1563,8 +1870,8 @@ uc_adder(Init, {_Pos, Add, Thres, Warp}) -> end. update_counter_neg(Opts) -> - Set = ets:new(set,Opts), - OrdSet = ets:new(ordered_set,[ordered_set | Opts]), + Set = ets_new(set,Opts), + OrdSet = ets_new(ordered_set,[ordered_set | Opts]), update_counter_neg_for(Set), update_counter_neg_for(OrdSet), ets:delete(Set), @@ -1572,8 +1879,8 @@ update_counter_neg(Opts) -> ets:delete(OrdSet), ?line {'EXIT',{badarg,_}} = (catch ets:update_counter(OrdSet,key,1)), - ?line Bag = ets:new(bag,[bag | Opts]), - ?line DBag = ets:new(duplicate_bag,[duplicate_bag | Opts]), + ?line Bag = ets_new(bag,[bag | Opts]), + ?line DBag = ets_new(duplicate_bag,[duplicate_bag | Opts]), ?line {'EXIT',{badarg,_}} = (catch ets:update_counter(Bag,key,1)), ?line {'EXIT',{badarg,_}} = (catch ets:update_counter(DBag,key,1)), true = ets:delete(Bag), @@ -1646,7 +1953,7 @@ wait_for_all(Pids0) -> end. evil_counter(I,Opts) -> - T = ets:new(a, Opts), + T = ets_new(a, Opts), Start0 = case I rem 3 of 0 -> 16#12345678; 1 -> 16#12345678FFFFFFFF; @@ -1654,7 +1961,7 @@ evil_counter(I,Opts) -> end, Start = Start0 + random:uniform(100000), ets:insert(T, {dracula,Start}), - Iter = 90000, + Iter = 40000, End = Start + Iter, End = evil_counter_1(Iter, T), ets:delete(T). @@ -1675,7 +1982,7 @@ fixtable_next(Config) when is_list(Config) -> fixtable_next_do(Opts) -> ?line EtsMem = etsmem(), - ?line do_fixtable_next(ets:new(set,[public | Opts])), + ?line do_fixtable_next(ets_new(set,[public | Opts])), ?line verify_etsmem(EtsMem). do_fixtable_next(Tab) -> @@ -1756,24 +2063,24 @@ write_concurrency(doc) -> ["The 'write_concurrency' option"]; write_concurrency(suite) -> []; write_concurrency(Config) when is_list(Config) -> ?line EtsMem = etsmem(), - Yes1 = ets:new(foo,[public,{write_concurrency,true}]), - Yes2 = ets:new(foo,[protected,{write_concurrency,true}]), - No1 = ets:new(foo,[private,{write_concurrency,true}]), + Yes1 = ets_new(foo,[public,{write_concurrency,true}]), + Yes2 = ets_new(foo,[protected,{write_concurrency,true}]), + No1 = ets_new(foo,[private,{write_concurrency,true}]), - Yes3 = ets:new(foo,[bag,public,{write_concurrency,true}]), - Yes4 = ets:new(foo,[bag,protected,{write_concurrency,true}]), - No2 = ets:new(foo,[bag,private,{write_concurrency,true}]), + Yes3 = ets_new(foo,[bag,public,{write_concurrency,true}]), + Yes4 = ets_new(foo,[bag,protected,{write_concurrency,true}]), + No2 = ets_new(foo,[bag,private,{write_concurrency,true}]), - Yes5 = ets:new(foo,[duplicate_bag,public,{write_concurrency,true}]), - Yes6 = ets:new(foo,[duplicate_bag,protected,{write_concurrency,true}]), - No3 = ets:new(foo,[duplicate_bag,private,{write_concurrency,true}]), + Yes5 = ets_new(foo,[duplicate_bag,public,{write_concurrency,true}]), + Yes6 = ets_new(foo,[duplicate_bag,protected,{write_concurrency,true}]), + No3 = ets_new(foo,[duplicate_bag,private,{write_concurrency,true}]), - No4 = ets:new(foo,[ordered_set,public,{write_concurrency,true}]), - No5 = ets:new(foo,[ordered_set,protected,{write_concurrency,true}]), - No6 = ets:new(foo,[ordered_set,private,{write_concurrency,true}]), + No4 = ets_new(foo,[ordered_set,public,{write_concurrency,true}]), + No5 = ets_new(foo,[ordered_set,protected,{write_concurrency,true}]), + No6 = ets_new(foo,[ordered_set,private,{write_concurrency,true}]), - No7 = ets:new(foo,[public,{write_concurrency,false}]), - No8 = ets:new(foo,[protected,{write_concurrency,false}]), + No7 = ets_new(foo,[public,{write_concurrency,false}]), + No8 = ets_new(foo,[protected,{write_concurrency,false}]), ?line YesMem = ets:info(Yes1,memory), ?line NoHashMem = ets:info(No1,memory), @@ -1800,10 +2107,10 @@ write_concurrency(Config) when is_list(Config) -> ?line true = YesMem =:= NoHashMem end, - ?line {'EXIT',{badarg,_}} = (catch ets:new(foo,[public,{write_concurrency,foo}])), - ?line {'EXIT',{badarg,_}} = (catch ets:new(foo,[public,{write_concurrency}])), - ?line {'EXIT',{badarg,_}} = (catch ets:new(foo,[public,{write_concurrency,true,foo}])), - ?line {'EXIT',{badarg,_}} = (catch ets:new(foo,[public,write_concurrency])), + ?line {'EXIT',{badarg,_}} = (catch ets_new(foo,[public,{write_concurrency,foo}])), + ?line {'EXIT',{badarg,_}} = (catch ets_new(foo,[public,{write_concurrency}])), + ?line {'EXIT',{badarg,_}} = (catch ets_new(foo,[public,{write_concurrency,true,foo}])), + ?line {'EXIT',{badarg,_}} = (catch ets_new(foo,[public,write_concurrency])), lists:foreach(fun(T) -> ets:delete(T) end, [Yes1,Yes2,Yes3,Yes4,Yes5,Yes6, @@ -1880,7 +2187,7 @@ heir_founder(Master, HeirData, Opts) -> none -> {heir,none}; _ -> {heir, Heir, HeirData} end, - ?line T = ets:new(foo,[named_table, private, HeirTpl | Opts]), + ?line T = ets_new(foo,[named_table, private, HeirTpl | Opts]), ?line true = ets:insert(T,{key,1}), ?line [{key,1}] = ets:lookup(T,key), Self = self(), @@ -1952,7 +2259,7 @@ give_away(Config) when is_list(Config) -> repeat_for_opts(give_away_do). give_away_do(Opts) -> - ?line T = ets:new(foo,[named_table, private | Opts]), + ?line T = ets_new(foo,[named_table, private | Opts]), ?line true = ets:insert(T,{key,1}), ?line [{key,1}] = ets:lookup(T,key), Parent = self(), @@ -1978,7 +2285,7 @@ give_away_do(Opts) -> ?line undefined = ets:info(T), %% Give and then kill receiver to get back - ?line T2 = ets:new(foo,[private | Opts]), + ?line T2 = ets_new(foo,[private | Opts]), ?line true = ets:insert(T2,{key,1}), ?line ets:setopts(T2,{heir,self(),"Som en gummiboll..."}), ?line {Receiver2,Mref2} = spawn_monitor(fun()-> give_away_receiver(T2,Parent) end), @@ -2000,7 +2307,7 @@ give_away_do(Opts) -> ?line give_me = receive_any(), ?line {'EXIT',{badarg,_}} = (catch ets:give_away(T2,ReceiverNeg,"A deleted table")), - ?line T3 = ets:new(foo,[public | Opts]), + ?line T3 = ets_new(foo,[public | Opts]), spawn_link(fun()-> {'EXIT',{badarg,_}} = (catch ets:give_away(T3,ReceiverNeg,"From non owner")), Parent ! done end), @@ -2035,7 +2342,7 @@ setopts(Config) when is_list(Config) -> setopts_do(Opts) -> Self = self(), - ?line T = ets:new(foo,[named_table, private | Opts]), + ?line T = ets_new(foo,[named_table, private | Opts]), ?line none = ets:info(T,heir), Heir = spawn_link(fun()->heir_heir(Self) end), ?line ets:setopts(T,{heir,Heir,"Data"}), @@ -2088,10 +2395,10 @@ bad_table(Config) when is_list(Config) -> bad_table_do(Opts, DummyFile) -> Parent = self(), - {Pid,Mref} = spawn_opt(fun()-> ets:new(priv,[private,named_table | Opts]), - Priv = ets:new(priv,[private | Opts]), - ets:new(prot,[protected,named_table | Opts]), - Prot = ets:new(prot,[protected | Opts]), + {Pid,Mref} = spawn_opt(fun()-> ets_new(priv,[private,named_table | Opts]), + Priv = ets_new(priv,[private | Opts]), + ets_new(prot,[protected,named_table | Opts]), + Prot = ets_new(prot,[protected | Opts]), Parent ! {self(),Priv,Prot}, die_please = receive_any() end, @@ -2149,11 +2456,11 @@ bad_table_do(Opts, DummyFile) -> bad_table_op({Opts,Priv,Prot}, Op) -> %%io:format("Doing Op=~p on ~p's\n",[Op,Type]), - T1 = ets:new(noname,Opts), + T1 = ets_new(noname,Opts), bad_table_call(noname,Op), ets:delete(T1), bad_table_call(T1,Op), - T2 = ets:new(named,[named_table | Opts]), + T2 = ets_new(named,[named_table | Opts]), ets:delete(T2), bad_table_call(named,Op), bad_table_call(T2,Op), @@ -2187,7 +2494,7 @@ rename(Config) when is_list(Config) -> rename_do(Opts) -> ?line EtsMem = etsmem(), - ets:new(foobazz,[named_table, public | Opts]), + ets_new(foobazz,[named_table, public | Opts]), ets:insert(foobazz,{foo,bazz}), ungermanbazz = ets:rename(foobazz,ungermanbazz), {'EXIT',{badarg, _}} = (catch ets:lookup(foobazz,foo)), @@ -2205,7 +2512,7 @@ rename_unnamed(Config) when is_list(Config) -> rename_unnamed_do(Opts) -> ?line EtsMem = etsmem(), - ?line Tab = ets:new(bonkz,[public | Opts]), + ?line Tab = ets_new(bonkz,[public | Opts]), ?line {'EXIT',{badarg, _}} = (catch ets:insert(bonkz,{foo,bazz})), ?line bonkz = ets:info(Tab, name), ?line Tab = ets:rename(Tab, tjabonkz), @@ -2224,7 +2531,7 @@ evil_rename(Config) when is_list(Config) -> evil_rename_1(Old, New, Flags) -> ?line process_flag(trap_exit, true), - ?line Old = ets:new(Old, Flags), + ?line Old = ets_new(Old, Flags), ?line Fixer = fun() -> ets:safe_fixtable(Old, true) end, ?line crazy_fixtable(15000, Fixer), ?line erlang:yield(), @@ -2234,7 +2541,7 @@ evil_rename_1(Old, New, Flags) -> ok. crazy_fixtable(N, Fixer) -> - Dracula = ets:new(count_dracula, [public]), + Dracula = ets_new(count_dracula, [public]), ets:insert(Dracula, {count,0}), SpawnFun = fun() -> Fixer(), @@ -2268,7 +2575,7 @@ evil_creater_destroyer() -> ets:delete(T1). evil_create_fixed_tab() -> - T = ets:new(arne, [public]), + T = ets_new(arne, [public]), ets:safe_fixtable(T, true), T. @@ -2282,8 +2589,8 @@ interface_equality(Config) when is_list(Config) -> interface_equality_do(Opts) -> ?line EtsMem = etsmem(), - ?line Set = ets:new(set,[set | Opts]), - ?line OrderedSet = ets:new(ordered_set,[ordered_set | Opts]), + ?line Set = ets_new(set,[set | Opts]), + ?line OrderedSet = ets_new(ordered_set,[ordered_set | Opts]), ?line F = fun(X,T,FF) -> case X of 0 -> true; _ -> @@ -2362,7 +2669,7 @@ ordered_match_do(Opts) -> FF(X-1,T,FF) end end, - ?line T1 = ets:new(xxx,[ordered_set| Opts]), + ?line T1 = ets_new(xxx,[ordered_set| Opts]), ?line F(3000,T1,F), ?line [[3,3],[3,3],[3,3]] = ets:match(T1, {'_','_','$1','$2',3}), ?line F2 = fun(X,Rem,Res,FF) -> case X of @@ -2400,7 +2707,7 @@ ordered(Config) when is_list(Config) -> ordered_do(Opts) -> ?line EtsMem = etsmem(), - ?line T = ets:new(oset, [ordered_set | Opts]), + ?line T = ets_new(oset, [ordered_set | Opts]), ?line InsList = [ 25,26,27,28, 5,6,7,8, @@ -2461,8 +2768,8 @@ setbag(doc) -> ["Small test case for both set and bag type ets tables."]; setbag(suite) -> []; setbag(Config) when is_list(Config) -> ?line EtsMem = etsmem(), - ?line Set = ets:new(set,[set]), - ?line Bag = ets:new(bag,[bag]), + ?line Set = ets_new(set,[set]), + ?line Bag = ets_new(bag,[bag]), ?line Key = {foo,bar}, %% insert some value @@ -2482,15 +2789,15 @@ setbag(Config) when is_list(Config) -> ?line verify_etsmem(EtsMem). badnew(doc) -> - ["Test case to check proper return values for illegal ets:new() calls."]; + ["Test case to check proper return values for illegal ets_new() calls."]; badnew(suite) -> []; badnew(Config) when is_list(Config) -> ?line EtsMem = etsmem(), - ?line {'EXIT',{badarg,_}} = (catch ets:new(12,[])), - ?line {'EXIT',{badarg,_}} = (catch ets:new({a,b},[])), - ?line {'EXIT',{badarg,_}} = (catch ets:new(name,[foo])), - ?line {'EXIT',{badarg,_}} = (catch ets:new(name,{bag})), - ?line {'EXIT',{badarg,_}} = (catch ets:new(name,bag)), + ?line {'EXIT',{badarg,_}} = (catch ets_new(12,[])), + ?line {'EXIT',{badarg,_}} = (catch ets_new({a,b},[])), + ?line {'EXIT',{badarg,_}} = (catch ets_new(name,[foo])), + ?line {'EXIT',{badarg,_}} = (catch ets_new(name,{bag})), + ?line {'EXIT',{badarg,_}} = (catch ets_new(name,bag)), ?line verify_etsmem(EtsMem). verybadnew(doc) -> @@ -2499,7 +2806,7 @@ verybadnew(doc) -> verybadnew(suite) -> []; verybadnew(Config) when is_list(Config) -> ?line EtsMem = etsmem(), - ?line {'EXIT',{badarg,_}} = (catch ets:new(verybad,[set|protected])), + ?line {'EXIT',{badarg,_}} = (catch ets_new(verybad,[set|protected])), ?line verify_etsmem(EtsMem). named(doc) -> ["Small check to see if named tables work."]; @@ -2576,9 +2883,9 @@ privacy_check(Pub,Prot,Priv) -> ?line [] = ets:lookup(Prot,foo). privacy_owner(Boss, Opts) -> - ets:new(pub, [public,named_table | Opts]), - ets:new(prot,[protected,named_table | Opts]), - ets:new(priv,[private,named_table | Opts]), + ets_new(pub, [public,named_table | Opts]), + ets_new(prot,[protected,named_table | Opts]), + ets_new(priv,[private,named_table | Opts]), Boss ! ok, privacy_owner_loop(Boss). @@ -2605,8 +2912,6 @@ rotate_tuple(Tuple, N) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -insert(doc) -> ["Test proper and improper inserts into a table."]; -insert(suite) -> [empty,badinsert]. empty(doc) -> ["Check lookup in an empty table and lookup of a non-existing key"]; @@ -2616,7 +2921,7 @@ empty(Config) when is_list(Config) -> empty_do(Opts) -> ?line EtsMem = etsmem(), - ?line Tab = ets:new(foo,Opts), + ?line Tab = ets_new(foo,Opts), ?line [] = ets:lookup(Tab,key), ?line true = ets:insert(Tab,{key2,val}), ?line [] = ets:lookup(Tab,key), @@ -2633,10 +2938,10 @@ badinsert_do(Opts) -> ?line EtsMem = etsmem(), ?line {'EXIT',{badarg,_}} = (catch ets:insert(foo,{key,val})), - ?line Tab = ets:new(foo,Opts), + ?line Tab = ets_new(foo,Opts), ?line {'EXIT',{badarg,_}} = (catch ets:insert(Tab,{})), - ?line Tab3 = ets:new(foo,[{keypos,3}| Opts]), + ?line Tab3 = ets_new(foo,[{keypos,3}| Opts]), ?line {'EXIT',{badarg,_}} = (catch ets:insert(Tab3,{a,b})), ?line {'EXIT',{badarg,_}} = (catch ets:insert(Tab,[key,val2])), @@ -2646,8 +2951,6 @@ badinsert_do(Opts) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -lookup(doc) -> ["Some tests for lookups (timing, bad lookups, etc.)."]; -lookup(suite) -> [time_lookup,badlookup,lookup_order]. time_lookup(doc) -> ["Lookup timing."]; time_lookup(suite) -> []; @@ -2660,7 +2963,7 @@ time_lookup(Config) when is_list(Config) -> "~p ets lookups/s",[Values]))}. time_lookup_do(Opts) -> - ?line Tab = ets:new(foo,Opts), + ?line Tab = ets_new(foo,Opts), ?line fill_tab(Tab,foo), ?line ets:insert(Tab,{{a,key},foo}), ?line {Time,_} = ?t:timecall(test_server,do_times, @@ -2675,7 +2978,7 @@ badlookup(suite) -> []; badlookup(Config) when is_list(Config) -> ?line EtsMem = etsmem(), ?line {'EXIT',{badarg,_}} = (catch ets:lookup(foo,key)), - ?line Tab = ets:new(foo,[]), + ?line Tab = ets_new(foo,[]), ?line ets:delete(Tab), ?line {'EXIT',{badarg,_}} = (catch ets:lookup(Tab,key)), ?line verify_etsmem(EtsMem). @@ -2700,7 +3003,7 @@ lookup_order_2(Opts, Fixed) -> Pair = [{A,B},{B,A},{A,C},{C,A},{B,C},{C,B}], Combos = [{D1,D2,D3} || D1<-ABC, D2<-Pair, D3<-Pair], lists:foreach(fun({D1,{D2a,D2b},{D3a,D3b}}) -> - T = ets:new(foo,Opts), + T = ets_new(foo,Opts), case Fixed of true -> ets:safe_fixtable(T,true); false -> ok @@ -2774,8 +3077,6 @@ fill_tab(Tab,Val) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -lookup_element(doc) -> ["Some tests for lookup_element."]; -lookup_element(suite) -> [lookup_element_mult]. lookup_element_mult(doc) -> ["Multiple return elements (OTP-2386)"]; lookup_element_mult(suite) -> []; @@ -2784,10 +3085,12 @@ lookup_element_mult(Config) when is_list(Config) -> lookup_element_mult_do(Opts) -> ?line EtsMem = etsmem(), - ?line T = ets:new(service, [bag, {keypos, 2} | Opts]), + ?line T = ets_new(service, [bag, {keypos, 2} | Opts]), ?line D = lists:reverse(lem_data()), ?line lists:foreach(fun(X) -> ets:insert(T, X) end, D), ?line ok = lem_crash_3(T), + ?line ets:insert(T, {0, "heap_key"}), + ?line ets:lookup_element(T, "heap_key", 2), ?line true = ets:delete(T), ?line verify_etsmem(EtsMem). @@ -2815,11 +3118,6 @@ lem_crash_3(T) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -delete(doc) -> - ["Check delete functionality (proper/improper deletes)"]; -delete(suite) -> - [delete_elem,delete_tab,delete_large_tab,delete_large_named_table,evil_delete, - table_leak,baddelete,match_delete,match_delete3]. delete_elem(doc) -> ["Check delete of an element inserted in a `filled' table."]; @@ -2829,7 +3127,7 @@ delete_elem(Config) when is_list(Config) -> delete_elem_do(Opts) -> ?line EtsMem = etsmem(), - ?line Tab = ets:new(foo,Opts), + ?line Tab = ets_new(foo,Opts), ?line fill_tab(Tab,foo), ?line ets:insert(Tab,{{b,key},foo}), ?line ets:insert(Tab,{{c,key},foo}), @@ -2849,17 +3147,17 @@ delete_tab(Config) when is_list(Config) -> delete_tab_do(Opts) -> Name = foo, ?line EtsMem = etsmem(), - ?line Name = ets:new(Name, [named_table | Opts]), + ?line Name = ets_new(Name, [named_table | Opts]), ?line true = ets:delete(foo), %% The name should be available again. - ?line Name = ets:new(Name, [named_table | Opts]), + ?line Name = ets_new(Name, [named_table | Opts]), ?line true = ets:delete(Name), ?line verify_etsmem(EtsMem). delete_large_tab(doc) -> "Check that ets:delete/1 works and that other processes can run."; delete_large_tab(Config) when is_list(Config) -> - ?line Data = [{erlang:phash2(I, 16#ffffff),I} || I <- lists:seq(1, 500000)], + ?line Data = [{erlang:phash2(I, 16#ffffff),I} || I <- lists:seq(1, 200000)], ?line EtsMem = etsmem(), repeat_for_opts(fun(Opts) -> delete_large_tab_do(Opts,Data) end), ?line verify_etsmem(EtsMem). @@ -2871,7 +3169,7 @@ delete_large_tab_do(Opts,Data) -> delete_large_tab_1(Name, Flags, Data, Fix) -> - ?line Tab = ets:new(Name, Flags), + ?line Tab = ets_new(Name, Flags), ?line ets:insert(Tab, Data), case Fix of @@ -2938,7 +3236,7 @@ delete_large_named_table_do(Opts,Data) -> ?line delete_large_named_table_1(foo_hash, [named_table | Opts], Data, true). delete_large_named_table_1(Name, Flags, Data, Fix) -> - ?line Tab = ets:new(Name, Flags), + ?line Tab = ets_new(Name, Flags), ?line ets:insert(Tab, Data), case Fix of @@ -2951,7 +3249,7 @@ delete_large_named_table_1(Name, Flags, Data, Fix) -> Pid = spawn_link(fun() -> receive {trace,Parent,call,_} -> - ets:new(Name, [named_table]) + ets_new(Name, [named_table]) end end), ?line erlang:trace(self(), true, [call,{tracer,Pid}]), @@ -2985,7 +3283,7 @@ evil_delete_do(Opts,Data) -> evil_delete_not_owner(Name, Flags, Data, Fix) -> io:format("Not owner: ~p, fix = ~p", [Name,Fix]), - ?line Tab = ets:new(Name, [public|Flags]), + ?line Tab = ets_new(Name, [public|Flags]), ?line ets:insert(Tab, Data), case Fix of false -> ok; @@ -3010,7 +3308,7 @@ evil_delete_not_owner(Name, Flags, Data, Fix) -> evil_delete_owner(Name, Flags, Data, Fix) -> ?line Fun = fun() -> - ?line Tab = ets:new(Name, [public|Flags]), + ?line Tab = ets_new(Name, [public|Flags]), ?line ets:insert(Tab, Data), case Fix of false -> ok; @@ -3037,48 +3335,60 @@ exit_large_table_owner(doc) -> exit_large_table_owner(suite) -> []; exit_large_table_owner(Config) when is_list(Config) -> - ?line Data = [{erlang:phash2(I, 16#ffffff),I} || I <- lists:seq(1, 500000)], + %%?line Data = [{erlang:phash2(I, 16#ffffff),I} || I <- lists:seq(1, 500000)], + ?line FEData = fun(Do) -> repeat_while(fun(500000) -> {false,ok}; + (I) -> Do({erlang:phash2(I, 16#ffffff),I}), + {true, I+1} + end, 1) + end, ?line EtsMem = etsmem(), - repeat_for_opts(fun(Opts) -> exit_large_table_owner_do(Opts,Data,Config) end), + repeat_for_opts({exit_large_table_owner_do,{FEData,Config}}), ?line verify_etsmem(EtsMem). -exit_large_table_owner_do(Opts,Data,Config) -> - ?line verify_rescheduling_exit(Config, Data, [named_table | Opts], true, 1, 1), - ?line verify_rescheduling_exit(Config, Data, Opts, false, 1, 1). +exit_large_table_owner_do(Opts,{FEData,Config}) -> + ?line verify_rescheduling_exit(Config, FEData, [named_table | Opts], true, 1, 1), + ?line verify_rescheduling_exit(Config, FEData, Opts, false, 1, 1). exit_many_large_table_owner(doc) -> []; exit_many_large_table_owner(suite) -> []; exit_many_large_table_owner(Config) when is_list(Config) -> - ?line Data = [{erlang:phash2(I, 16#ffffff),I} || I <- lists:seq(1, 500000)], + %%?line Data = [{erlang:phash2(I, 16#ffffff),I} || I <- lists:seq(1, 500000)], + ?line FEData = fun(Do) -> repeat_while(fun(500000) -> {false,ok}; + (I) -> Do({erlang:phash2(I, 16#ffffff),I}), + {true, I+1} + end, 1) + end, ?line EtsMem = etsmem(), - repeat_for_opts(fun(Opts) -> exit_many_large_table_owner_do(Opts,Data,Config) end), + repeat_for_opts(fun(Opts) -> exit_many_large_table_owner_do(Opts,FEData,Config) end), ?line verify_etsmem(EtsMem). -exit_many_large_table_owner_do(Opts,Data,Config) -> - ?line verify_rescheduling_exit(Config, Data, Opts, true, 1, 4), - ?line verify_rescheduling_exit(Config, Data, [named_table | Opts], false, 1, 4). +exit_many_large_table_owner_do(Opts,FEData,Config) -> + ?line verify_rescheduling_exit(Config, FEData, Opts, true, 1, 4), + ?line verify_rescheduling_exit(Config, FEData, [named_table | Opts], false, 1, 4). exit_many_tables_owner(doc) -> []; exit_many_tables_owner(suite) -> []; exit_many_tables_owner(Config) when is_list(Config) -> + NoData = fun(_Do) -> ok end, ?line EtsMem = etsmem(), - ?line verify_rescheduling_exit(Config, [], [named_table], false, 1000, 1), - ?line verify_rescheduling_exit(Config, [], [named_table,{write_concurrency,true}], false, 1000, 1), + ?line verify_rescheduling_exit(Config, NoData, [named_table], false, 1000, 1), + ?line verify_rescheduling_exit(Config, NoData, [named_table,{write_concurrency,true}], false, 1000, 1), ?line verify_etsmem(EtsMem). exit_many_many_tables_owner(doc) -> []; exit_many_many_tables_owner(suite) -> []; exit_many_many_tables_owner(Config) when is_list(Config) -> ?line Data = [{erlang:phash2(I, 16#ffffff),I} || I <- lists:seq(1, 50)], - repeat_for_opts(fun(Opts) -> exit_many_many_tables_owner_do(Opts,Data,Config) end). + ?line FEData = fun(Do) -> lists:foreach(Do, Data) end, + repeat_for_opts(fun(Opts) -> exit_many_many_tables_owner_do(Opts,FEData,Config) end). -exit_many_many_tables_owner_do(Opts,Data,Config) -> - ?line verify_rescheduling_exit(Config, Data, [named_table | Opts], true, 200, 5), - ?line verify_rescheduling_exit(Config, Data, Opts, false, 200, 5), +exit_many_many_tables_owner_do(Opts,FEData,Config) -> + ?line verify_rescheduling_exit(Config, FEData, [named_table | Opts], true, 200, 5), + ?line verify_rescheduling_exit(Config, FEData, Opts, false, 200, 5), ?line wait_for_test_procs(), ?line EtsMem = etsmem(), - ?line verify_rescheduling_exit(Config, Data, Opts, true, 200, 5), - ?line verify_rescheduling_exit(Config, Data, [named_table | Opts], false, 200, 5), + ?line verify_rescheduling_exit(Config, FEData, Opts, true, 200, 5), + ?line verify_rescheduling_exit(Config, FEData, [named_table | Opts], false, 200, 5), ?line verify_etsmem(EtsMem). @@ -3121,7 +3431,7 @@ vre_fix_tables(Tab) -> receive Go -> ok end, ok. -verify_rescheduling_exit(Config, Data, Flags, Fix, NOTabs, NOProcs) -> +verify_rescheduling_exit(Config, ForEachData, Flags, Fix, NOTabs, NOProcs) -> ?line NoFix = 5, ?line TestCase = atom_to_list(?config(test_case, Config)), ?line Parent = self(), @@ -3136,8 +3446,8 @@ verify_rescheduling_exit(Config, Data, Flags, Fix, NOTabs, NOProcs) -> ++ "-" ++ integer_to_list(A) ++ "-" ++ integer_to_list(B) ++ "-" ++ integer_to_list(C)), - Tab = ets:new(Name, Flags), - ets:insert(Tab, Data), + Tab = ets_new(Name, Flags), + ForEachData(fun(Data) -> ets:insert(Tab, Data) end), case Fix of false -> ok; true -> @@ -3145,10 +3455,10 @@ verify_rescheduling_exit(Config, Data, Flags, Fix, NOTabs, NOProcs) -> vre_fix_tables(Tab) end, lists:seq(1,NoFix)), - lists:foreach(fun({K,_}) -> - ets:delete(Tab, K) - end, - Data) + KeyPos = ets:info(Tab,keypos), + ForEachData(fun(Data) -> + ets:delete(Tab, element(KeyPos,Data)) + end) end end, NOTabs), @@ -3195,7 +3505,7 @@ table_leak(Config) when is_list(Config) -> table_leak_1(_,0) -> ok; table_leak_1(Opts,N) -> - ?line T = ets:new(fooflarf, Opts), + ?line T = ets_new(fooflarf, Opts), ?line true = ets:delete(T), table_leak_1(Opts,N-1). @@ -3205,7 +3515,7 @@ baddelete(suite) -> []; baddelete(Config) when is_list(Config) -> ?line EtsMem = etsmem(), ?line {'EXIT',{badarg,_}} = (catch ets:delete(foo)), - ?line Tab = ets:new(foo,[]), + ?line Tab = ets_new(foo,[]), ?line true = ets:delete(Tab), ?line {'EXIT',{badarg,_}} = (catch ets:delete(Tab)), ?line verify_etsmem(EtsMem). @@ -3220,7 +3530,7 @@ match_delete(Config) when is_list(Config) -> match_delete_do(Opts) -> ?line EtsMem = etsmem(), - ?line Tab = ets:new(kad,Opts), + ?line Tab = ets_new(kad,Opts), ?line fill_tab(Tab,foo), ?line ets:insert(Tab,{{c,key},bar}), ?line _ = ets:match_delete(Tab,{'_',foo}), @@ -3264,7 +3574,7 @@ firstnext(Config) when is_list(Config) -> firstnext_do(Opts) -> ?line EtsMem = etsmem(), - ?line Tab = ets:new(foo,Opts), + ?line Tab = ets_new(foo,Opts), ?line [] = firstnext_collect(Tab,ets:first(Tab),[]), ?line fill_tab(Tab,foo), ?line Len = length(ets:tab2list(Tab)), @@ -3287,10 +3597,10 @@ firstnext_concurrent(Config) when is_list(Config) -> [dynamic_go() || _ <- lists:seq(1, 2)], receive after 5000 -> ok - end. + end. ets_init(Tab, N) -> - ets:new(Tab, [named_table,public,ordered_set]), + ets_new(Tab, [named_table,public,ordered_set]), cycle(Tab, lists:seq(1,N+1)). cycle(_Tab, [H|T]) when H > length(T)-> ok; @@ -3323,7 +3633,7 @@ slot(Config) when is_list(Config) -> slot_do(Opts) -> ?line EtsMem = etsmem(), - ?line Tab = ets:new(foo,Opts), + ?line Tab = ets_new(foo,Opts), ?line fill_tab(Tab,foo), ?line Elts = ets:info(Tab,size), ?line Elts = slot_loop(Tab,0,0), @@ -3342,7 +3652,6 @@ slot_loop(Tab,SlotNo,EltsSoFar) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -match(suite) -> [match1, match2, match_object, match_object2]. match1(suite) -> []; match1(Config) when is_list(Config) -> @@ -3350,7 +3659,7 @@ match1(Config) when is_list(Config) -> match1_do(Opts) -> ?line EtsMem = etsmem(), - ?line Tab = ets:new(foo,Opts), + ?line Tab = ets_new(foo,Opts), ?line fill_tab(Tab,foo), ?line [] = ets:match(Tab,{}), ?line ets:insert(Tab,{{one,4},4}), @@ -3415,7 +3724,7 @@ match_object(Config) when is_list(Config) -> match_object_do(Opts) -> ?line EtsMem = etsmem(), - ?line Tab = ets:new(foobar, Opts), + ?line Tab = ets_new(foobar, Opts), ?line fill_tab(Tab, foo), ?line ets:insert(Tab, {{one, 4}, 4}), ?line ets:insert(Tab,{{one,5},5}), @@ -3459,7 +3768,7 @@ match_object2(Config) when is_list(Config) -> match_object2_do(Opts) -> ?line EtsMem = etsmem(), - ?line Tab = ets:new(foo, [bag, {keypos, 2} | Opts]), + ?line Tab = ets_new(foo, [bag, {keypos, 2} | Opts]), ?line fill_tab2(Tab, 0, 13005), % match_db_object does 1000 % elements per pass, might % change in the future. @@ -3477,7 +3786,6 @@ match_object2_do(Opts) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -misc(suite) -> [misc1, safe_fixtable, info, dups, tab2list]. tab2list(doc) -> ["Tests tab2list (OTP-3319)"]; tab2list(suite) -> []; @@ -3498,7 +3806,7 @@ misc1(Config) when is_list(Config) -> misc1_do(Opts) -> ?line EtsMem = etsmem(), - ?line Tab = ets:new(foo,Opts), + ?line Tab = ets_new(foo,Opts), ?line true = lists:member(Tab,ets:all()), ?line ets:delete(Tab), ?line false = lists:member(Tab,ets:all()), @@ -3517,7 +3825,7 @@ safe_fixtable(Config) when is_list(Config) -> safe_fixtable_do(Opts) -> ?line EtsMem = etsmem(), - ?line Tab = ets:new(foo, Opts), + ?line Tab = ets_new(foo, Opts), ?line fill_tab(Tab, foobar), ?line true = ets:safe_fixtable(Tab, true), ?line receive after 1 -> ok end, @@ -3556,7 +3864,7 @@ info_do(Opts) -> ?line EtsMem = etsmem(), ?line MeMyselfI=self(), ?line ThisNode=node(), - ?line Tab = ets:new(foobar, [{keypos, 2} | Opts]), + ?line Tab = ets_new(foobar, [{keypos, 2} | Opts]), %% Note: ets:info/1 used to return a tuple, but from R11B onwards it %% returns a list. @@ -3610,15 +3918,12 @@ dups_do(Opts) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -files(suite) -> [tab2file, tab2file2, tab2file3, tabfile_ext1, tabfile_ext2, - tabfile_ext3, tabfile_ext4]. - tab2file(doc) -> ["Check the ets:tab2file function on an empty " "ets table."]; tab2file(suite) -> []; tab2file(Config) when is_list(Config) -> %% Write an empty ets table to a file, read back and check properties. - ?line Tab = ets:new(ets_SUITE_foo_tab, [named_table, set, private, + ?line Tab = ets_new(ets_SUITE_foo_tab, [named_table, set, private, {keypos, 2}]), ?line FName = filename:join([?config(priv_dir, Config),"tab2file_case"]), ?line ok = ets:tab2file(Tab, FName), @@ -3634,51 +3939,36 @@ tab2file(Config) when is_list(Config) -> ?line verify_etsmem(EtsMem). tab2file2(doc) -> ["Check the ets:tab2file function on a ", - "filled set type ets table."]; + "filled set/bag type ets table."]; tab2file2(suite) -> []; -tab2file2(Config) when is_list(Config) -> - %% Try the same on a filled set table. - ?line EtsMem = etsmem(), - ?line Tab = ets:new(ets_SUITE_foo_tab, [named_table, set, private, - {keypos, 2}]), - ?line FName = filename:join([?config(priv_dir, Config),"tab2file2_case"]), - ?line ok = fill_tab2(Tab, 0, 10000), % Fill up the table (grucho mucho!) - ?line Len = length(ets:tab2list(Tab)), - ?line ok = ets:tab2file(Tab, FName), - ?line true = ets:delete(Tab), - % - ?line {ok, Tab2} = ets:file2tab(FName), - ?line private = ets:info(Tab2, protection), - ?line true = ets:info(Tab2, named_table), - ?line 2 = ets:info(Tab2, keypos), - ?line set = ets:info(Tab2, type), - ?line Len = length(ets:tab2list(Tab2)), - ?line true = ets:delete(Tab2), - ?line verify_etsmem(EtsMem). +tab2file2(Config) when is_list(Config) -> + repeat_for_opts({tab2file2_do,Config}, [[set,bag],compressed]). -tab2file3(doc) -> ["Check the ets:tab2file function on a ", - "filled bag type ets table."]; -tab2file3(suite) -> []; -tab2file3(Config) when is_list(Config) -> - %% Try the same on a filled bag table. +tab2file2_do(Opts, Config) -> ?line EtsMem = etsmem(), - ?line Tab = ets:new(ets_SUITE_foo_tab, [named_table, bag, private, - {keypos, 2}]), - ?line FName = filename:join([?config(priv_dir, Config),"tab2file3_case"]), + ?line Tab = ets_new(ets_SUITE_foo_tab, [named_table, private, + {keypos, 2} | Opts]), + ?line FName = filename:join([?config(priv_dir, Config),"tab2file2_case"]), ?line ok = fill_tab2(Tab, 0, 10000), % Fill up the table (grucho mucho!) ?line Len = length(ets:tab2list(Tab)), ?line Mem = ets:info(Tab, memory), + ?line Type = ets:info(Tab, type), + %%io:format("org tab: ~p\n",[ets:info(Tab)]), ?line ok = ets:tab2file(Tab, FName), ?line true = ets:delete(Tab), + ?line EtsMem4 = etsmem(), + ?line {ok, Tab2} = ets:file2tab(FName), + %%io:format("loaded tab: ~p\n",[ets:info(Tab2)]), ?line private = ets:info(Tab2, protection), ?line true = ets:info(Tab2, named_table), ?line 2 = ets:info(Tab2, keypos), - ?line bag = ets:info(Tab2, type), + ?line Type = ets:info(Tab2, type), ?line Len = length(ets:tab2list(Tab2)), ?line Mem = ets:info(Tab2, memory), ?line true = ets:delete(Tab2), + io:format("Between = ~p\n", [EtsMem4]), ?line verify_etsmem(EtsMem). -define(test_list, [8,5,4,1,58,125,255, 250, 245, 240, 235, @@ -3722,7 +4012,7 @@ tabfile_ext1_do(Opts,Config) -> ?line FName = filename:join([?config(priv_dir, Config),"nisse.dat"]), ?line FName2 = filename:join([?config(priv_dir, Config),"countflip.dat"]), L = lists:seq(1,10), - T = ets:new(x,Opts), + T = ets_new(x,Opts), Name = make_ref(), [ets:insert(T,{X,integer_to_list(X)}) || X <- L], ok = ets:tab2file(T,FName,[{extended_info,[object_count]}]), @@ -3762,7 +4052,7 @@ tabfile_ext2_do(Opts,Config) -> ?line FName = filename:join([?config(priv_dir, Config),"olle.dat"]), ?line FName2 = filename:join([?config(priv_dir, Config),"bitflip.dat"]), L = lists:seq(1,10), - T = ets:new(x,Opts), + T = ets_new(x,Opts), Name = make_ref(), [ets:insert(T,{X,integer_to_list(X)}) || X <- L], ok = ets:tab2file(T,FName,[{extended_info,[md5sum]}]), @@ -3800,7 +4090,7 @@ tabfile_ext3(Config) when is_list(Config) -> ?line FName2 = filename:join([?config(priv_dir, Config),"ncountflip.dat"]), L = lists:seq(1,10), Name = make_ref(), - ?MODULE = ets:new(?MODULE,[named_table]), + ?MODULE = ets_new(?MODULE,[named_table]), [ets:insert(?MODULE,{X,integer_to_list(X)}) || X <- L], ets:tab2file(?MODULE,FName), {error,cannot_create_table} = ets:file2tab(FName), @@ -3832,7 +4122,7 @@ tabfile_ext4(doc) -> tabfile_ext4(Config) when is_list(Config) -> ?line FName = filename:join([?config(priv_dir, Config),"bauta.dat"]), LL = lists:seq(1,10000), - TL = ets:new(x,[]), + TL = ets_new(x,[]), Name2 = make_ref(), [ets:insert(TL,{X,integer_to_list(X)}) || X <- LL], ok = ets:tab2file(TL,FName,[{extended_info,[md5sum]}]), @@ -3877,7 +4167,6 @@ make_sub_binary(List, Num) when is_list(List) -> {_,B} = split_binary(Bin, N+1), B. -heavy(suite) -> [heavy_lookup, heavy_lookup_element]. %% Lookup stuff like crazy... heavy_lookup(doc) -> ["Performs multiple lookups for every key ", @@ -3888,7 +4177,7 @@ heavy_lookup(Config) when is_list(Config) -> heavy_lookup_do(Opts) -> ?line EtsMem = etsmem(), - ?line Tab = ets:new(foobar_table, [set, protected, {keypos, 2} | Opts]), + ?line Tab = ets_new(foobar_table, [set, protected, {keypos, 2} | Opts]), ?line ok = fill_tab2(Tab, 0, 7000), ?line ?t:do_times(50, ?MODULE, do_lookup, [Tab, 6999]), ?line true = ets:delete(Tab), @@ -3911,7 +4200,7 @@ heavy_lookup_element(Config) when is_list(Config) -> heavy_lookup_element_do(Opts) -> ?line EtsMem = etsmem(), - ?line Tab = ets:new(foobar_table, [set, protected, {keypos, 2} | Opts]), + ?line Tab = ets_new(foobar_table, [set, protected, {keypos, 2} | Opts]), ?line ok = fill_tab2(Tab, 0, 7000), case os:type() of vxworks -> @@ -3940,9 +4229,44 @@ do_lookup_element(Tab, N, M) -> end. -fold(suite) -> [foldl_ordered, foldr_ordered, - foldl, foldr, - fold_empty]. +heavy_concurrent(Config) when is_list(Config) -> + repeat_for_opts(do_heavy_concurrent). + +do_heavy_concurrent(Opts) -> + ?line Size = 10000, + ?line EtsMem = etsmem(), + ?line Tab = ets_new(blupp, [set, public, {keypos, 2} | Opts]), + ?line ok = fill_tab2(Tab, 0, Size), + ?line Procs = lists:map( + fun (N) -> + spawn_link( + fun () -> + do_heavy_concurrent_proc(Tab, Size, N) + end) + end, + lists:seq(1, 500)), + ?line lists:foreach(fun (P) -> + M = erlang:monitor(process, P), + receive + {'DOWN', M, process, P, _} -> + ok + end + end, + Procs), + ?line true = ets:delete(Tab), + ?line verify_etsmem(EtsMem). + +do_heavy_concurrent_proc(_Tab, 0, _Offs) -> + done; +do_heavy_concurrent_proc(Tab, N, Offs) when (N+Offs) rem 100 == 0 -> + Data = {"here", are, "S O M E ", data, "toooooooooooooooooo", insert, + make_ref(), make_ref(), make_ref()}, + true=ets:insert(Tab, {{self(),Data}, N}), + do_heavy_concurrent_proc(Tab, N-1, Offs); +do_heavy_concurrent_proc(Tab, N, Offs) -> + _ = ets:lookup(Tab, N), + do_heavy_concurrent_proc(Tab, N-1, Offs). + fold_empty(doc) -> []; @@ -4012,7 +4336,7 @@ member(Config) when is_list(Config) -> member_do(Opts) -> ?line EtsMem = etsmem(), - ?line T = ets:new(xxx, Opts), + ?line T = ets_new(xxx, Opts), ?line false = ets:member(T,hej), ?line E = fun(0,_F)->ok; (N,F) -> @@ -4037,7 +4361,7 @@ member_do(Opts) -> build_table(L1,L2,Num) -> - T = ets:new(xxx, [ordered_set] + T = ets_new(xxx, [ordered_set] ), lists:foreach( fun(X1) -> @@ -4059,7 +4383,7 @@ build_table(L1,L2,Num) -> T. build_table2(L1,L2,Num) -> - T = ets:new(xxx, [ordered_set] + T = ets_new(xxx, [ordered_set] ), lists:foreach( fun(X1) -> @@ -4190,7 +4514,7 @@ do_n_times(Fun,N) -> do_n_times(Fun,N-1). make_table(Name, Options, Elements) -> - T = ets:new(Name, Options), + T = ets_new(Name, Options), lists:foreach(fun(E) -> ets:insert(T, E) end, Elements), T. filltabint(Tab,0) -> @@ -4254,13 +4578,13 @@ xfilltabstr(Tab,N) -> fill_sets_int(N) -> fill_sets_int(N,[]). fill_sets_int(N,Opts) -> - Tab1 = ets:new(xxx, [ordered_set|Opts]), + Tab1 = ets_new(xxx, [ordered_set|Opts]), filltabint(Tab1,N), - Tab2 = ets:new(xxx, [set|Opts]), + Tab2 = ets_new(xxx, [set|Opts]), filltabint(Tab2,N), - Tab3 = ets:new(xxx, [bag|Opts]), + Tab3 = ets_new(xxx, [bag|Opts]), filltabint2(Tab3,N), - Tab4 = ets:new(xxx, [duplicate_bag|Opts]), + Tab4 = ets_new(xxx, [duplicate_bag|Opts]), filltabint3(Tab4,N), [Tab1,Tab2,Tab3,Tab4]. @@ -4412,7 +4736,7 @@ gen_dets_filename(Config,N) -> "testdets_" ++ integer_to_list(N) ++ ".dets"). otp_6842_select_1000(Config) when is_list(Config) -> - ?line Tab = ets:new(xxx,[ordered_set]), + ?line Tab = ets_new(xxx,[ordered_set]), ?line [ets:insert(Tab,{X,X}) || X <- lists:seq(1,10000)], ?line AllTrue = lists:duplicate(10,true), ?line AllTrue = @@ -4445,7 +4769,7 @@ check_seq(A,B,C) -> otp_6338(Config) when is_list(Config) -> L = binary_to_term(<<131,108,0,0,0,2,104,2,108,0,0,0,2,103,100,0,19,112,112,98,49,95,98,115,49,50,64,98,108,97,100,101,95,48,95,53,0,0,33,50,0,0,0,4,1,98,0,0,23,226,106,100,0,4,101,120,105,116,104,2,108,0,0,0,2,104,2,100,0,3,115,98,109,100,0,19,112,112,98,50,95,98,115,49,50,64,98,108,97,100,101,95,48,95,56,98,0,0,18,231,106,100,0,4,114,101,99,118,106>>), - T = ets:new(xxx,[ordered_set]), + T = ets_new(xxx,[ordered_set]), lists:foreach(fun(X) -> ets:insert(T,X) end,L), [[4839,recv]] = ets:match(T,{[{sbm,ppb2_bs12@blade_0_8},'$1'],'$2'}), ets:delete(T). @@ -4456,7 +4780,7 @@ otp_5340(Config) when is_list(Config) -> otp_5340_do(Opts) -> N = 3000, - T = ets:new(otp_5340, [bag,public | Opts]), + T = ets_new(otp_5340, [bag,public | Opts]), Ids = [1,2,3,4,5], [w(T, N, Id) || Id <- Ids], verify(T, Ids), @@ -4492,7 +4816,7 @@ otp_7665(Config) when is_list(Config) -> repeat_for_opts(otp_7665_do). otp_7665_do(Opts) -> - Tab = ets:new(otp_7665,[bag | Opts]), + Tab = ets_new(otp_7665,[bag | Opts]), Min = 0, Max = 10, lists:foreach(fun(N)-> otp_7665_act(Tab,Min,Max,N) end, @@ -4555,7 +4879,7 @@ meta_wb_do(Opts) -> Names). meta_wb_new(Name, _, Tabs, Opts) -> - case (catch ets:new(Name,[named_table|Opts])) of + case (catch ets_new(Name,[named_table|Opts])) of Name -> ?line false = lists:member(Name, Tabs), [Name | Tabs]; @@ -4603,7 +4927,7 @@ grow_shrink_0([N|Ns], EtsMem) -> grow_shrink_0([], _) -> ok. grow_shrink_1(N, Flags) -> - ?line T = ets:new(a, Flags), + ?line T = ets_new(a, Flags), ?line grow_shrink_2(N, N, T), ?line ets:delete(T). @@ -4633,7 +4957,7 @@ grow_pseudo_deleted_do() -> grow_pseudo_deleted_do(Type) -> process_flag(scheduler,1), Self = self(), - ?line T = ets:new(kalle,[Type,public,{write_concurrency,true}]), + ?line T = ets_new(kalle,[Type,public,{write_concurrency,true}]), Mod = 7, Mult = 10000, filltabint(T,Mod*Mult), ?line true = ets:safe_fixtable(T,true), @@ -4675,7 +4999,7 @@ shrink_pseudo_deleted_do() -> shrink_pseudo_deleted_do(Type) -> process_flag(scheduler,1), Self = self(), - ?line T = ets:new(kalle,[Type,public,{write_concurrency,true}]), + ?line T = ets_new(kalle,[Type,public,{write_concurrency,true}]), Half = 10000, filltabint(T,Half*2), ?line true = ets:safe_fixtable(T,true), @@ -4704,17 +5028,10 @@ shrink_pseudo_deleted_do(Type) -> process_flag(scheduler,0). -meta_smp(suite) -> - [meta_lookup_unnamed_read, - meta_lookup_unnamed_write, - meta_lookup_named_read, - meta_lookup_named_write, - meta_newdel_unnamed, - meta_newdel_named]. meta_lookup_unnamed_read(suite) -> []; meta_lookup_unnamed_read(Config) when is_list(Config) -> - InitF = fun(_) -> Tab = ets:new(unnamed,[]), + InitF = fun(_) -> Tab = ets_new(unnamed,[]), true = ets:insert(Tab,{key,data}), Tab end, @@ -4727,7 +5044,7 @@ meta_lookup_unnamed_read(Config) when is_list(Config) -> meta_lookup_unnamed_write(suite) -> []; meta_lookup_unnamed_write(Config) when is_list(Config) -> - InitF = fun(_) -> Tab = ets:new(unnamed,[]), + InitF = fun(_) -> Tab = ets_new(unnamed,[]), {Tab,0} end, ExecF = fun({Tab,N}) -> true = ets:insert(Tab,{key,N}), @@ -4740,7 +5057,7 @@ meta_lookup_unnamed_write(Config) when is_list(Config) -> meta_lookup_named_read(suite) -> []; meta_lookup_named_read(Config) when is_list(Config) -> InitF = fun([ProcN|_]) -> Name = list_to_atom(integer_to_list(ProcN)), - Tab = ets:new(Name,[named_table]), + Tab = ets_new(Name,[named_table]), true = ets:insert(Tab,{key,data}), Tab end, @@ -4754,7 +5071,7 @@ meta_lookup_named_read(Config) when is_list(Config) -> meta_lookup_named_write(suite) -> []; meta_lookup_named_write(Config) when is_list(Config) -> InitF = fun([ProcN|_]) -> Name = list_to_atom(integer_to_list(ProcN)), - Tab = ets:new(Name,[named_table]), + Tab = ets_new(Name,[named_table]), {Tab,0} end, ExecF = fun({Tab,N}) -> true = ets:insert(Tab,{key,N}), @@ -4767,7 +5084,7 @@ meta_lookup_named_write(Config) when is_list(Config) -> meta_newdel_unnamed(suite) -> []; meta_newdel_unnamed(Config) when is_list(Config) -> InitF = fun(_) -> ok end, - ExecF = fun(_) -> Tab = ets:new(unnamed,[]), + ExecF = fun(_) -> Tab = ets_new(unnamed,[]), true = ets:delete(Tab) end, FiniF = fun(_) -> ok end, @@ -4777,7 +5094,7 @@ meta_newdel_named(suite) -> []; meta_newdel_named(Config) when is_list(Config) -> InitF = fun([ProcN|_]) -> list_to_atom(integer_to_list(ProcN)) end, - ExecF = fun(Name) -> Name = ets:new(Name,[named_table]), + ExecF = fun(Name) -> Name = ets_new(Name,[named_table]), true = ets:delete(Name), Name end, @@ -4787,7 +5104,7 @@ meta_newdel_named(Config) when is_list(Config) -> smp_insert(doc) -> ["Concurrent insert's on same table"]; smp_insert(suite) -> []; smp_insert(Config) when is_list(Config) -> - ets:new(smp_insert,[named_table,public,{write_concurrency,true}]), + ets_new(smp_insert,[named_table,public,{write_concurrency,true}]), InitF = fun(_) -> ok end, ExecF = fun(_) -> true = ets:insert(smp_insert,{random:uniform(10000)}) end, @@ -4802,7 +5119,7 @@ smp_fixed_delete(Config) when is_list(Config) -> only_if_smp(fun()->smp_fixed_delete_do() end). smp_fixed_delete_do() -> - T = ets:new(foo,[public,{write_concurrency,true}]), + T = ets_new(foo,[public,{write_concurrency,true}]), %%Mem = ets:info(T,memory), NumOfObjs = 100000, filltabint(T,NumOfObjs), @@ -4838,7 +5155,7 @@ smp_unfix_fix(Config) when is_list(Config) -> smp_unfix_fix_do() -> process_flag(scheduler,1), Parent = self(), - T = ets:new(foo,[public,{write_concurrency,true}]), + T = ets_new(foo,[public,{write_concurrency,true}]), %%Mem = ets:info(T,memory), NumOfObjs = 100000, Deleted = 50000, @@ -4898,7 +5215,7 @@ otp_8166_do(WC) -> %% Bug scenario: One process segv while reading the table because another %% process is doing unfix without write-lock at the end of a trapping match_object. process_flag(scheduler,1), - T = ets:new(foo,[public, {write_concurrency,WC}]), + T = ets_new(foo,[public, {write_concurrency,WC}]), NumOfObjs = 3000, %% Need more than 1000 live objects for match_object to trap one time Deleted = NumOfObjs div 2, filltabint(T,NumOfObjs), @@ -5010,14 +5327,20 @@ verify_table_load(T) -> end. - - +otp_8732(doc) -> ["ets:select on a tree with NIL key object"]; +otp_8732(Config) when is_list(Config) -> + Tab = ets_new(noname,[ordered_set]), + filltabstr(Tab,999), + ets:insert(Tab,{[],"nasty NIL object"}), + ?line [] = ets:match(Tab,{'_',nomatch}), %% Will hang if bug not fixed + ok. + smp_select_delete(suite) -> []; smp_select_delete(doc) -> ["Run concurrent select_delete (and inserts) on same table."]; smp_select_delete(Config) when is_list(Config) -> - T = ets:new(smp_select_delete,[named_table,public,{write_concurrency,true}]), + T = ets_new(smp_select_delete,[named_table,public,{write_concurrency,true}]), Mod = 17, Zeros = erlang:make_tuple(Mod,0), InitF = fun(_) -> Zeros end, @@ -5070,6 +5393,39 @@ smp_select_delete(Config) when is_list(Config) -> ?line false = ets:info(T,fixed), ets:delete(T). +types(doc) -> ["Test different types"]; +types(Config) when is_list(Config) -> + init_externals(), + repeat_for_opts(types_do,[[set,ordered_set],compressed]). + +types_do(Opts) -> + EtsMem = etsmem(), + ?line T = ets_new(xxx,Opts), + Fun = fun(Term) -> + ets:insert(T,{Term}), + ?line [{Term}] = ets:lookup(T,Term), + ets:insert(T,{Term,xxx}), + ?line [{Term,xxx}] = ets:lookup(T,Term), + ets:insert(T,{Term,"xxx"}), + ?line [{Term,"xxx"}] = ets:lookup(T,Term), + ets:insert(T,{xxx,Term}), + ?line [{xxx,Term}] = ets:lookup(T,xxx), + ets:insert(T,{"xxx",Term}), + ?line [{"xxx",Term}] = ets:lookup(T,"xxx"), + ets:delete_all_objects(T), + ?line 0 = ets:info(T,size) + end, + test_terms(Fun, strict), + ets:delete(T), + ?line verify_etsmem(EtsMem). + + + + +% +% Utility functions: +% + add_lists(L1,L2) -> add_lists(L1,L2,[]). add_lists([],[],Acc) -> @@ -5134,31 +5490,55 @@ my_tab_to_list(_Ts,'$end_of_table', Acc) -> lists:reverse(Acc); my_tab_to_list(Ts,Key, Acc) -> my_tab_to_list(Ts,ets:next(Ts,Key),[ets:lookup(Ts, Key)| Acc]). +wait_for_all_schedulers_online_to_execute() -> + PMs = lists:map(fun (Sched) -> + spawn_opt(fun () -> ok end, + [monitor, {scheduler, Sched}]) + end, + lists:seq(1,erlang:system_info(schedulers_online))), + lists:foreach(fun ({P, M}) -> + receive + {'DOWN', M, process, P, _} -> ok + end + end, + PMs), + ok. + etsmem() -> + %% Wait until it is guaranteed that all already scheduled + %% deallocations of DbTable structures have completed. + wait_for_all_schedulers_online_to_execute(), + + AllTabs = lists:map(fun(T) -> {T,ets:info(T,name),ets:info(T,size), + ets:info(T,memory),ets:info(T,type)} + end, ets:all()), + Mem = {try erlang:memory(ets) catch error:notsup -> notsup end, case erlang:system_info({allocator,ets_alloc}) of false -> undefined; MemInfo -> - MSBCS = lists:foldl( - fun ({instance, _, L}, Acc) -> - {value,{_,MBCS}} = lists:keysearch(mbcs, 1, L), - {value,{_,SBCS}} = lists:keysearch(sbcs, 1, L), - [MBCS,SBCS | Acc] - end, - [], - MemInfo), + CS = lists:foldl( + fun ({instance, _, L}, Acc) -> + {value,{_,SBMBCS}} = lists:keysearch(sbmbcs, 1, L), + {value,{_,MBCS}} = lists:keysearch(mbcs, 1, L), + {value,{_,SBCS}} = lists:keysearch(sbcs, 1, L), + [SBMBCS,MBCS,SBCS | Acc] + end, + [], + MemInfo), lists:foldl( fun(L, {Bl0,BlSz0}) -> {value,{_,Bl,_,_}} = lists:keysearch(blocks, 1, L), {value,{_,BlSz,_,_}} = lists:keysearch(blocks_size, 1, L), {Bl0+Bl,BlSz0+BlSz} - end, {0,0}, MSBCS) - end}. + end, {0,0}, CS) + end}, + {Mem,AllTabs}. -verify_etsmem(MemInfo) -> +verify_etsmem({MemInfo,AllTabs}) -> wait_for_test_procs(), case etsmem() of - MemInfo -> + {MemInfo,_} -> io:format("Ets mem info: ~p", [MemInfo]), case MemInfo of {ErlMem,EtsAlloc} when ErlMem == notsup; EtsAlloc == undefined -> @@ -5167,12 +5547,15 @@ verify_etsmem(MemInfo) -> _ -> ok end; - Other -> + {MemInfo2, AllTabs2} -> io:format("Expected: ~p", [MemInfo]), - io:format("Actual: ~p", [Other]), + io:format("Actual: ~p", [MemInfo2]), + io:format("Changed tables before: ~p\n",[AllTabs -- AllTabs2]), + io:format("Changed tables after: ~p\n", [AllTabs2 -- AllTabs]), ?t:fail() end. + start_loopers(N, Prio, Fun, State) -> lists:map(fun (_) -> my_spawn_opt(fun () -> looper(Fun, State) end, @@ -5277,6 +5660,20 @@ repeat_while(Fun, Arg0) -> {false,Ret} -> Ret end. +%% Some (but not all) permutations of List +repeat_for_permutations(Fun, List) -> + repeat_for_permutations(Fun, List, length(List)-1). +repeat_for_permutations(Fun, List, 0) -> + Fun(List); +repeat_for_permutations(Fun, List, N) -> + {A,B} = lists:split(N, List), + L1 = B++A, + L2 = lists:reverse(L1), + L3 = B++lists:reverse(A), + L4 = lists:reverse(B)++A, + Fun(L1), Fun(L2), Fun(L3), Fun(L4), + repeat_for_permutations(Fun, List, N-1). + receive_any() -> receive M -> io:format("Process ~p got msg ~p\n", [self(),M]), @@ -5332,22 +5729,232 @@ only_if_smp(Schedulers, Func) -> {true,_} -> Func() end. +%% Copy-paste from emulator/test/binary_SUITE.erl +-define(heap_binary_size, 64). +test_terms(Test_Func, Mode) -> + garbage_collect(), + ?line Pib0 = process_info(self(),binary), + + ?line Test_Func(atom), + ?line Test_Func(''), + ?line Test_Func('a'), + ?line Test_Func('ab'), + ?line Test_Func('abc'), + ?line Test_Func('abcd'), + ?line Test_Func('abcde'), + ?line Test_Func('abcdef'), + ?line Test_Func('abcdefg'), + ?line Test_Func('abcdefgh'), + + ?line Test_Func(fun() -> ok end), + X = id([a,{b,c},c]), + Y = id({x,y,z}), + Z = id(1 bsl 8*257), + ?line Test_Func(fun() -> X end), + ?line Test_Func(fun() -> {X,Y} end), + ?line Test_Func([fun() -> {X,Y,Z} end, + fun() -> {Z,X,Y} end, + fun() -> {Y,Z,X} end]), + + ?line Test_Func({trace_ts,{even_bigger,{some_data,fun() -> ok end}},{1,2,3}}), + ?line Test_Func({trace_ts,{even_bigger,{some_data,<<1,2,3,4,5,6,7,8,9,10>>}}, + {1,2,3}}), + + ?line Test_Func(1), + ?line Test_Func(42), + ?line Test_Func(-23), + ?line Test_Func(256), + ?line Test_Func(25555), + ?line Test_Func(-3333), + + ?line Test_Func(1.0), + + ?line Test_Func(183749783987483978498378478393874), + ?line Test_Func(-37894183749783987483978498378478393874), + Very_Big = very_big_num(), + ?line Test_Func(Very_Big), + ?line Test_Func(-Very_Big+1), + + ?line Test_Func([]), + ?line Test_Func("abcdef"), + ?line Test_Func([a, b, 1, 2]), + ?line Test_Func([a|b]), + + ?line Test_Func({}), + ?line Test_Func({1}), + ?line Test_Func({a, b}), + ?line Test_Func({a, b, c}), + ?line Test_Func(list_to_tuple(lists:seq(0, 255))), + ?line Test_Func(list_to_tuple(lists:seq(0, 256))), + + ?line Test_Func(make_ref()), + ?line Test_Func([make_ref(), make_ref()]), + + ?line Test_Func(make_port()), + + ?line Test_Func(make_pid()), + ?line Test_Func(make_ext_pid()), + ?line Test_Func(make_ext_port()), + ?line Test_Func(make_ext_ref()), + + Bin0 = list_to_binary(lists:seq(0, 14)), + ?line Test_Func(Bin0), + Bin1 = list_to_binary(lists:seq(0, ?heap_binary_size)), + ?line Test_Func(Bin1), + Bin2 = list_to_binary(lists:seq(0, ?heap_binary_size+1)), + ?line Test_Func(Bin2), + Bin3 = list_to_binary(lists:seq(0, 255)), + garbage_collect(), + Pib = process_info(self(),binary), + ?line Test_Func(Bin3), + garbage_collect(), + case Mode of + strict -> ?line Pib = process_info(self(),binary); + skip_refc_check -> ok + end, + + ?line Test_Func(make_unaligned_sub_binary(Bin0)), + ?line Test_Func(make_unaligned_sub_binary(Bin1)), + ?line Test_Func(make_unaligned_sub_binary(Bin2)), + ?line Test_Func(make_unaligned_sub_binary(Bin3)), + + ?line Test_Func(make_sub_binary(lists:seq(42, 43))), + ?line Test_Func(make_sub_binary([42,43,44])), + ?line Test_Func(make_sub_binary([42,43,44,45])), + ?line Test_Func(make_sub_binary([42,43,44,45,46])), + ?line Test_Func(make_sub_binary([42,43,44,45,46,47])), + ?line Test_Func(make_sub_binary([42,43,44,45,46,47,48])), + ?line Test_Func(make_sub_binary(lists:seq(42, 49))), + ?line Test_Func(make_sub_binary(lists:seq(0, 14))), + ?line Test_Func(make_sub_binary(lists:seq(0, ?heap_binary_size))), + ?line Test_Func(make_sub_binary(lists:seq(0, ?heap_binary_size+1))), + ?line Test_Func(make_sub_binary(lists:seq(0, 255))), + + ?line Test_Func(make_unaligned_sub_binary(lists:seq(42, 43))), + ?line Test_Func(make_unaligned_sub_binary([42,43,44])), + ?line Test_Func(make_unaligned_sub_binary([42,43,44,45])), + ?line Test_Func(make_unaligned_sub_binary([42,43,44,45,46])), + ?line Test_Func(make_unaligned_sub_binary([42,43,44,45,46,47])), + ?line Test_Func(make_unaligned_sub_binary([42,43,44,45,46,47,48])), + ?line Test_Func(make_unaligned_sub_binary(lists:seq(42, 49))), + ?line Test_Func(make_unaligned_sub_binary(lists:seq(0, 14))), + ?line Test_Func(make_unaligned_sub_binary(lists:seq(0, ?heap_binary_size))), + ?line Test_Func(make_unaligned_sub_binary(lists:seq(0, ?heap_binary_size+1))), + ?line Test_Func(make_unaligned_sub_binary(lists:seq(0, 255))), + + %% Bit level binaries. + ?line Test_Func(<<1:1>>), + ?line Test_Func(<<2:2>>), + ?line Test_Func(<<42:10>>), + ?line Test_Func(list_to_bitstring([<<5:6>>|lists:seq(0, 255)])), + + ?line Test_Func(F = fun(A) -> 42*A end), + ?line Test_Func(lists:duplicate(32, F)), + + ?line Test_Func(FF = fun binary_SUITE:all/1), + ?line Test_Func(lists:duplicate(32, FF)), + + garbage_collect(), + case Mode of + strict -> ?line Pib0 = process_info(self(),binary); + skip_refc_check -> ok + end, + ok. + + +id(I) -> I. + +very_big_num() -> + very_big_num(33, 1). + +very_big_num(Left, Result) when Left > 0 -> + ?line very_big_num(Left-1, Result*256); +very_big_num(0, Result) -> + ?line Result. + +make_port() -> + ?line open_port({spawn, efile}, [eof]). + +make_pid() -> + ?line spawn_link(?MODULE, sleeper, []). + +sleeper() -> + ?line receive after infinity -> ok end. + +make_ext_pid() -> + {Pid, _, _} = get(externals), + Pid. + +make_ext_port() -> + {_, Port, _} = get(externals), + Port. +make_ext_ref() -> + {_, _, Ref} = get(externals), + 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), + put(externals, Res); + + {_,_,_} -> ok + end. + +rpc_externals() -> + {self(), make_port(), make_ref()}. + +make_sub_binary(Bin) when is_binary(Bin) -> + {_,B} = split_binary(list_to_binary([0,1,3,Bin]), 3), + B; +make_sub_binary(List) -> + make_sub_binary(list_to_binary(List)). + +make_unaligned_sub_binary(Bin0) when is_binary(Bin0) -> + Bin1 = <<0:3,Bin0/binary,31:5>>, + Sz = size(Bin0), + <<0:3,Bin:Sz/binary,31:5>> = id(Bin1), + Bin; +make_unaligned_sub_binary(List) -> + make_unaligned_sub_binary(list_to_binary(List)). %% Repeat test function with different combination of table options %% repeat_for_opts(F) -> - repeat_for_opts(F, [write_concurrency]). + repeat_for_opts(F, [write_concurrency, read_concurrency, compressed]). repeat_for_opts(F, OptGenList) when is_atom(F) -> repeat_for_opts(fun(Opts) -> ?MODULE:F(Opts) end, OptGenList); +repeat_for_opts({F,Args}, OptGenList) when is_atom(F) -> + repeat_for_opts(fun(Opts) -> ?MODULE:F(Opts,Args) end, OptGenList); repeat_for_opts(F, OptGenList) -> repeat_for_opts(F, OptGenList, []). repeat_for_opts(F, [], Acc) -> - lists:map(fun(Opts) -> - io:format("Calling with options ~p\n",[Opts]), - F(Opts) - end, Acc); + lists:map(fun(Opts) -> + OptList = lists:filter(fun(E) -> E =/= void end, Opts), + io:format("Calling with options ~p\n",[OptList]), + F(OptList) + end, Acc); repeat_for_opts(F, [OptList | Tail], []) when is_list(OptList) -> repeat_for_opts(F, Tail, [[Opt] || Opt <- OptList]); repeat_for_opts(F, [OptList | Tail], AccList) when is_list(OptList) -> @@ -5356,6 +5963,10 @@ repeat_for_opts(F, [Atom | Tail], AccList) when is_atom(Atom) -> repeat_for_opts(F, [repeat_for_opts_atom2list(Atom) | Tail ], AccList). repeat_for_opts_atom2list(all_types) -> [set,ordered_set,bag,duplicate_bag]; -repeat_for_opts_atom2list(write_concurrency) -> [{write_concurrency,false},{write_concurrency,true}]. - +repeat_for_opts_atom2list(write_concurrency) -> [{write_concurrency,false},{write_concurrency,true}]; +repeat_for_opts_atom2list(read_concurrency) -> [{read_concurrency,false},{read_concurrency,true}]; +repeat_for_opts_atom2list(compressed) -> [compressed,void]. +ets_new(Name, Opts) -> + %%ets:new(Name, [compressed | Opts]). + ets:new(Name, Opts). diff --git a/lib/stdlib/test/ets_tough_SUITE.erl b/lib/stdlib/test/ets_tough_SUITE.erl index 4c8d941f13..d9d0461575 100644 --- a/lib/stdlib/test/ets_tough_SUITE.erl +++ b/lib/stdlib/test/ets_tough_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. 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,13 +17,33 @@ %% %CopyrightEnd% %% -module(ets_tough_SUITE). --export([all/1,ex1/1]). +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2,ex1/1]). -export([init/1,terminate/2,handle_call/3,handle_info/2]). --export([init_per_testcase/2, fin_per_testcase/2]). +-export([init_per_testcase/2, end_per_testcase/2]). -compile([export_all]). --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [ex1]. + +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. -all(suite) -> [ex1]. -define(DEBUG(X),debug_disabled). @@ -34,7 +54,7 @@ init_per_testcase(_Func, Config) -> Dog=test_server:timetrap(test_server:seconds(300)), [{watchdog, Dog}|Config]. -fin_per_testcase(_Func, Config) -> +end_per_testcase(_Func, Config) -> Dog=?config(watchdog, Config), test_server:timetrap_cancel(Dog), ets:delete(?GLOBAL_PARAMS). diff --git a/lib/stdlib/test/file_sorter_SUITE.erl b/lib/stdlib/test/file_sorter_SUITE.erl index c00ed91fe7..80d4ea5fdc 100644 --- a/lib/stdlib/test/file_sorter_SUITE.erl +++ b/lib/stdlib/test/file_sorter_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2009. All Rights Reserved. +%% Copyright Ericsson AB 2001-2011. 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,12 +27,13 @@ -define(t,test_server). -define(privdir(_), "./file_sorter_SUITE_priv"). -else. --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). -define(format(S, A), ok). -define(privdir(Conf), ?config(priv_dir, Conf)). -endif. --export([all/1, basic/1, badarg/1, +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2, basic/1, badarg/1, term_sort/1, term_keysort/1, binary_term_sort/1, binary_term_keysort/1, binary_sort/1, @@ -44,30 +45,42 @@ binary_check/1, inout/1, misc/1, many/1]). --export([init_per_testcase/2, fin_per_testcase/2]). +-export([init_per_testcase/2, end_per_testcase/2]). init_per_testcase(_Case, Config) -> Dog=?t:timetrap(?t:minutes(2)), [{watchdog, Dog}|Config]. -fin_per_testcase(_Case, Config) -> +end_per_testcase(_Case, Config) -> Dog=?config(watchdog, Config), test_server:timetrap_cancel(Dog), ok. -all(suite) -> - {req,[stdlib,kernel], - [basic, badarg, - term_sort, term_keysort, - binary_term_sort, binary_term_keysort, - binary_sort, - term_merge, term_keymerge, - binary_term_merge, binary_term_keymerge, - binary_merge, - term_check, binary_term_keycheck, - binary_term_check, binary_term_keycheck, - binary_check, - inout, misc, many]}. +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [basic, badarg, term_sort, term_keysort, + binary_term_sort, binary_term_keysort, binary_sort, + term_merge, term_keymerge, binary_term_merge, + binary_term_keymerge, binary_merge, term_check, + binary_term_keycheck, binary_term_check, + binary_term_keycheck, binary_check, inout, misc, many]. + +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + basic(doc) -> ["Basic test case."]; diff --git a/lib/stdlib/test/filelib_SUITE.erl b/lib/stdlib/test/filelib_SUITE.erl index d54741051f..a355097fe2 100644 --- a/lib/stdlib/test/filelib_SUITE.erl +++ b/lib/stdlib/test/filelib_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2010. All Rights Reserved. +%% Copyright Ericsson AB 2005-2011. 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,27 +19,47 @@ -module(filelib_SUITE). --export([all/1,init_per_testcase/2,fin_per_testcase/2, +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2, + init_per_testcase/2,end_per_testcase/2, wildcard_one/1,wildcard_two/1,wildcard_errors/1, fold_files/1,otp_5960/1,ensure_dir_eexist/1]). -import(lists, [foreach/2]). --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). -include_lib("kernel/include/file.hrl"). init_per_testcase(_Case, Config) -> ?line Dog = ?t:timetrap(?t:minutes(5)), [{watchdog,Dog}|Config]. -fin_per_testcase(_Case, Config) -> +end_per_testcase(_Case, Config) -> Dog = ?config(watchdog, Config), test_server:timetrap_cancel(Dog), ok. -all(suite) -> - [wildcard_one,wildcard_two,wildcard_errors,fold_files,otp_5960, - ensure_dir_eexist]. +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [wildcard_one, wildcard_two, wildcard_errors, + fold_files, otp_5960, ensure_dir_eexist]. + +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + wildcard_one(Config) when is_list(Config) -> ?line {ok,OldCwd} = file:get_cwd(), @@ -53,8 +73,11 @@ wildcard_one(Config) when is_list(Config) -> 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) -> filelib:wildcard(Wc, Dir) end), + ?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,_} -> @@ -253,5 +276,7 @@ ensure_dir_eexist(Config) when is_list(Config) -> %% There already is a file with the name of the directory %% we want to create. ?line NeedFile = filename:join(Name, "file"), + ?line NeedFileB = filename:join(Name, <<"file">>), ?line {error, eexist} = filelib:ensure_dir(NeedFile), + ?line {error, eexist} = filelib:ensure_dir(NeedFileB), ok. diff --git a/lib/stdlib/test/filename_SUITE.erl b/lib/stdlib/test/filename_SUITE.erl index ab6521f37b..70b0d413dc 100644 --- a/lib/stdlib/test/filename_SUITE.erl +++ b/lib/stdlib/test/filename_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2011. 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,17 +17,44 @@ %% %CopyrightEnd% %% -module(filename_SUITE). --export([all/1]). +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2]). -export([absname/1, absname_2/1, basename_1/1, basename_2/1, dirname/1, extension/1, join/1, t_nativename/1]). -export([pathtype/1,rootname/1,split/1,find_src/1]). --include("test_server.hrl"). +-export([absname_bin/1, absname_bin_2/1, + basename_bin_1/1, basename_bin_2/1, + dirname_bin/1, extension_bin/1, join_bin/1]). +-export([pathtype_bin/1,rootname_bin/1,split_bin/1]). -all(suite) -> +-include_lib("test_server/include/test_server.hrl"). + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> [absname, absname_2, basename_1, basename_2, dirname, extension, - join, pathtype, rootname, split, t_nativename, find_src]. + join, pathtype, rootname, split, t_nativename, find_src, + absname_bin, absname_bin_2, basename_bin_1, basename_bin_2, dirname_bin, + extension_bin, + join_bin, pathtype_bin, rootname_bin, split_bin]. + +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -457,3 +484,307 @@ find_src(Config) when is_list(Config) -> %% Try to find the source for a preloaded module. ?line {error,{preloaded,init}} = filename:find_src(init), ok. + +%% +%% +%% With binaries +%% +%% + +absname_bin(Config) when is_list(Config) -> + case os:type() of + {win32, _} -> + ?line [Drive|_] = ?config(priv_dir, Config), + ?line Temp = filename:join([Drive|":/"], "temp"), + ?line case file:make_dir(Temp) of + ok -> ok; + {error,eexist} -> ok + end, + ?line {ok,Cwd} = file:get_cwd(), + ?line ok = file:set_cwd(Temp), + ?line <<Drive:8,":/temp/foo">> = filename:absname(<<"foo">>), + ?line <<Drive:8,":/temp/../ebin">> = filename:absname(<<"../ebin">>), + ?line <<Drive:8,":/erlang">> = filename:absname(<<"/erlang">>), + ?line <<Drive:8,":/erlang/src">> = filename:absname(<<"/erlang/src">>), + ?line <<Drive:8,":/erlang/src">> = filename:absname(<<"\\erlang\\src">>), + ?line <<Drive:8,":/temp/erlang">> = filename:absname(<<Drive:8,":erlang">>), + ?line <<Drive:8,":/temp/erlang/src">> = + filename:absname(<<Drive:8,":erlang/src">>), + ?line <<Drive:8,":/temp/erlang/src">> = + filename:absname(<<Drive:8,":erlang\\src\\">>), + ?line <<"a:/erlang">> = filename:absname(<<"a:erlang">>), + + ?line file:set_cwd(<<Drive:8,":/">>), + ?line <<Drive:8,":/foo">> = filename:absname(<<"foo">>), + ?line <<Drive:8,":/../ebin">> = filename:absname(<<"../ebin">>), + ?line <<Drive:8,":/erlang">> = filename:absname(<<"/erlang">>), + ?line <<Drive:8,":/erlang/src">> = filename:absname(<<"/erlang/src">>), + ?line <<Drive:8,":/erlang/src">> = filename:absname(<<"\\erlang\\\\src">>), + ?line <<Drive:8,":/erlang">> = filename:absname(<<Drive:8,":erlang">>), + ?line <<Drive:8,":/erlang/src">> = filename:absname(<<Drive:8,":erlang/src">>), + ?line <<"a:/erlang">> = filename:absname(<<"a:erlang">>), + + ?line file:set_cwd(Cwd), + ok; + {unix, _} -> + ?line ok = file:set_cwd(<<"/usr">>), + ?line <<"/usr/foo">> = filename:absname(<<"foo">>), + ?line <<"/usr/../ebin">> = filename:absname(<<"../ebin">>), + + ?line file:set_cwd(<<"/">>), + ?line <<"/foo">> = filename:absname(<<"foo">>), + ?line <<"/../ebin">> = filename:absname(<<"../ebin">>), + ?line <<"/erlang">> = filename:absname(<<"/erlang">>), + ?line <<"/erlang/src">> = filename:absname(<<"/erlang/src">>), + ?line <<"/erlang/src">> = filename:absname(<<"/erlang///src">>), + ok + end. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +absname_bin_2(Config) when is_list(Config) -> + case os:type() of + {win32, _} -> + ?line [Drive|_] = ?config(priv_dir, Config), + ?line <<Drive:8,":/temp/foo">> = filename:absname(<<"foo">>, <<Drive:8,":/temp">>), + ?line <<Drive:8,":/temp/../ebin">> = filename:absname(<<"../ebin">>, + <<Drive:8,":/temp">>), + ?line <<Drive:8,":/erlang">> = filename:absname(<<"/erlang">>, <<Drive:8,":/temp">>), + ?line <<Drive:8,":/erlang/src">> = filename:absname(<<"/erlang/src">>, + <<Drive:8,":/temp">>), + ?line <<Drive:8,":/erlang/src">> = filename:absname(<<"\\erlang\\src">>, + <<Drive:8,":/temp">>), + ?line <<Drive:8,":/temp/erlang">> = filename:absname(<<Drive:8,":erlang">>, + <<Drive:8,":/temp">>), + ?line <<Drive:8,":/temp/erlang/src">> = filename:absname(<<Drive:8,":erlang/src">>, + <<Drive:8,":/temp">>), + ?line <<Drive:8,":/temp/erlang/src">> = + filename:absname(<<Drive:8,":erlang\\src\\">>, <<Drive:8,":/temp">>), + ?line <<"a:/erlang">> = filename:absname(<<"a:erlang">>, <<Drive:8,":/temp">>), + + ?line file:set_cwd(<<Drive:8,":/">>), + ?line <<Drive:8,":/foo">> = filename:absname(foo, <<Drive:8,":/">>), + ?line <<Drive:8,":/foo">> = filename:absname(<<"foo">>, <<Drive:8,":/">>), + ?line <<Drive:8,":/../ebin">> = filename:absname(<<"../ebin">>, <<Drive:8,":/">>), + ?line <<Drive:8,":/erlang">> = filename:absname(<<"/erlang">>, <<Drive:8,":/">>), + ?line <<Drive:8,":/erlang/src">> = filename:absname(<<"/erlang/src">>, + <<Drive:8,":/">>), + ?line <<Drive:8,":/erlang/src">> = filename:absname(<<"\\erlang\\\\src">>, + <<Drive:8,":/">>), + ?line <<Drive:8,":/erlang">> = filename:absname(<<Drive:8,":erlang">>, + <<Drive:8,":/">>), + ?line <<Drive:8,":/erlang/src">> = filename:absname(<<Drive:8,":erlang/src">>, + <<Drive:8,":/">>), + ?line <<"a:/erlang">> = filename:absname(<<"a:erlang">>, <<Drive:8,":/">>), + + ok; + {unix, _} -> + ?line <<"/usr/foo">> = filename:absname(<<"foo">>, <<"/usr">>), + ?line <<"/usr/../ebin">> = filename:absname(<<"../ebin">>, <<"/usr">>), + + ?line <<"/foo">> = filename:absname(<<"foo">>, <<"/">>), + ?line <<"/../ebin">> = filename:absname(<<"../ebin">>, <<"/">>), + ?line <<"/erlang">> = filename:absname(<<"/erlang">>, <<"/">>), + ?line <<"/erlang/src">> = filename:absname(<<"/erlang/src">>, <<"/">>), + ?line <<"/erlang/src">> = filename:absname(<<"/erlang///src">>, <<"/">>), + ok + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +basename_bin_1(Config) when is_list(Config) -> + ?line Dog = test_server:timetrap(test_server:seconds(10)), + ?line <<".">> = filename:basename(<<".">>), + ?line <<"foo">> = filename:basename(<<"foo">>), + ?line <<"foo">> = filename:basename(<<"/usr/foo">>), + ?line <<"foo.erl">> = filename:basename(<<"A:usr/foo.erl">>), + ?line case os:type() of + {win32, _} -> + ?line <<"foo">> = filename:basename(<<"A:\\usr\\foo">>), + ?line <<"foo">> = filename:basename(<<"A:foo">>); + {unix, _} -> + ?line <<"strange\\but\\true">> = + filename:basename(<<"strange\\but\\true">>) + end, + ?line test_server:timetrap_cancel(Dog), + ok. + +basename_bin_2(Config) when is_list(Config) -> + ?line Dog = test_server:timetrap(test_server:seconds(10)), + ?line <<".">> = filename:basename(<<".">>, <<".erl">>), + ?line <<"foo">> = filename:basename(<<"foo.erl">>, <<".erl">>), + ?line <<"foo.erl">> = filename:basename(<<"/usr/foo.erl">>, <<".hrl">>), + ?line <<"foo.erl">> = filename:basename(<<"/usr.hrl/foo.erl">>, <<".hrl">>), + ?line <<"foo">> = filename:basename(<<"/usr.hrl/foo">>, <<".hrl">>), + ?line <<"foo">> = filename:basename(<<"usr/foo/">>, <<".erl">>), + ?line <<"foo.erl">> = filename:basename(<<"usr/foo.erl/">>, <<".erl">>), + ?line case os:type() of + {win32, _} -> + ?line <<"foo">> = filename:basename(<<"A:foo">>, <<".erl">>), + ?line <<"foo.erl">> = filename:basename(<<"a:\\usr\\foo.erl">>, + <<".hrl">>), + ?line <<"foo.erl">> = filename:basename(<<"c:\\usr.hrl\\foo.erl">>, + <<".hrl">>), + ?line <<"foo">> = filename:basename(<<"A:\\usr\\foo">>, <<".hrl">>); + {unix, _} -> + ?line <<"strange\\but\\true">> = + filename:basename(<<"strange\\but\\true.erl">>, <<".erl">>), + ?line <<"strange\\but\\true">> = + filename:basename(<<"strange\\but\\true">>, <<".erl">>) + end, + ?line test_server:timetrap_cancel(Dog), + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +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:">>); + _ -> 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, + 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. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +join_bin(Config) when is_list(Config) -> + ?line <<"/">> = filename:join([<<"/">>]), + ?line <<"/">> = filename:join([<<"//">>]), + ?line <<"usr/foo.erl">> = filename:join(<<"usr">>,<<"foo.erl">>), + ?line <<"/src/foo.erl">> = filename:join(usr, <<"/src/foo.erl">>), + ?line <<"/src/foo.erl">> = filename:join([<<"/src/">>,'foo.erl']), + ?line <<"/src/foo.erl">> = filename:join(<<"usr">>, ["/sr", 'c/foo.erl']), + ?line <<"/src/foo.erl">> = filename:join(<<"usr">>, <<"/src/foo.erl">>), + + %% Make sure that redundant slashes work too. + ?line <<"a/b/c/d/e/f/g">> = filename:join([<<"a//b/c/////d//e/f/g">>]), + ?line <<"a/b/c/d/e/f/g">> = filename:join([<<"a//b/c/">>, <<"d//e/f/g">>]), + ?line <<"a/b/c/d/e/f/g">> = filename:join([<<"a//b/c">>, <<"d//e/f/g">>]), + ?line <<"/d/e/f/g">> = filename:join([<<"a//b/c">>, <<"/d//e/f/g">>]), + ?line <<"/d/e/f/g">> = filename:join([<<"a//b/c">>, <<"//d//e/f/g">>]), + + ?line <<"foo/bar">> = filename:join([$f,$o,$o,$/,[]], <<"bar">>), + + ?line case os:type() of + {win32, _} -> + ?line <<"d:/">> = filename:join([<<"D:/">>]), + ?line <<"d:/">> = filename:join([<<"D:\\">>]), + ?line <<"d:/abc">> = filename:join([<<"D:/">>, <<"abc">>]), + ?line <<"d:abc">> = filename:join([<<"D:">>, <<"abc">>]), + ?line <<"a/b/c/d/e/f/g">> = + filename:join([<<"a//b\\c//\\/\\d/\\e/f\\g">>]), + ?line <<"a:usr/foo.erl">> = + filename:join([<<"A:">>,<<"usr">>,<<"foo.erl">>]), + ?line <<"/usr/foo.erl">> = + filename:join([<<"A:">>,<<"/usr">>,<<"foo.erl">>]), + ?line <<"c:usr">> = filename:join(<<"A:">>,<<"C:usr">>), + ?line <<"a:usr">> = filename:join(<<"A:">>,<<"usr">>), + ?line <<"c:/usr">> = filename:join(<<"A:">>, <<"C:/usr">>), + ?line <<"c:/usr/foo.erl">> = + filename:join([<<"A:">>,<<"C:/usr">>,<<"foo.erl">>]), + ?line <<"c:usr/foo.erl">> = + filename:join([<<"A:">>,<<"C:usr">>,<<"foo.erl">>]), + ?line <<"d:/foo">> = filename:join([$D, $:, $/, []], <<"foo">>), + ok; + {unix, _} -> + ok + 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'), + case os:type() of + {win32, _} -> + ?line volumerelative = filename:pathtype(<<"/usr/local/bin">>), + ?line volumerelative = filename:pathtype(<<"A:usr/local/bin">>), + ok; + {unix, _} -> + ?line absolute = filename:pathtype(<<"/">>), + ?line 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">>), + 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">>), + case os:type() of + {win32,_} -> + ?line [<<"a:/">>,<<"msdev">>,<<"include">>] = + filename:split(<<"a:/msdev/include">>), + ?line [<<"a:/">>,<<"msdev">>,<<"include">>] = + filename:split(<<"A:/msdev/include">>), + ?line [<<"msdev">>,<<"include">>] = + filename:split(<<"msdev\\include">>), + ?line [<<"a:/">>,<<"msdev">>,<<"include">>] = + filename:split(<<"a:\\msdev\\include">>), + ?line [<<"a:">>,<<"msdev">>,<<"include">>] = + filename:split(<<"a:msdev\\include">>), + ok; + _ -> + ok + end. + diff --git a/lib/stdlib/test/fixtable_SUITE.erl b/lib/stdlib/test/fixtable_SUITE.erl index 1940ee147e..57fe4c4508 100644 --- a/lib/stdlib/test/fixtable_SUITE.erl +++ b/lib/stdlib/test/fixtable_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2010. All Rights Reserved. +%% Copyright Ericsson AB 1999-2011. 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,22 +21,41 @@ %%%---------------------------------------------------------------------- -module(fixtable_SUITE). --export([all/1]). +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2]). %%% Test cases -export([multiple_fixes/1, multiple_processes/1, other_process_deletes/1, owner_dies/1, other_process_closes/1,insert_same_key/1]). -export([fixbag/1]). --export([init_per_testcase/2, fin_per_testcase/2]). +-export([init_per_testcase/2, end_per_testcase/2]). %%% Internal exports -export([command_loop/0,start_commander/0]). -all(suite) -> {req, [stdlib], - [multiple_fixes, multiple_processes, - other_process_deletes, owner_dies, - other_process_closes,insert_same_key,fixbag]}. +suite() -> [{ct_hooks,[ts_install_cth]}]. --include("test_server.hrl"). +all() -> + [multiple_fixes, multiple_processes, + other_process_deletes, owner_dies, other_process_closes, + insert_same_key, fixbag]. + +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + +-include_lib("test_server/include/test_server.hrl"). %%% I wrote this thinking I would use more than one temporary at a time, but %%% I wasn't... Well, maybe in the future... @@ -53,7 +72,7 @@ init_per_testcase(_Func, Config) -> Dog=test_server:timetrap(test_server:seconds(60)), [{watchdog, Dog}|Config]. -fin_per_testcase(_Func, Config) -> +end_per_testcase(_Func, Config) -> Dog=?config(watchdog, Config), test_server:timetrap_cancel(Dog), lists:foreach(fun(X) -> diff --git a/lib/stdlib/test/format_SUITE.erl b/lib/stdlib/test/format_SUITE.erl index 1c9e953003..68e17a0459 100644 --- a/lib/stdlib/test/format_SUITE.erl +++ b/lib/stdlib/test/format_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2010. All Rights Reserved. +%% Copyright Ericsson AB 1998-2011. 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,13 +17,14 @@ %% %CopyrightEnd% %% -module(format_SUITE). --export([all/1]). +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2]). -export([hang_1/1]). --export([init_per_testcase/2, fin_per_testcase/2]). +-export([init_per_testcase/2, end_per_testcase/2]). --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). % Default timetrap timeout (set in init_per_testcase). -define(default_timeout, ?t:minutes(1)). @@ -31,16 +32,32 @@ init_per_testcase(_Case, Config) -> ?line Dog = ?t:timetrap(?default_timeout), [{watchdog, Dog} | Config]. -fin_per_testcase(_Case, Config) -> +end_per_testcase(_Case, Config) -> Dog = ?config(watchdog, Config), test_server:timetrap_cancel(Dog), ok. -all(doc) -> - ["Test cases for io:format/[2,3]."]; -all(suite) -> +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> [hang_1]. +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + hang_1(doc) -> ["Bad args can hang (OTP-2400)"]; hang_1(suite) -> diff --git a/lib/stdlib/test/gen_event_SUITE.erl b/lib/stdlib/test/gen_event_SUITE.erl index 8cbffaca56..b3a7edc140 100644 --- a/lib/stdlib/test/gen_event_SUITE.erl +++ b/lib/stdlib/test/gen_event_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. 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,14 +18,40 @@ %% -module(gen_event_SUITE). --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). --export([all/1]). --export([start/1, test_all/1, add_handler/1, add_sup_handler/1, +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2]). +-export([start/1, add_handler/1, add_sup_handler/1, delete_handler/1, swap_handler/1, swap_sup_handler/1, - notify/1, sync_notify/1, call/1, info/1, hibernate/1]). + notify/1, sync_notify/1, call/1, info/1, hibernate/1, + call_format_status/1, call_format_status_anon/1, + error_format_status/1]). + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [start, {group, test_all}, hibernate, + call_format_status, call_format_status_anon, error_format_status]. + +groups() -> + [{test_all, [], + [add_handler, add_sup_handler, delete_handler, + swap_handler, swap_sup_handler, notify, sync_notify, + call, info]}]. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. -all(suite) -> {req, [stdlib], [start, test_all, hibernate]}. %% -------------------------------------- %% Start an event manager. @@ -169,9 +195,6 @@ hibernate(Config) when is_list(Config) -> ok. -test_all(suite) -> [add_handler, add_sup_handler, delete_handler, - swap_handler, swap_sup_handler, notify, - sync_notify, call, info]. add_handler(doc) -> []; add_handler(suite) -> []; @@ -844,3 +867,72 @@ info(Config) when is_list(Config) -> ?line ok = gen_event:stop(my_dummy_handler), ok. + +call_format_status(suite) -> + []; +call_format_status(doc) -> + ["Test that sys:get_status/1,2 calls format_status/2"]; +call_format_status(Config) when is_list(Config) -> + ?line {ok, Pid} = gen_event:start({local, my_dummy_handler}), + %% State here intentionally differs from what we expect from format_status + State = self(), + FmtState = "dummy1_h handler state", + ?line ok = gen_event:add_handler(my_dummy_handler, dummy1_h, [State]), + ?line Status1 = sys:get_status(Pid), + ?line Status2 = sys:get_status(Pid, 5000), + ?line ok = gen_event:stop(Pid), + ?line {status, Pid, _, [_, _, Pid, [], Data1]} = Status1, + ?line HandlerInfo1 = proplists:get_value(items, Data1), + ?line {"Installed handlers", [{_,dummy1_h,_,FmtState,_}]} = HandlerInfo1, + ?line {status, Pid, _, [_, _, Pid, [], Data2]} = Status2, + ?line HandlerInfo2 = proplists:get_value(items, Data2), + ?line {"Installed handlers", [{_,dummy1_h,_,FmtState,_}]} = HandlerInfo2, + ok. + +call_format_status_anon(suite) -> + []; +call_format_status_anon(doc) -> + ["Test that sys:get_status/1,2 calls format_status/2 for anonymous gen_event processes"]; +call_format_status_anon(Config) when is_list(Config) -> + ?line {ok, Pid} = gen_event:start(), + %% The 'Name' of the gen_event process will be a pid() here, so + %% the next line will crash if format_status can't string-ify pids. + ?line Status1 = sys:get_status(Pid), + ?line ok = gen_event:stop(Pid), + Header = "Status for event handler " ++ pid_to_list(Pid), + ?line {status, Pid, _, [_, _, Pid, [], Data1]} = Status1, + ?line Header = proplists:get_value(header, Data1), + ok. + + +error_format_status(suite) -> + []; +error_format_status(doc) -> + ["Test that a handler error calls format_status/2"]; +error_format_status(Config) when is_list(Config) -> + ?line error_logger_forwarder:register(), + OldFl = process_flag(trap_exit, true), + State = self(), + ?line {ok, Pid} = gen_event:start({local, my_dummy_handler}), + ?line ok = gen_event:add_sup_handler(my_dummy_handler, dummy1_h, [State]), + ?line ok = gen_event:notify(my_dummy_handler, do_crash), + ?line receive + {gen_event_EXIT,dummy1_h,{'EXIT',_}} -> ok + after 5000 -> + ?t:fail(exit_gen_event) + end, + FmtState = "dummy1_h handler state", + receive + {error,_GroupLeader, {Pid, + "** gen_event handler"++_, + [dummy1_h,my_dummy_handler,do_crash, + FmtState, _]}} -> + ok; + Other -> + ?line io:format("Unexpected: ~p", [Other]), + ?line ?t:fail() + end, + ?t:messages_get(), + ?line ok = gen_event:stop(Pid), + process_flag(trap_exit, OldFl), + ok. diff --git a/lib/stdlib/test/gen_fsm_SUITE.erl b/lib/stdlib/test/gen_fsm_SUITE.erl index 23c1d9a193..d60629d841 100644 --- a/lib/stdlib/test/gen_fsm_SUITE.erl +++ b/lib/stdlib/test/gen_fsm_SUITE.erl @@ -1,36 +1,37 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1996-2011. 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(gen_fsm_SUITE). --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). %% Test cases --export([all/1]). +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2]). --export([start/1, start1/1, start2/1, start3/1, start4/1 , start5/1, start6/1, +-export([ start1/1, start2/1, start3/1, start4/1 , start5/1, start6/1, start7/1, start8/1, start9/1, start10/1, start11/1]). --export([abnormal/1, abnormal1/1, abnormal2/1]). +-export([ abnormal1/1, abnormal2/1]). -export([shutdown/1]). --export([sys/1, sys1/1, call_format_status/1]). +-export([ sys1/1, call_format_status/1, error_format_status/1]). -export([hibernate/1,hiber_idle/3,hiber_wakeup/3,hiber_idle/2,hiber_wakeup/2]). @@ -53,13 +54,31 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -all(suite) -> - [start, abnormal, shutdown, sys, hibernate, enter_loop]. - +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [{group, start}, {group, abnormal}, shutdown, + {group, sys}, hibernate, enter_loop]. +groups() -> + [{start, [], + [start1, start2, start3, start4, start5, start6, start7, + start8, start9, start10, start11]}, + {abnormal, [], [abnormal1, abnormal2]}, + {sys, [], + [sys1, call_format_status, error_format_status]}]. -start(suite) -> [start1, start2, start3, start4, start5, start6, start7, - start8, start9, start10, start11]. +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. %% anonymous start1(Config) when is_list(Config) -> @@ -239,7 +258,6 @@ start11(Config) when is_list(Config) -> test_server:messages_get(), ok. -abnormal(suite) -> [abnormal1, abnormal2]. %% Check that time outs in calls work abnormal1(suite) -> []; @@ -305,7 +323,6 @@ shutdown(Config) when is_list(Config) -> ok. -sys(suite) -> [sys1, call_format_status]. sys1(Config) when is_list(Config) -> ?line {ok, Pid} = @@ -320,10 +337,53 @@ sys1(Config) when is_list(Config) -> call_format_status(Config) when is_list(Config) -> ?line {ok, Pid} = gen_fsm:start(gen_fsm_SUITE, [], []), ?line Status = sys:get_status(Pid), - ?line {status, Pid, _Mod, [_PDict, running, _Parent, _, Data]} = Status, + ?line {status, Pid, _Mod, [_PDict, running, _, _, Data]} = Status, ?line [format_status_called | _] = lists:reverse(Data), - ?line stop_it(Pid). + ?line stop_it(Pid), + %% check that format_status can handle a name being an atom (pid is + %% already checked by the previous test) + ?line {ok, Pid2} = gen_fsm:start({local, gfsm}, gen_fsm_SUITE, [], []), + ?line Status2 = sys:get_status(gfsm), + ?line {status, Pid2, _Mod, [_PDict2, running, _, _, Data2]} = Status2, + ?line [format_status_called | _] = lists:reverse(Data2), + ?line stop_it(Pid2), + + %% check that format_status can handle a name being a term other than a + %% pid or atom + GlobalName1 = {global, "CallFormatStatus"}, + ?line {ok, Pid3} = gen_fsm:start(GlobalName1, gen_fsm_SUITE, [], []), + ?line Status3 = sys:get_status(GlobalName1), + ?line {status, Pid3, _Mod, [_PDict3, running, _, _, Data3]} = Status3, + ?line [format_status_called | _] = lists:reverse(Data3), + ?line stop_it(Pid3), + GlobalName2 = {global, {name, "term"}}, + ?line {ok, Pid4} = gen_fsm:start(GlobalName2, gen_fsm_SUITE, [], []), + ?line Status4 = sys:get_status(GlobalName2), + ?line {status, Pid4, _Mod, [_PDict4, running, _, _, Data4]} = Status4, + ?line [format_status_called | _] = lists:reverse(Data4), + ?line stop_it(Pid4). + +error_format_status(Config) when is_list(Config) -> + ?line error_logger_forwarder:register(), + OldFl = process_flag(trap_exit, true), + StateData = "called format_status", + ?line {ok, Pid} = gen_fsm:start(gen_fsm_SUITE, {state_data, StateData}, []), + %% bad return value in the gen_fsm loop + ?line {'EXIT',{{bad_return_value, badreturn},_}} = + (catch gen_fsm:sync_send_event(Pid, badreturn)), + receive + {error,_GroupLeader,{Pid, + "** State machine"++_, + [Pid,{_,_,badreturn},idle,StateData,_]}} -> + ok; + Other -> + ?line io:format("Unexpected: ~p", [Other]), + ?line ?t:fail() + end, + ?t:messages_get(), + process_flag(trap_exit, OldFl), + ok. %% Hibernation hibernate(suite) -> []; @@ -704,6 +764,8 @@ init(hiber) -> {ok, hiber_idle, []}; init(hiber_now) -> {ok, hiber_idle, [], hibernate}; +init({state_data, StateData}) -> + {ok, idle, StateData}; init(_) -> {ok, idle, state_data}. @@ -844,5 +906,7 @@ handle_sync_event(stop_shutdown_reason, _From, _State, Data) -> handle_sync_event({get, _Pid}, _From, State, Data) -> {reply, {state, State, Data}, State, Data}. -format_status(_Opt, [_Pdict, _StateData]) -> +format_status(terminate, [_Pdict, StateData]) -> + StateData; +format_status(normal, [_Pdict, _StateData]) -> [format_status_called]. diff --git a/lib/stdlib/test/gen_server_SUITE.erl b/lib/stdlib/test/gen_server_SUITE.erl index 6efdce78a1..a614d6595d 100644 --- a/lib/stdlib/test/gen_server_SUITE.erl +++ b/lib/stdlib/test/gen_server_SUITE.erl @@ -1,36 +1,38 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1996-2011. 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(gen_server_SUITE). --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). -include_lib("kernel/include/inet.hrl"). --export([init_per_testcase/2, fin_per_testcase/2]). +-export([init_per_testcase/2, end_per_testcase/2]). --export([all/1]). +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2]). -export([start/1, crash/1, call/1, cast/1, cast_fast/1, info/1, abcast/1, multicall/1, multicall_down/1, call_remote1/1, call_remote2/1, call_remote3/1, call_remote_n1/1, call_remote_n2/1, call_remote_n3/1, spec_init/1, spec_init_local_registered_parent/1, spec_init_global_registered_parent/1, - otp_5854/1, hibernate/1, otp_7669/1, call_format_status/1 + otp_5854/1, hibernate/1, otp_7669/1, call_format_status/1, + error_format_status/1, call_with_huge_message_queue/1 ]). % spawn export @@ -44,21 +46,55 @@ -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, format_status/2]). -all(suite) -> - [start, crash, call, cast, cast_fast, info, - abcast, multicall, multicall_down, call_remote1, - call_remote2, call_remote3, call_remote_n1, - call_remote_n2, call_remote_n3, spec_init, +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [start, crash, call, cast, cast_fast, info, abcast, + multicall, multicall_down, call_remote1, call_remote2, + call_remote3, call_remote_n1, call_remote_n2, + call_remote_n3, spec_init, spec_init_local_registered_parent, - spec_init_global_registered_parent, - otp_5854, hibernate, otp_7669, call_format_status]. + spec_init_global_registered_parent, otp_5854, hibernate, + otp_7669, call_format_status, error_format_status, + call_with_huge_message_queue]. + +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + -define(default_timeout, ?t:minutes(1)). +init_per_testcase(Case, Config) when Case == call_remote1; + Case == call_remote2; + Case == call_remote3; + Case == call_remote_n1; + Case == call_remote_n2; + Case == call_remote_n3 -> + {ok,N} = start_node(hubba), + ?line Dog = ?t:timetrap(?default_timeout), + [{node,N},{watchdog, Dog} | Config]; init_per_testcase(_Case, Config) -> ?line Dog = ?t:timetrap(?default_timeout), [{watchdog, Dog} | Config]. -fin_per_testcase(_Case, Config) -> +end_per_testcase(_Case, Config) -> + case proplists:get_value(node, Config) of + undefined -> + ok; + N -> + test_server:stop_node(N) + end, Dog = ?config(watchdog, Config), test_server:timetrap_cancel(Dog), ok. @@ -291,8 +327,8 @@ start_node(Name) -> call_remote1(suite) -> []; call_remote1(Config) when is_list(Config) -> - ?line N = hubba, - ?line {ok, Node} = start_node(N), + N = hubba, + ?line Node = proplists:get_value(node,Config), ?line {ok, Pid} = rpc:call(Node, gen_server, start, [{global, N}, ?MODULE, [], []]), ?line ok = (catch gen_server:call({global, N}, started_p, infinity)), @@ -305,7 +341,7 @@ call_remote1(Config) when is_list(Config) -> call_remote2(suite) -> []; call_remote2(Config) when is_list(Config) -> ?line N = hubba, - ?line {ok, Node} = start_node(N), + ?line Node = proplists:get_value(node,Config), ?line {ok, Pid} = rpc:call(Node, gen_server, start, [{global, N}, ?MODULE, [], []]), @@ -318,8 +354,7 @@ call_remote2(Config) when is_list(Config) -> call_remote3(suite) -> []; call_remote3(Config) when is_list(Config) -> - ?line N = hubba, - ?line {ok, Node} = start_node(N), + ?line Node = proplists:get_value(node,Config), ?line {ok, Pid} = rpc:call(Node, gen_server, start, [{local, piller}, ?MODULE, [], []]), @@ -337,7 +372,7 @@ call_remote3(Config) when is_list(Config) -> call_remote_n1(suite) -> []; call_remote_n1(Config) when is_list(Config) -> ?line N = hubba, - ?line {ok, Node} = start_node(N), + ?line Node = proplists:get_value(node,Config), ?line {ok, _Pid} = rpc:call(Node, gen_server, start, [{global, N}, ?MODULE, [], []]), ?line _ = test_server:stop_node(Node), @@ -349,7 +384,7 @@ call_remote_n1(Config) when is_list(Config) -> call_remote_n2(suite) -> []; call_remote_n2(Config) when is_list(Config) -> ?line N = hubba, - ?line {ok, Node} = start_node(N), + ?line Node = proplists:get_value(node,Config), ?line {ok, Pid} = rpc:call(Node, gen_server, start, [{global, N}, ?MODULE, [], []]), @@ -361,8 +396,7 @@ call_remote_n2(Config) when is_list(Config) -> call_remote_n3(suite) -> []; call_remote_n3(Config) when is_list(Config) -> - ?line N = hubba, - ?line {ok, Node} = start_node(N), + ?line Node = proplists:get_value(node,Config), ?line {ok, _Pid} = rpc:call(Node, gen_server, start, [{local, piller}, ?MODULE, [], []]), @@ -895,15 +929,105 @@ call_format_status(doc) -> ["Test that sys:get_status/1,2 calls format_status/2"]; call_format_status(Config) when is_list(Config) -> ?line {ok, Pid} = gen_server:start_link({local, call_format_status}, - gen_server_SUITE, [], []), + ?MODULE, [], []), ?line Status1 = sys:get_status(call_format_status), ?line {status, Pid, _Mod, [_PDict, running, _Parent, _, Data1]} = Status1, ?line [format_status_called | _] = lists:reverse(Data1), ?line Status2 = sys:get_status(call_format_status, 5000), ?line {status, Pid, _Mod, [_PDict, running, _Parent, _, Data2]} = Status2, ?line [format_status_called | _] = lists:reverse(Data2), + + %% check that format_status can handle a name being a pid (atom is + %% already checked by the previous test) + ?line {ok, Pid3} = gen_server:start_link(gen_server_SUITE, [], []), + ?line Status3 = sys:get_status(Pid3), + ?line {status, Pid3, _Mod, [_PDict3, running, _Parent, _, Data3]} = Status3, + ?line [format_status_called | _] = lists:reverse(Data3), + + %% check that format_status can handle a name being a term other than a + %% pid or atom + GlobalName1 = {global, "CallFormatStatus"}, + ?line {ok, Pid4} = gen_server:start_link(GlobalName1, + gen_server_SUITE, [], []), + ?line Status4 = sys:get_status(Pid4), + ?line {status, Pid4, _Mod, [_PDict4, running, _Parent, _, Data4]} = Status4, + ?line [format_status_called | _] = lists:reverse(Data4), + GlobalName2 = {global, {name, "term"}}, + ?line {ok, Pid5} = gen_server:start_link(GlobalName2, + gen_server_SUITE, [], []), + ?line Status5 = sys:get_status(GlobalName2), + ?line {status, Pid5, _Mod, [_PDict5, running, _Parent, _, Data5]} = Status5, + ?line [format_status_called | _] = lists:reverse(Data5), + ok. + +%% Verify that error termination correctly calls our format_status/2 fun +%% +error_format_status(suite) -> + []; +error_format_status(doc) -> + ["Test that an error termination calls format_status/2"]; +error_format_status(Config) when is_list(Config) -> + ?line error_logger_forwarder:register(), + OldFl = process_flag(trap_exit, true), + State = "called format_status", + ?line {ok, Pid} = gen_server:start_link(?MODULE, {state, State}, []), + ?line {'EXIT',{crashed,_}} = (catch gen_server:call(Pid, crash)), + receive + {'EXIT', Pid, crashed} -> + ok + end, + receive + {error,_GroupLeader,{Pid, + "** Generic server"++_, + [Pid,crash,State,crashed]}} -> + ok; + Other -> + ?line io:format("Unexpected: ~p", [Other]), + ?line ?t:fail() + end, + ?t:messages_get(), + process_flag(trap_exit, OldFl), ok. +%% Test that the time for a huge message queue is not +%% significantly slower than with an empty message queue. +call_with_huge_message_queue(Config) when is_list(Config) -> + ?line Pid = spawn_link(fun echo_loop/0), + + ?line {Time,ok} = tc(fun() -> calls(10, Pid) end), + + ?line [self() ! {msg,N} || N <- lists:seq(1, 500000)], + erlang:garbage_collect(), + ?line {NewTime,ok} = tc(fun() -> calls(10, Pid) end), + io:format("Time for empty message queue: ~p", [Time]), + io:format("Time for huge message queue: ~p", [NewTime]), + + case (NewTime+1) / (Time+1) of + Q when Q < 10 -> + ok; + Q -> + io:format("Q = ~p", [Q]), + ?line ?t:fail() + end, + ok. + +calls(0, _) -> ok; +calls(N, Pid) -> + {ultimate_answer,42} = call(Pid, {ultimate_answer,42}), + calls(N-1, Pid). + +call(Pid, Msg) -> + gen_server:call(Pid, Msg, infinity). + +tc(Fun) -> + timer:tc(erlang, apply, [Fun,[]]). + +echo_loop() -> + receive + {'$gen_call',{Pid,Ref},Msg} -> + Pid ! {Ref,Msg}, + echo_loop() + end. %%-------------------------------------------------------------- %% Help functions to spec_init_* @@ -1064,5 +1188,7 @@ terminate({From, stopped_info}, _State) -> terminate(_Reason, _State) -> ok. -format_status(_Opt, [_PDict, _State]) -> - [format_status_called]. +format_status(terminate, [_PDict, State]) -> + State; +format_status(normal, [_PDict, _State]) -> + format_status_called. diff --git a/lib/stdlib/test/id_transform_SUITE.erl b/lib/stdlib/test/id_transform_SUITE.erl index 95ee509833..e1972a100e 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-2009. All Rights Reserved. +%% Copyright Ericsson AB 2003-2011. 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,17 +21,37 @@ -include_lib("kernel/include/file.hrl"). --export([all/1, +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2, 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]). -% Serves as test... + % Serves as test... -hej(hopp). --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [id_transform]. + +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. -all(suite) -> [id_transform]. id_transform(doc) -> "Test erl_id_trans."; id_transform(Config) when is_list(Config) -> diff --git a/lib/stdlib/test/io_SUITE.erl b/lib/stdlib/test/io_SUITE.erl index 73efeb004a..54a98985cd 100644 --- a/lib/stdlib/test/io_SUITE.erl +++ b/lib/stdlib/test/io_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% Copyright Ericsson AB 1999-2011. 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,15 +18,16 @@ %% -module(io_SUITE). --export([all/1]). +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2]). --export([init_per_testcase/2, fin_per_testcase/2]). +-export([init_per_testcase/2, end_per_testcase/2]). -export([error_1/1, float_g/1, otp_5403/1, otp_5813/1, otp_6230/1, 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]). + io_fread_newlines/1, otp_8989/1]). %-define(debug, true). @@ -37,7 +38,7 @@ -define(t, test_server). -define(privdir(_), "./io_SUITE_priv"). -else. --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). -define(format(S, A), ok). -define(privdir(Conf), ?config(priv_dir, Conf)). -endif. @@ -49,17 +50,35 @@ init_per_testcase(_Case, Config) -> ?line Dog = ?t:timetrap(?default_timeout), [{watchdog, Dog} | Config]. -fin_per_testcase(_Case, _Config) -> +end_per_testcase(_Case, _Config) -> Dog = ?config(watchdog, _Config), test_server:timetrap_cancel(Dog), ok. -all(doc) -> - ["Test cases for io."]; -all(suite) -> - [error_1,float_g,otp_5403,otp_5813,otp_6230,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]. +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [error_1, float_g, otp_5403, otp_5813, otp_6230, + 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]. + +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + error_1(doc) -> ["Error cases for output"]; @@ -1898,3 +1917,81 @@ read_newlines(Fd, Acc, N0) -> eof -> {lists:reverse(Acc),N0} end. + + + +otp_8989(doc) -> + "OTP-8989 io:format for ~F.Ps ignores P in some cases"; +otp_8989(Suite) when is_list(Suite) -> + Hello = "Hello", + ?line " Hello" = fmt("~6.6s", [Hello]), + ?line " Hello" = fmt("~*.6s", [6,Hello]), + ?line " Hello" = fmt("~6.*s", [6,Hello]), + ?line " Hello" = fmt("~*.*s", [6,6,Hello]), + %% + ?line " Hello" = fmt("~6.5s", [Hello]), + ?line " Hello" = fmt("~*.5s", [6,Hello]), + ?line " Hello" = fmt("~6.*s", [5,Hello]), + ?line " Hello" = fmt("~*.*s", [6,5,Hello]), + %% + ?line " Hell" = fmt("~6.4s", [Hello]), + ?line " Hell" = fmt("~*.4s", [6,Hello]), + ?line " Hell" = fmt("~6.*s", [4,Hello]), + ?line " Hell" = fmt("~*.*s", [6,4,Hello]), + %% + ?line "Hello" = fmt("~5.5s", [Hello]), + ?line "Hello" = fmt("~*.5s", [5,Hello]), + ?line "Hello" = fmt("~5.*s", [5,Hello]), + ?line "Hello" = fmt("~*.*s", [5,5,Hello]), + %% + ?line " Hell" = fmt("~5.4s", [Hello]), + ?line " Hell" = fmt("~*.4s", [5,Hello]), + ?line " Hell" = fmt("~5.*s", [4,Hello]), + ?line " Hell" = fmt("~*.*s", [5,4,Hello]), + %% + ?line "Hell" = fmt("~4.4s", [Hello]), + ?line "Hell" = fmt("~*.4s", [4,Hello]), + ?line "Hell" = fmt("~4.*s", [4,Hello]), + ?line "Hell" = fmt("~*.*s", [4,4,Hello]), + %% + ?line " Hel" = fmt("~4.3s", [Hello]), + ?line " Hel" = fmt("~*.3s", [4,Hello]), + ?line " Hel" = fmt("~4.*s", [3,Hello]), + ?line " Hel" = fmt("~*.*s", [4,3,Hello]), + %% + %% + ?line "Hello " = fmt("~-6.6s", [Hello]), + ?line "Hello " = fmt("~*.6s", [-6,Hello]), + ?line "Hello " = fmt("~-6.*s", [6,Hello]), + ?line "Hello " = fmt("~*.*s", [-6,6,Hello]), + %% + ?line "Hello " = fmt("~-6.5s", [Hello]), + ?line "Hello " = fmt("~*.5s", [-6,Hello]), + ?line "Hello " = fmt("~-6.*s", [5,Hello]), + ?line "Hello " = fmt("~*.*s", [-6,5,Hello]), + %% + ?line "Hell " = fmt("~-6.4s", [Hello]), + ?line "Hell " = fmt("~*.4s", [-6,Hello]), + ?line "Hell " = fmt("~-6.*s", [4,Hello]), + ?line "Hell " = fmt("~*.*s", [-6,4,Hello]), + %% + ?line "Hello" = fmt("~-5.5s", [Hello]), + ?line "Hello" = fmt("~*.5s", [-5,Hello]), + ?line "Hello" = fmt("~-5.*s", [5,Hello]), + ?line "Hello" = fmt("~*.*s", [-5,5,Hello]), + %% + ?line "Hell " = fmt("~-5.4s", [Hello]), + ?line "Hell " = fmt("~*.4s", [-5,Hello]), + ?line "Hell " = fmt("~-5.*s", [4,Hello]), + ?line "Hell " = fmt("~*.*s", [-5,4,Hello]), + %% + ?line "Hell" = fmt("~-4.4s", [Hello]), + ?line "Hell" = fmt("~*.4s", [-4,Hello]), + ?line "Hell" = fmt("~-4.*s", [4,Hello]), + ?line "Hell" = fmt("~*.*s", [-4,4,Hello]), + %% + ?line "Hel " = fmt("~-4.3s", [Hello]), + ?line "Hel " = fmt("~*.3s", [-4,Hello]), + ?line "Hel " = fmt("~-4.*s", [3,Hello]), + ?line "Hel " = fmt("~*.*s", [-4,3,Hello]), + ok. diff --git a/lib/stdlib/test/io_proto_SUITE.erl b/lib/stdlib/test/io_proto_SUITE.erl index 93159fbd5b..b69cd74edb 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-2010. All Rights Reserved. +%% Copyright Ericsson AB 2009-2011. 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,16 +17,21 @@ %% %CopyrightEnd% %% -module(io_proto_SUITE). +-compile(r12). --export([all/1]). +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2]). --export([init_per_testcase/2, fin_per_testcase/2]). +-export([init_per_testcase/2, end_per_testcase/2]). --export([setopts_getopts/1,unicode_options/1,unicode_options_gen/1, binary_options/1, bc_with_r12/1, - bc_with_r12_gl/1, read_modes_gl/1,bc_with_r12_ogl/1, read_modes_ogl/1, broken_unicode/1,eof_on_pipe/1,unicode_prompt/1]). +-export([setopts_getopts/1,unicode_options/1,unicode_options_gen/1, + binary_options/1, bc_with_r12/1, + bc_with_r12_gl/1, read_modes_gl/1,bc_with_r12_ogl/1, + read_modes_ogl/1, broken_unicode/1,eof_on_pipe/1,unicode_prompt/1]). --export([io_server_proxy/1,start_io_server_proxy/0, proxy_getall/1, proxy_setnext/2, proxy_quit/1]). +-export([io_server_proxy/1,start_io_server_proxy/0, proxy_getall/1, + proxy_setnext/2, proxy_quit/1]). %% For spawn -export([toerl_server/3,hold_the_line/3,answering_machine1/3, answering_machine2/3]). @@ -41,7 +46,7 @@ -define(t, test_server). -define(privdir(_), "./io_SUITE_priv"). -else. --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). -define(privdir(Conf), ?config(priv_dir, Conf)). -endif. @@ -72,19 +77,36 @@ init_per_testcase(_Case, Config) -> end, os:putenv("TERM","vt100"), [{watchdog, Dog}, {term, Term} | Config]. -fin_per_testcase(_Case, Config) -> +end_per_testcase(_Case, Config) -> Dog = ?config(watchdog, Config), Term = ?config(term,Config), os:putenv("TERM",Term), test_server:timetrap_cancel(Dog), ok. -all(doc) -> - ["Test cases for the io_protocol."]; -all(suite) -> - [setopts_getopts, unicode_options, unicode_options_gen, binary_options, bc_with_r12, - bc_with_r12_gl,bc_with_r12_ogl, read_modes_gl, read_modes_ogl, - broken_unicode,eof_on_pipe,unicode_prompt]. +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [setopts_getopts, unicode_options, unicode_options_gen, + binary_options, bc_with_r12, bc_with_r12_gl, + bc_with_r12_ogl, read_modes_gl, read_modes_ogl, + broken_unicode, eof_on_pipe, unicode_prompt]. + +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + -record(state, { diff --git a/lib/stdlib/test/lists_SUITE.erl b/lib/stdlib/test/lists_SUITE.erl index 0089e874c8..b56f0b39d8 100644 --- a/lib/stdlib/test/lists_SUITE.erl +++ b/lib/stdlib/test/lists_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2011. 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,7 +21,7 @@ %%%----------------------------------------------------------------- -module(lists_SUITE). --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). % Default timetrap timeout (set in init_per_testcase). @@ -30,36 +30,37 @@ -define(default_timeout, ?t:minutes(4)). % Test server specific exports --export([all/1]). --export([init_per_testcase/2, fin_per_testcase/2]). +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2]). +-export([init_per_testcase/2, end_per_testcase/2]). % Test cases must be exported. -export([member/1, reverse/1, keymember/1, keysearch_keyfind/1, keystore/1, keytake/1, - append/1, append_1/1, append_2/1, - seq/1, seq_loop/1, seq_2/1, seq_3/1, seq_2_e/1, seq_3_e/1, - sublist/1, flatten/1, + append_1/1, append_2/1, + seq_loop/1, seq_2/1, seq_3/1, seq_2_e/1, seq_3_e/1, + sublist_2/1, sublist_3/1, sublist_2_e/1, sublist_3_e/1, flatten_1/1, flatten_2/1, flatten_1_e/1, flatten_2_e/1, dropwhile/1, - sort/1, sort_1/1, sort_stable/1, merge/1, rmerge/1, sort_rand/1, - usort/1, usort_1/1, usort_stable/1, umerge/1, rumerge/1,usort_rand/1, + sort_1/1, sort_stable/1, merge/1, rmerge/1, sort_rand/1, + usort_1/1, usort_stable/1, umerge/1, rumerge/1,usort_rand/1, keymerge/1, rkeymerge/1, - keysort/1, keysort_1/1, keysort_i/1, keysort_stable/1, + keysort_1/1, keysort_i/1, keysort_stable/1, keysort_rand/1, keysort_error/1, ukeymerge/1, rukeymerge/1, - ukeysort/1, ukeysort_1/1, ukeysort_i/1, ukeysort_stable/1, + ukeysort_1/1, ukeysort_i/1, ukeysort_stable/1, ukeysort_rand/1, ukeysort_error/1, funmerge/1, rfunmerge/1, - funsort/1, funsort_1/1, funsort_stable/1, funsort_rand/1, + funsort_1/1, funsort_stable/1, funsort_rand/1, funsort_error/1, ufunmerge/1, rufunmerge/1, - ufunsort/1, ufunsort_1/1, ufunsort_stable/1, ufunsort_rand/1, + ufunsort_1/1, ufunsort_stable/1, ufunsort_rand/1, ufunsort_error/1, zip_unzip/1, zip_unzip3/1, zipwith/1, zipwith3/1, filter_partition/1, - tickets/1, otp_5939/1, otp_6023/1, otp_6606/1, otp_7230/1, + otp_5939/1, otp_6023/1, otp_6606/1, otp_7230/1, suffix/1, subtract/1]). %% Sort randomized lists until stopped. @@ -76,21 +77,59 @@ %% %% all/1 %% -all(doc) -> - []; -all(suite) -> - [append, reverse, member, keymember, keysearch_keyfind, keystore, keytake, - dropwhile, - sort, usort, keysort, ukeysort, - funsort, ufunsort, sublist, flatten, seq, - zip_unzip, zip_unzip3, zipwith, zipwith3, - filter_partition, tickets, suffix, subtract]. +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [{group, append}, reverse, member, keymember, + keysearch_keyfind, keystore, keytake, dropwhile, {group,sort}, + {group, usort}, {group, keysort}, {group, ukeysort}, + {group, funsort}, {group, ufunsort}, {group, sublist}, + {group, flatten}, {group, seq}, zip_unzip, zip_unzip3, + zipwith, zipwith3, filter_partition, {group, tickets}, + suffix, subtract]. + +groups() -> + [{append, [], [append_1, append_2]}, + {usort, [], + [umerge, rumerge, usort_1, usort_rand, usort_stable]}, + {keysort, [], + [keymerge, rkeymerge, keysort_1, keysort_rand, + keysort_i, keysort_stable, keysort_error]}, + {sort,[],[merge, rmerge, sort_1, sort_rand]}, + {ukeysort, [], + [ukeymerge, rukeymerge, ukeysort_1, ukeysort_rand, + ukeysort_i, ukeysort_stable, ukeysort_error]}, + {funsort, [], + [funmerge, rfunmerge, funsort_1, funsort_stable, + funsort_error, funsort_rand]}, + {ufunsort, [], + [ufunmerge, rufunmerge, ufunsort_1, ufunsort_stable, + ufunsort_error, ufunsort_rand]}, + {seq, [], [seq_loop, seq_2, seq_3, seq_2_e, seq_3_e]}, + {sublist, [], + [sublist_2, sublist_3, sublist_2_e, sublist_3_e]}, + {flatten, [], + [flatten_1, flatten_2, flatten_1_e, flatten_2_e]}, + {tickets, [], [otp_5939, otp_6023, otp_6606, otp_7230]}]. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + init_per_testcase(_Case, Config) -> ?line Dog=test_server:timetrap(?default_timeout), [{watchdog, Dog}|Config]. -fin_per_testcase(_Case, Config) -> +end_per_testcase(_Case, Config) -> Dog=?config(watchdog, Config), test_server:timetrap_cancel(Dog), ok. @@ -98,10 +137,6 @@ fin_per_testcase(_Case, Config) -> % % Test cases starts here. % -append(doc) -> - ["Tests lists:append/1 & lists:append/2"]; -append(suite) -> - [append_1, append_2]. append_1(doc) -> []; append_1(suite) -> []; @@ -346,12 +381,6 @@ keytake(Config) when is_list(Config) -> ?line false = lists:keytake(4, 2, L), ok. -sort(doc) -> - ["Tests merge functions and lists:sort/1"]; -sort(suite) -> - %% [merge, rmerge, sort_1, sort_rand, sort_stable]. - [merge, rmerge, sort_1, sort_rand]. - merge(doc) -> ["merge functions"]; merge(suite) -> []; merge(Config) when is_list(Config) -> @@ -536,10 +565,6 @@ expl_pid([{I,F} | T], L) when is_function(F) -> expl_pid([], L) -> L. -usort(doc) -> - ["Tests unique merge functions and lists:usort/1"]; -usort(suite) -> - [umerge, rumerge, usort_1, usort_rand, usort_stable]. usort_1(suite) -> []; usort_1(doc) -> [""]; @@ -750,11 +775,6 @@ ucheck_stability(L) -> U = lists:usort(L), check_stab(L, U, S, "usort/1", "ukeysort/2"). -keysort(doc) -> - ["Tests lists:keysort/2"]; -keysort(suite) -> - [keymerge, rkeymerge, - keysort_1, keysort_rand, keysort_i, keysort_stable, keysort_error]. keymerge(doc) -> ["Key merge two lists."]; keymerge(suite) -> []; @@ -946,11 +966,6 @@ keycompare(I, J, A, B) when element(I, A) == element(I, B), element(J, A) =< element(J, B) -> ok. -ukeysort(doc) -> - ["Tests lists:ukeysort/2"]; -ukeysort(suite) -> - [ukeymerge, rukeymerge, - ukeysort_1, ukeysort_rand, ukeysort_i, ukeysort_stable, ukeysort_error]. ukeymerge(suite) -> []; ukeymerge(doc) -> ["Merge two lists while removing duplicates."]; @@ -1240,11 +1255,6 @@ ukeycompare(I, J, A, B) when A =/= B, ok. -funsort(doc) -> - ["Tests lists:sort/2"]; -funsort(suite) -> - [funmerge, rfunmerge, - funsort_1, funsort_stable, funsort_error, funsort_rand]. funmerge(doc) -> ["Merge two lists using a fun."]; funmerge(suite) -> []; @@ -1377,11 +1387,6 @@ funsort_check(I, Input, Expected) -> ?line Expected = funsort(I, Input), check_sorted(I, Input, Expected). -ufunsort(doc) -> - ["Tests lists:usort/2"]; -ufunsort(suite) -> - [ufunmerge, rufunmerge, - ufunsort_1, ufunsort_stable, ufunsort_error, ufunsort_rand]. ufunmerge(suite) -> []; ufunmerge(doc) -> ["Merge two lists while removing duplicates using a fun."]; @@ -2076,12 +2081,6 @@ rkeymerge2_2(_I, T1, _E1, [], M, H1) -> %%%------------------------------------------------------------ -seq(doc) -> - ["Tests lists:seq/3"]; -seq(suite) -> - [ - seq_loop, - seq_2, seq_3, seq_2_e, seq_3_e]. seq_loop(doc) -> ["Test for infinite loop (OTP-2404)."]; @@ -2229,10 +2228,6 @@ property(From, To, Step) -> %%%------------------------------------------------------------ -sublist(doc) -> - ["Tests lists:sublist/[2,3]"]; -sublist(suite) -> - [sublist_2, sublist_3, sublist_2_e, sublist_3_e]. -define(sublist_error2(X,Y), ?line {'EXIT', _} = (catch lists:sublist(X,Y))). -define(sublist_error3(X,Y,Z), ?line {'EXIT', _} = (catch lists:sublist(X,Y,Z))). @@ -2326,10 +2321,6 @@ sublist_3_e(Config) when is_list(Config) -> %%%------------------------------------------------------------ -flatten(doc) -> - ["Tests lists:flatten/[1,2]"]; -flatten(suite) -> - [flatten_1, flatten_2, flatten_1_e, flatten_2_e]. -define(flatten_error1(X), ?line {'EXIT', _} = (catch lists:flatten(X))). -define(flatten_error2(X,Y), ?line {'EXIT', _} = (catch lists:flatten(X,Y))). @@ -2489,10 +2480,6 @@ filpart(F, All, Exp) -> Other = lists:filter(fun(E) -> not F(E) end, All), {Exp,Other} = lists:partition(F, All). -tickets(doc) -> - ["Ticktes."]; -tickets(suite) -> - [otp_5939, otp_6023, otp_6606, otp_7230]. otp_5939(doc) -> ["OTP-5939. Guard tests added."]; otp_5939(suite) -> []; diff --git a/lib/stdlib/test/log_mf_h_SUITE.erl b/lib/stdlib/test/log_mf_h_SUITE.erl index 640261f665..2fd05afb11 100644 --- a/lib/stdlib/test/log_mf_h_SUITE.erl +++ b/lib/stdlib/test/log_mf_h_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. 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,12 +18,32 @@ %% -module(log_mf_h_SUITE). --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). -include_lib("kernel/include/file.hrl"). --export([all/1, test/1]). +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2, test/1]). + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [test]. + +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. -all(suite) -> [test]. %%----------------------------------------------------------------- diff --git a/lib/stdlib/test/ms_transform_SUITE.erl b/lib/stdlib/test/ms_transform_SUITE.erl index 79a0a9af89..4e5df12798 100644 --- a/lib/stdlib/test/ms_transform_SUITE.erl +++ b/lib/stdlib/test/ms_transform_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2010. All Rights Reserved. +%% Copyright Ericsson AB 2003-2011. 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,9 +19,10 @@ -module(ms_transform_SUITE). -author('[email protected]'). --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). --export([all/1]). +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2]). -export([basic_ets/1]). -export([basic_dbg/1]). -export([from_shell/1]). @@ -37,20 +38,122 @@ -export([andalso_orelse/1]). -export([float_1_function/1]). -export([action_function/1]). --export([init_per_testcase/2, fin_per_testcase/2]). +-export([warnings/1]). +-export([init_per_testcase/2, end_per_testcase/2]). init_per_testcase(_Func, Config) -> Dog=test_server:timetrap(test_server:seconds(360)), [{watchdog, Dog}|Config]. -fin_per_testcase(_Func, Config) -> +end_per_testcase(_Func, Config) -> Dog=?config(watchdog, Config), test_server:timetrap_cancel(Dog). -all(suite) -> [from_shell,basic_ets,basic_dbg,records,record_index,multipass, - bitsyntax, record_defaults, andalso_orelse, - float_1_function, action_function, - top_match, old_guards, autoimported, semicolon]. +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [from_shell, basic_ets, basic_dbg, records, + record_index, multipass, bitsyntax, record_defaults, + andalso_orelse, float_1_function, action_function, + warnings, top_match, old_guards, autoimported, + semicolon]. + +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + +%% This may be subject to change +-define(WARN_NUMBER_SHADOW,50). +warnings(suite) -> + []; +warnings(doc) -> + ["Check that shadowed variables in fun head generate warning"]; +warnings(Config) when is_list(Config) -> + ?line setup(Config), + Prog = <<"A=5, " + "ets:fun2ms(fun({A,B}) " + " when is_integer(A) and (A+5 > B) -> " + " A andalso B " + " end)">>, + ?line [{_,[{_,ms_transform,{?WARN_NUMBER_SHADOW,'A'}}]}] = + compile_ww(Prog), + Prog2 = <<"C=5, " + "ets:fun2ms(fun({A,B} = C) " + " when is_integer(A) and (A+5 > B) -> " + " {A andalso B,C} " + " end)">>, + ?line [{_,[{_,ms_transform,{?WARN_NUMBER_SHADOW,'C'}}]}] = + compile_ww(Prog2), + Rec3 = <<"-record(a,{a,b,c,d=foppa}).">>, + Prog3 = <<"A=3,C=5, " + "ets:fun2ms(fun(#a{a = A, b = B} = C) " + " when is_integer(A) and (A+5 > B) -> " + " {A andalso B,C} " + " end)">>, + ?line [{_,[{_,ms_transform,{?WARN_NUMBER_SHADOW,'A'}}, + {_,ms_transform,{?WARN_NUMBER_SHADOW,'C'}}]}] = + compile_ww(Rec3,Prog3), + Rec4 = <<"-record(a,{a,b,c,d=foppa}).">>, + Prog4 = <<"A=3,C=5, " + "F = fun(B) -> B*3 end," + "erlang:display(F(A))," + "ets:fun2ms(fun(#a{a = A, b = B} = C) " + " when is_integer(A) and (A+5 > B) -> " + " {A andalso B,C} " + " end)">>, + ?line [{_,[{_,ms_transform,{?WARN_NUMBER_SHADOW,'A'}}, + {_,ms_transform,{?WARN_NUMBER_SHADOW,'C'}}]}] = + compile_ww(Rec4,Prog4), + Rec5 = <<"-record(a,{a,b,c,d=foppa}).">>, + Prog5 = <<"A=3,C=5, " + "F = fun(B) -> B*3 end," + "erlang:display(F(A))," + "B = ets:fun2ms(fun(#a{a = A, b = B} = C) " + " when is_integer(A) and (A+5 > B) -> " + " {A andalso B,C} " + " end)">>, + ?line [{_,[{_,ms_transform,{?WARN_NUMBER_SHADOW,'A'}}, + {_,ms_transform,{?WARN_NUMBER_SHADOW,'C'}}]}] = + compile_ww(Rec5,Prog5), + Prog6 = <<" X=bar, " + " A = case X of" + " foo ->" + " foo;" + " Y ->" + " ets:fun2ms(fun(Y) ->" % This is a warning + " 3*Y" + " end)" + " end," + " ets:fun2ms(fun(Y) ->" % Y out of "scope" here, so no warning + " {3*Y,A}" + " end)">>, + ?line [{_,[{_,ms_transform,{?WARN_NUMBER_SHADOW,'Y'}}]}] = + compile_ww(Prog6), + Prog7 = <<" X=bar, " + " A = case X of" + " foo ->" + " Y = foo;" + " Y ->" + " bar" + " end," + " ets:fun2ms(fun(Y) ->" % Y exported from case and safe, so warn + " {3*Y,A}" + " end)">>, + ?line [{_,[{_,ms_transform,{?WARN_NUMBER_SHADOW,'Y'}}]}] = + compile_ww(Prog7), + ok. andalso_orelse(suite) -> []; @@ -721,6 +824,24 @@ compile_and_run(Records,Expr) -> code:load_binary(tmp,FN,Bin), tmp:tmp(). +compile_ww(Expr) -> + compile_ww(<<>>,Expr). +compile_ww(Records,Expr) -> + Prog = << + "-module(tmp).\n", + "-include_lib(\"stdlib/include/ms_transform.hrl\").\n", + "-export([tmp/0]).\n", + Records/binary,"\n", + "tmp() ->\n", + Expr/binary,".\n">>, + FN=temp_name(), + file:write_file(FN,Prog), + {ok,Forms} = epp:parse_file(FN,"",""), + {ok,tmp,_Bin,Wlist} = compile:forms(Forms,[return_warnings, + nowarn_unused_vars, + nowarn_unused_record]), + Wlist. + do_eval(String) -> {done,{ok,T,_},[]} = erl_scan:tokens( [], diff --git a/lib/stdlib/test/naughty_child.erl b/lib/stdlib/test/naughty_child.erl index b56130929c..b939436bfc 100644 --- a/lib/stdlib/test/naughty_child.erl +++ b/lib/stdlib/test/naughty_child.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2009. All Rights Reserved. +%% Copyright Ericsson AB 2002-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 diff --git a/lib/stdlib/test/proc_lib_SUITE.erl b/lib/stdlib/test/proc_lib_SUITE.erl index 2fd7725335..1565aa9bba 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-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. 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,10 +23,12 @@ %% %%-define(STANDALONE,1). --export([all/1, crash/1, sync_start/1, sync_start_nolink/1, sync_start_link/1, +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2, + 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([tickets/1, otp_6345/1]). +-export([ otp_6345/1]). -export([hib_loop/1, awaken/1]). @@ -40,12 +42,32 @@ -ifdef(STANDALONE). -define(line, noop, ). -else. --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). -endif. -all(suite) -> [crash, sync_start, spawn_opt, hibernate, tickets]. +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [crash, {group, sync_start}, spawn_opt, hibernate, + {group, tickets}]. + +groups() -> + [{tickets, [], [otp_6345]}, + {sync_start, [], [sync_start_nolink, sync_start_link]}]. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + -tickets(suite) -> [otp_6345]. %%----------------------------------------------------------------- %% We don't have to test that spwn and spawn_link actually spawns @@ -127,7 +149,6 @@ crash(Config) when is_list(Config) -> ok end. -sync_start(suite) -> [sync_start_nolink, sync_start_link]. sync_start_nolink(Config) when is_list(Config) -> _Pid = spawn_link(?MODULE, sp5, [self()]), diff --git a/lib/stdlib/test/qlc_SUITE.erl b/lib/stdlib/test/qlc_SUITE.erl index ff11ebc6bf..98eeaee118 100644 --- a/lib/stdlib/test/qlc_SUITE.erl +++ b/lib/stdlib/test/qlc_SUITE.erl @@ -1,25 +1,26 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2004-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2004-2011. 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% %% %%%---------------------------------------------------------------- %%% Purpose:Test Suite for the 'qlc' module. %%%----------------------------------------------------------------- -module(qlc_SUITE). +-compile(r12). -define(QLC, qlc). -define(QLCs, "qlc"). @@ -42,7 +43,7 @@ -define(testcase, current_testcase). % don't know -define(t, test_server). -else. --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). -define(datadir, ?config(data_dir, Config)). -define(privdir, ?config(priv_dir, Config)). -define(testcase, ?config(?TESTCASE, Config)). @@ -50,36 +51,33 @@ -include_lib("stdlib/include/ms_transform.hrl"). --export([all/1, init_per_testcase/2, fin_per_testcase/2]). +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2, + init_per_testcase/2, end_per_testcase/2]). --export([parse_transform/1, - badarg/1, nested_qlc/1, unused_var/1, lc/1, fun_clauses/1, - filter_var/1, single/1, exported_var/1, generator_vars/1, - nomatch/1, errors/1, pattern/1, +-export([ + badarg/1, nested_qlc/1, unused_var/1, lc/1, fun_clauses/1, + filter_var/1, single/1, exported_var/1, generator_vars/1, + nomatch/1, errors/1, pattern/1, - evaluation/1, - eval/1, cursor/1, fold/1, eval_unique/1, eval_cache/1, append/1, - evaluator/1, string_to_handle/1, table/1, process_dies/1, - sort/1, keysort/1, filesort/1, cache/1, cache_list/1, filter/1, - info/1, nested_info/1, lookup1/1, lookup2/1, lookup_rec/1, - indices/1, pre_fun/1, skip_filters/1, + eval/1, cursor/1, fold/1, eval_unique/1, eval_cache/1, append/1, + evaluator/1, string_to_handle/1, table/1, process_dies/1, + sort/1, keysort/1, filesort/1, cache/1, cache_list/1, filter/1, + info/1, nested_info/1, lookup1/1, lookup2/1, lookup_rec/1, + indices/1, pre_fun/1, skip_filters/1, - table_impls/1, - ets/1, dets/1, + ets/1, dets/1, - join/1, - join_option/1, join_filter/1, join_lookup/1, join_merge/1, - join_sort/1, join_complex/1, + join_option/1, join_filter/1, join_lookup/1, join_merge/1, + join_sort/1, join_complex/1, - tickets/1, - otp_5644/1, otp_5195/1, otp_6038_bug/1, otp_6359/1, otp_6562/1, - otp_6590/1, otp_6673/1, otp_6964/1, otp_7114/1, otp_7238/1, - otp_7232/1, otp_7552/1, otp_6674/1, otp_7714/1, + otp_5644/1, otp_5195/1, otp_6038_bug/1, otp_6359/1, otp_6562/1, + otp_6590/1, otp_6673/1, otp_6964/1, otp_7114/1, otp_7238/1, + otp_7232/1, otp_7552/1, otp_6674/1, otp_7714/1, - manpage/1, + manpage/1, - compat/1, - backward/1, forward/1]). + backward/1, forward/1]). %% Internal exports. -export([bad_table_throw/1, bad_table_exit/1, default_table/1, bad_table/1, @@ -113,17 +111,50 @@ init_per_testcase(Case, Config) -> ?line Dog = ?t:timetrap(?default_timeout), [{?TESTCASE, Case}, {watchdog, Dog} | Config]. -fin_per_testcase(_Case, _Config) -> +end_per_testcase(_Case, _Config) -> Dog = ?config(watchdog, _Config), test_server:timetrap_cancel(Dog), ok. -all(suite) -> - [parse_transform, evaluation, table_impls, join, tickets, manpage, compat]. +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [{group, parse_transform}, {group, evaluation}, + {group, table_impls}, {group, join}, {group, tickets}, + manpage, {group, compat}]. + +groups() -> + [{parse_transform, [], + [badarg, nested_qlc, unused_var, lc, fun_clauses, + filter_var, single, exported_var, generator_vars, + nomatch, errors, pattern]}, + {evaluation, [], + [eval, cursor, fold, eval_unique, eval_cache, append, + evaluator, string_to_handle, table, process_dies, sort, + keysort, filesort, cache, cache_list, filter, info, + nested_info, lookup1, lookup2, lookup_rec, indices, + pre_fun, skip_filters]}, + {table_impls, [], [ets, dets]}, + {join, [], + [join_option, join_filter, join_lookup, join_merge, + join_sort, join_complex]}, + {tickets, [], + [otp_5644, otp_5195, otp_6038_bug, otp_6359, otp_6562, + otp_6590, otp_6673, otp_6964, otp_7114, otp_7232, + otp_7238, otp_7552, otp_6674, otp_7714]}, + {compat, [], [backward, forward]}]. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. -parse_transform(suite) -> - [badarg, nested_qlc, unused_var, lc, fun_clauses, filter_var, - single, exported_var, generator_vars, nomatch, errors, pattern]. +end_per_group(_GroupName, Config) -> + Config. badarg(doc) -> "Badarg."; @@ -460,11 +491,6 @@ pattern(Config) when is_list(Config) -> -record(k, {t,v}).\n">>, Ts), ok. -evaluation(suite) -> - [eval, cursor, fold, eval_unique, eval_cache, append, evaluator, - string_to_handle, table, process_dies, sort, keysort, filesort, cache, - cache_list, filter, info, nested_info, lookup1, lookup2, lookup_rec, - indices, pre_fun, skip_filters]. eval(doc) -> "eval/2"; @@ -3183,7 +3209,9 @@ lookup2(Config) when is_list(Config) -> [] = qlc:e(Q), false = lookup_keys(Q) end, [{1,b},{2,3}])">>, - {warnings,[{{3,48},qlc,nomatch_filter}]}}, + {warnings,[{2,sys_core_fold,nomatch_guard}, + {3,qlc,nomatch_filter}, + {3,sys_core_fold,{eval_failure,badarg}}]}}, <<"etsc(fun(E) -> Q = qlc:q([X || {X} <- ets:table(E), element(1,{X}) =:= 1]), @@ -4294,8 +4322,6 @@ skip_filters(Config) when is_list(Config) -> ok. -table_impls(suite) -> - [ets, dets]. ets(doc) -> "ets:table/1,2."; @@ -4442,9 +4468,6 @@ dets(Config) when is_list(Config) -> _ = file:delete(Fname), ok. -join(suite) -> - [join_option, join_filter, join_lookup, join_merge, - join_sort, join_complex]. join_option(doc) -> "The 'join' option (any, lookup, merge, nested_loop). Also cache/unique."; @@ -5726,10 +5749,6 @@ join_complex(Config) when is_list(Config) -> ok. -tickets(suite) -> - [otp_5644, otp_5195, otp_6038_bug, otp_6359, otp_6562, otp_6590, - otp_6673, otp_6964, otp_7114, otp_7232, otp_7238, otp_7552, otp_6674, - otp_7714]. otp_5644(doc) -> "OTP-5644. Handle the new language element M:F/A."; @@ -7375,8 +7394,6 @@ gb_iter(I0, N, EFun) -> end. ">>. -compat(suite) -> - [backward, forward]. backward(doc) -> "OTP-6674. Join info and extra constants."; diff --git a/lib/stdlib/test/queue_SUITE.erl b/lib/stdlib/test/queue_SUITE.erl index 2cd6b52311..3d3152919a 100644 --- a/lib/stdlib/test/queue_SUITE.erl +++ b/lib/stdlib/test/queue_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2010. All Rights Reserved. +%% Copyright Ericsson AB 1999-2011. 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,13 +17,14 @@ %% %CopyrightEnd% %% -module(queue_SUITE). --export([all/1]). +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2]). -export([do/1, to_list/1, io_test/1, op_test/1, error/1, oops/1]). --export([init_per_testcase/2, fin_per_testcase/2]). +-export([init_per_testcase/2, end_per_testcase/2]). --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). % Default timetrap timeout (set in init_per_testcase). -define(default_timeout, ?t:minutes(1)). @@ -31,16 +32,32 @@ init_per_testcase(_Case, Config) -> ?line Dog = ?t:timetrap(?default_timeout), [{watchdog, Dog} | Config]. -fin_per_testcase(_Case, Config) -> +end_per_testcase(_Case, Config) -> Dog = ?config(watchdog, Config), test_server:timetrap_cancel(Dog), ok. -all(doc) -> - ["Test cases for queue."]; -all(suite) -> +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> [do, to_list, io_test, op_test, error, oops]. +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + do(doc) -> [""]; do(suite) -> diff --git a/lib/stdlib/test/random_SUITE.erl b/lib/stdlib/test/random_SUITE.erl index 8f1c304705..ac9d1a6c06 100644 --- a/lib/stdlib/test/random_SUITE.erl +++ b/lib/stdlib/test/random_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2009. All Rights Reserved. +%% Copyright Ericsson AB 2000-2011. 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,13 +17,14 @@ %% %CopyrightEnd% -module(random_SUITE). --export([all/1]). +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2]). -export([interval_1/1, seed0/1, seed/1]). --export([init_per_testcase/2, fin_per_testcase/2]). +-export([init_per_testcase/2, end_per_testcase/2]). --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). % Default timetrap timeout (set in init_per_testcase). -define(default_timeout, ?t:minutes(1)). @@ -31,16 +32,32 @@ init_per_testcase(_Case, Config) -> ?line Dog = ?t:timetrap(?default_timeout), [{watchdog, Dog} | Config]. -fin_per_testcase(_Case, Config) -> +end_per_testcase(_Case, Config) -> Dog = ?config(watchdog, Config), test_server:timetrap_cancel(Dog), ok. -all(doc) -> - ["Test cases for random."]; -all(suite) -> +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> [interval_1, seed0, seed]. +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + seed0(doc) -> ["Test that seed is set implicitly, and always the same."]; seed0(suite) -> diff --git a/lib/stdlib/test/random_iolist.erl b/lib/stdlib/test/random_iolist.erl index 4bce347d9a..8f21b5a3b3 100644 --- a/lib/stdlib/test/random_iolist.erl +++ b/lib/stdlib/test/random_iolist.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2009. All Rights Reserved. +%% Copyright Ericsson AB 2008-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 diff --git a/lib/stdlib/test/random_unicode_list.erl b/lib/stdlib/test/random_unicode_list.erl index 3e83383b08..b8bd719b89 100644 --- a/lib/stdlib/test/random_unicode_list.erl +++ b/lib/stdlib/test/random_unicode_list.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2009. All Rights Reserved. +%% Copyright Ericsson AB 2008-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 diff --git a/lib/stdlib/test/re_SUITE.erl b/lib/stdlib/test/re_SUITE.erl index 02683f9f1a..c4817c0d38 100644 --- a/lib/stdlib/test/re_SUITE.erl +++ b/lib/stdlib/test/re_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2010. All Rights Reserved. +%% Copyright Ericsson AB 2008-2011. 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,12 +18,41 @@ %% -module(re_SUITE). --export([all/1, pcre/1,compile_options/1,run_options/1,combined_options/1,replace_autogen/1,global_capture/1,replace_input_types/1,replace_return/1,split_autogen/1,split_options/1,split_specials/1,error_handling/1,pcre_cve_2008_2371/1]). +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2, pcre/1,compile_options/1, + run_options/1,combined_options/1,replace_autogen/1, + global_capture/1,replace_input_types/1,replace_return/1, + split_autogen/1,split_options/1,split_specials/1, + error_handling/1,pcre_cve_2008_2371/1, + pcre_compile_workspace_overflow/1,re_infinite_loop/1]). --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). -include_lib("kernel/include/file.hrl"). -all(suite) -> [pcre,compile_options,run_options,combined_options,replace_autogen,global_capture,replace_input_types,replace_return,split_autogen,split_options,split_specials,error_handling,pcre_cve_2008_2371]. +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [pcre, compile_options, run_options, combined_options, + replace_autogen, global_capture, replace_input_types, + replace_return, split_autogen, split_options, + split_specials, error_handling, pcre_cve_2008_2371, + pcre_compile_workspace_overflow, re_infinite_loop]. + +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + pcre(doc) -> ["Run all applicable tests from the PCRE testsuites."]; @@ -544,3 +573,25 @@ pcre_cve_2008_2371(Config) when is_list(Config) -> %% Make sure it doesn't crash the emulator. re:compile(<<"(?i)[\xc3\xa9\xc3\xbd]|[\xc3\xa9\xc3\xbdA]">>, [unicode]), ok. + +pcre_compile_workspace_overflow(doc) -> + "Patch from http://vcs.pcre.org/viewvc/code/trunk/pcre_compile.c?r1=504&r2=505&view=patch"; +pcre_compile_workspace_overflow(Config) when is_list(Config) -> + N = 819, + ?line {error,{"internal error: overran compiling workspace",799}} = + re:compile([lists:duplicate(N, $(), lists:duplicate(N, $))]), + ok. +re_infinite_loop(doc) -> + "Make sure matches that really loop infinitely actually fail"; +re_infinite_loop(Config) when is_list(Config) -> + Dog = ?t:timetrap(?t:minutes(1)), + ?line Str = + "http:/www.flickr.com/slideShow/index.gne?group_id=&user_id=69845378@N0", + ?line EMail_regex = "[a-z0-9!#$%&'*+/=?^_`{|}~-]+" + ++ "(\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*" + ++ "@.*([a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+" + ++ "([a-zA-Z]{2}|com|org|net|gov|mil" + ++ "|biz|info|mobi|name|aero|jobs|museum)", + ?line nomatch = re:run(Str, EMail_regex), + ?t:timetrap_cancel(Dog), + ok. diff --git a/lib/stdlib/test/select_SUITE.erl b/lib/stdlib/test/select_SUITE.erl index 6900f1a8f5..546c25f954 100644 --- a/lib/stdlib/test/select_SUITE.erl +++ b/lib/stdlib/test/select_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2010. All Rights Reserved. +%% Copyright Ericsson AB 2000-2011. 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,7 +37,7 @@ -export([config/2]). -define(fmt(A,B),io:format(A,B)). -else. --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). -define(fmt(A,B),test_server:format(A,B)). -endif. @@ -58,23 +58,41 @@ config(priv_dir,_) -> ".". -else. %% When run in test server. --export([all/1,select_test/1,init_per_testcase/2, fin_per_testcase/2, +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2,select_test/1, + init_per_testcase/2, end_per_testcase/2, return_values/1]). init_per_testcase(_Case, Config) when is_list(Config) -> ?line Dog=test_server:timetrap(test_server:seconds(1200)), [{watchdog, Dog}|Config]. - -fin_per_testcase(_Case, Config) -> + +end_per_testcase(_Case, Config) -> Dog=?config(watchdog, Config), test_server:timetrap_cancel(Dog), ok. -all(doc) -> - ["Test ets:select"]; -all(suite) -> +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> [return_values, select_test]. - + +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + select_test(suite) -> []; select_test(doc) -> diff --git a/lib/stdlib/test/sets_SUITE.erl b/lib/stdlib/test/sets_SUITE.erl index c9f1a03598..f284276bd7 100644 --- a/lib/stdlib/test/sets_SUITE.erl +++ b/lib/stdlib/test/sets_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2009. All Rights Reserved. +%% Copyright Ericsson AB 2004-2011. 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,13 +22,15 @@ -module(sets_SUITE). --export([all/1,init_per_testcase/2,fin_per_testcase/2, +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2, + init_per_testcase/2,end_per_testcase/2, create/1,add_element/1,del_element/1, subtract/1,intersection/1,union/1,is_subset/1, is_set/1,fold/1,filter/1, take_smallest/1,take_largest/1]). --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). -import(lists, [foldl/3,reverse/1]). @@ -36,15 +38,33 @@ init_per_testcase(_Case, Config) -> ?line Dog = ?t:timetrap(?t:minutes(5)), [{watchdog,Dog}|Config]. -fin_per_testcase(_Case, Config) -> +end_per_testcase(_Case, Config) -> Dog = ?config(watchdog, Config), test_server:timetrap_cancel(Dog), ok. -all(suite) -> - [create,add_element,del_element,subtract, - intersection,union,is_subset,is_set,fold,filter, - take_smallest,take_largest]. +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [create, add_element, del_element, subtract, + intersection, union, is_subset, is_set, fold, filter, + take_smallest, take_largest]. + +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + create(Config) when is_list(Config) -> test_all(fun create_1/1). diff --git a/lib/stdlib/test/sets_test_lib.erl b/lib/stdlib/test/sets_test_lib.erl index 6b6fb00550..bdfb0d59d2 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-2009. All Rights Reserved. +%% Copyright Ericsson AB 2004-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 diff --git a/lib/stdlib/test/shell_SUITE.erl b/lib/stdlib/test/shell_SUITE.erl index 588342d46a..8273377ba1 100644 --- a/lib/stdlib/test/shell_SUITE.erl +++ b/lib/stdlib/test/shell_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2010. All Rights Reserved. +%% Copyright Ericsson AB 2004-2011. 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,21 +17,22 @@ %% %CopyrightEnd% %% -module(shell_SUITE). --export([all/1]). +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2]). -export([forget/1, records/1, known_bugs/1, otp_5226/1, otp_5327/1, - otp_5435/1, otp_5195/1, otp_5915/1, otp_5916/1, - bits/1, bs_match_misc_SUITE/1, bs_match_int_SUITE/1, - bs_match_tail_SUITE/1, bs_match_bin_SUITE/1, - bs_construct_SUITE/1, - refman/1, refman_bit_syntax/1, - progex/1, progex_bit_syntax/1, progex_records/1, - progex_lc/1, progex_funs/1, - tickets/1, otp_5990/1, otp_6166/1, otp_6554/1, otp_6785/1, - otp_7184/1, otp_7232/1, otp_8393/1]). - --export([restricted/1, start_restricted_from_shell/1, - start_restricted_on_command_line/1,restricted_local/1]). + otp_5435/1, otp_5195/1, otp_5915/1, otp_5916/1, + bs_match_misc_SUITE/1, bs_match_int_SUITE/1, + bs_match_tail_SUITE/1, bs_match_bin_SUITE/1, + bs_construct_SUITE/1, + 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]). + +-export([ start_restricted_from_shell/1, + start_restricted_on_command_line/1,restricted_local/1]). %% Internal export. -export([otp_5435_2/0, prompt1/1, prompt2/1, prompt3/1, prompt4/1, @@ -50,8 +51,8 @@ config(priv_dir,_) -> ".". -else. --include("test_server.hrl"). --export([init_per_testcase/2, fin_per_testcase/2]). +-include_lib("test_server/include/test_server.hrl"). +-export([init_per_testcase/2, end_per_testcase/2]). % Default timetrap timeout (set in init_per_testcase). -define(default_timeout, ?t:minutes(2)). init_per_testcase(_Case, Config) -> @@ -60,7 +61,7 @@ init_per_testcase(_Case, Config) -> ?line code:add_patha(?config(priv_dir,Config)), [{orig_path,OrigPath}, {watchdog, Dog} | Config]. -fin_per_testcase(_Case, Config) -> +end_per_testcase(_Case, Config) -> ?line Dog = ?config(watchdog, Config), ?line test_server:timetrap_cancel(Dog), ?line OrigPath = ?config(orig_path,Config), @@ -71,18 +72,44 @@ fin_per_testcase(_Case, Config) -> ok. -endif. -all(doc) -> - ["Test cases for the 'shell' module."]; -all(suite) -> - [forget, records, known_bugs, otp_5226, otp_5327, otp_5435, otp_5195, - otp_5915, otp_5916, bits, refman, progex, tickets, restricted]. +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [forget, records, known_bugs, otp_5226, otp_5327, + otp_5435, otp_5195, otp_5915, otp_5916, {group, bits}, + {group, refman}, {group, progex}, {group, tickets}, + {group, restricted}]. + +groups() -> + [{restricted, [], + [start_restricted_from_shell, + start_restricted_on_command_line, restricted_local]}, + {bits, [], + [bs_match_misc_SUITE, bs_match_tail_SUITE, + bs_match_bin_SUITE, bs_construct_SUITE]}, + {refman, [], [refman_bit_syntax]}, + {progex, [], + [progex_bit_syntax, progex_records, progex_lc, + progex_funs]}, + {tickets, [], + [otp_5990, otp_6166, otp_6554, otp_6785, otp_7184, + otp_7232, otp_8393]}]. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + -record(state, {bin, reply, leader}). -restricted(doc) -> - ["Test restricted_shell"]; -restricted(suite) -> - [start_restricted_from_shell,start_restricted_on_command_line,restricted_local]. start_restricted_from_shell(doc) -> ["Test that a restricted shell can be started from the normal shell"]; @@ -797,9 +824,6 @@ otp_5916(Config) when is_list(Config) -> [ok] = scan(C), ok. -bits(suite) -> - [bs_match_misc_SUITE, % bs_match_int_SUITE/, - bs_match_tail_SUITE, bs_match_bin_SUITE, bs_construct_SUITE]. bs_match_misc_SUITE(doc) -> ["OTP-5327. Adopted from parts of emulator/test/bs_match_misc_SUITE.erl."]; @@ -1520,8 +1544,6 @@ evaluate(Str, Vars) -> Result end. -refman(suite) -> - [refman_bit_syntax]. refman_bit_syntax(doc) -> ["Bit syntax examples from the Reference Manual. OTP-5237."]; @@ -1564,8 +1586,6 @@ refman_bit_syntax(Config) when is_list(Config) -> ?line <<2,4,6>> = << << (X*2) >> || <<X>> <= << 1,2,3 >> >>, ok. -progex(suite) -> - [progex_bit_syntax, progex_records, progex_lc, progex_funs]. -define(IP_VERSION, 4). -define(IP_MIN_HDR_LEN, 5). @@ -2256,8 +2276,6 @@ progex_funs(Config) when is_list(Config) -> ?line [ok] = scan(Test2_shell), ok. -tickets(suite) -> - [otp_5990, otp_6166, otp_6554, otp_6785, otp_7184, otp_7232, otp_8393]. otp_5990(doc) -> "OTP-5990. {erlang,is_record}."; diff --git a/lib/stdlib/test/slave_SUITE.erl b/lib/stdlib/test/slave_SUITE.erl index 5c1282fe9b..37fc694083 100644 --- a/lib/stdlib/test/slave_SUITE.erl +++ b/lib/stdlib/test/slave_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2010. All Rights Reserved. +%% Copyright Ericsson AB 1997-2011. 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,18 +18,37 @@ %% -module(slave_SUITE). --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). --export([all/1, t_start/1, t_start_link/1, +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2, t_start/1, t_start_link/1, start_link_nodedown/1, errors/1]). %% Internal exports. -export([fun_init/1, test_errors/1]). -export([timeout_test/1, auth_test/1, rsh_test/1, start_a_slave/3]). -all(suite) -> +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> [t_start_link, start_link_nodedown, t_start, errors]. +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + t_start_link(suite) -> []; t_start_link(Config) when is_list(Config) -> ?line Dog = test_server:timetrap(test_server:seconds(20)), diff --git a/lib/stdlib/test/sofs_SUITE.erl b/lib/stdlib/test/sofs_SUITE.erl index d60cfc6895..d6f88a655e 100644 --- a/lib/stdlib/test/sofs_SUITE.erl +++ b/lib/stdlib/test/sofs_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2010. All Rights Reserved. +%% Copyright Ericsson AB 2001-2011. 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,13 +26,14 @@ -define(config(X,Y), foo). -define(t, test_server). -else. --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). -define(format(S, A), ok). -endif. --export([all/1]). +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2]). --export([sofs/1, from_term_1/1, set_1/1, from_sets_1/1, relation_1/1, +-export([ from_term_1/1, set_1/1, from_sets_1/1, relation_1/1, a_function_1/1, family_1/1, projection/1, relation_to_family_1/1, domain_1/1, range_1/1, image/1, inverse_image/1, inverse_1/1, converse_1/1, no_elements_1/1, @@ -47,7 +48,7 @@ multiple_relative_product/1, digraph/1, constant_function/1, misc/1]). --export([sofs_family/1, family_specification/1, +-export([ family_specification/1, family_domain_1/1, family_range_1/1, family_to_relation_1/1, union_of_family_1/1, intersection_of_family_1/1, @@ -81,18 +82,56 @@ union/1, union/2, family_to_digraph/1, family_to_digraph/2, digraph_to_family/1, digraph_to_family/2]). --export([init_per_testcase/2, fin_per_testcase/2]). +-export([init_per_testcase/2, end_per_testcase/2]). -compile({inline,[{eval,2}]}). -all(suite) -> - [sofs, sofs_family]. +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [{group, sofs}, {group, sofs_family}]. + +groups() -> + [{sofs, [], + [from_term_1, set_1, from_sets_1, relation_1, + a_function_1, family_1, relation_to_family_1, domain_1, + range_1, image, inverse_image, inverse_1, converse_1, + no_elements_1, substitution, restriction, drestriction, + projection, strict_relation_1, extension, + weak_relation_1, to_sets_1, specification, union_1, + intersection_1, difference, symdiff, + symmetric_partition, is_sofs_set_1, is_set_1, is_equal, + is_subset, is_a_function_1, is_disjoint, join, + canonical, composite_1, relative_product_1, + relative_product_2, product_1, partition_1, partition_3, + multiple_relative_product, digraph, constant_function, + misc]}, + {sofs_family, [], + [family_specification, family_domain_1, family_range_1, + family_to_relation_1, union_of_family_1, + intersection_of_family_1, family_projection, + family_difference, family_intersection_1, + family_intersection_2, family_union_1, family_union_2, + partition_family]}]. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + init_per_testcase(_Case, Config) -> Dog=?t:timetrap(?t:minutes(2)), [{watchdog, Dog}|Config]. -fin_per_testcase(_Case, Config) -> +end_per_testcase(_Case, Config) -> Dog=?config(watchdog, Config), test_server:timetrap_cancel(Dog), ok. @@ -100,18 +139,6 @@ fin_per_testcase(_Case, Config) -> %% [{2,b},{1,a,b}] == lists:sort([{2,b},{1,a,b}]) %% [{1,a,b},{2,b}] == lists:keysort(1,[{2,b},{1,a,b}]) -sofs(suite) -> - [from_term_1, set_1, from_sets_1, relation_1, a_function_1, - family_1, relation_to_family_1, domain_1, range_1, image, - inverse_image, inverse_1, converse_1, no_elements_1, - substitution, restriction, drestriction, projection, - strict_relation_1, extension, weak_relation_1, to_sets_1, - specification, union_1, intersection_1, difference, symdiff, - symmetric_partition, is_sofs_set_1, is_set_1, is_equal, - is_subset, is_a_function_1, is_disjoint, join, canonical, - composite_1, relative_product_1, relative_product_2, product_1, - partition_1, partition_3, multiple_relative_product, digraph, - constant_function, misc]. from_term_1(suite) -> []; from_term_1(doc) -> [""]; @@ -1575,19 +1602,15 @@ relative_product_2(Conf) when is_list(Conf) -> from_term([{{a},b}])}, ER)), ?line {'EXIT', {badarg, _}} = (catch relative_product({}, ER)), - ?line eval(relative_product({relation([{a,b}])}, - from_term([],[{{atom},atom}])), - ER), - ?line eval(relative_product({relation([{a,b}]),relation([{a,1}])}, - from_term([{{b,1},{tjo,hej,sa}}])), - from_term([{a,{tjo,hej,sa}}])), - ?line eval(relative_product({relation([{a,b}]), ER}, - from_term([{{a,b},b}])), - ER), - ?line eval(relative_product({relation([{a,b},{c,a}]), - relation([{a,1},{a,2}])}, - from_term([{{b,1},b1},{{b,2},b2}])), - relation([{a,b1},{a,b2}])), + ?line relprod2({relation([{a,b}])}, from_term([],[{{atom},atom}]), ER), + ?line relprod2({relation([{a,b}]),relation([{a,1}])}, + from_term([{{b,1},{tjo,hej,sa}}]), + from_term([{a,{tjo,hej,sa}}])), + ?line relprod2({relation([{a,b}]), ER}, from_term([{{a,b},b}]), ER), + ?line relprod2({relation([{a,b},{c,a}]), + relation([{a,1},{a,2}])}, + from_term([{{b,1},b1},{{b,2},b2}]), + relation([{a,b1},{a,b2}])), ?line eval(relative_product({relation([{a,b}]), ER}), from_term([],[{atom,{atom,atom}}])), ?line eval(relative_product({from_term([{{a,[a,b]},[a]}]), @@ -1595,6 +1618,11 @@ relative_product_2(Conf) when is_list(Conf) -> from_term([{{a,[a,b]},{[a],[[a,b]]}}])), ok. +relprod2(A1T, A2, R) -> + %% A tuple as first argument is the old interface: + eval(relative_product(A1T, A2), R), + eval(relative_product(tuple_to_list(A1T), A2), R). + product_1(suite) -> []; product_1(doc) -> [""]; product_1(Conf) when is_list(Conf) -> @@ -1934,12 +1962,6 @@ relational_restriction(R) -> Fun = fun(S) -> no_elements(S) > 1 end, family_to_relation(family_specification(Fun, relation_to_family(R))). -sofs_family(suite) -> - [family_specification, family_domain_1, family_range_1, - family_to_relation_1, union_of_family_1, intersection_of_family_1, - family_projection, family_difference, - family_intersection_1, family_intersection_2, - family_union_1, family_union_2, partition_family]. family_specification(suite) -> []; family_specification(doc) -> [""]; diff --git a/lib/stdlib/test/stdlib.cover b/lib/stdlib/test/stdlib.cover index b98d949889..61f4f064b9 100644 --- a/lib/stdlib/test/stdlib.cover +++ b/lib/stdlib/test/stdlib.cover @@ -1,10 +1,17 @@ %% -*- erlang -*- -{exclude, - [erl_parse, - ets, - filename, - gen_event, - gen_server, - gen, - lists, - proc_lib]}. +{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 b/lib/stdlib/test/stdlib.spec index bbfb43bd15..3768e494b2 100644 --- a/lib/stdlib/test/stdlib.spec +++ b/lib/stdlib/test/stdlib.spec @@ -1,4 +1 @@ -{topcase, {dir, "../stdlib_test"}}. -%{skip,{dets_SUITE,open_file_1,"Crashes Windows tests"}}. -%{skip,{dets_SUITE,fold,"Crashes Windows tests"}}. -%{skip,{dets_SUITE,match,"Crashes Windows tests"}}. +{suites,"../stdlib_test",all}. diff --git a/lib/stdlib/test/stdlib_SUITE.erl b/lib/stdlib/test/stdlib_SUITE.erl index d46a2caf90..0cca030b3d 100644 --- a/lib/stdlib/test/stdlib_SUITE.erl +++ b/lib/stdlib/test/stdlib_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2011. 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 +20,7 @@ %%% Purpose:Stdlib application test suite. %%%----------------------------------------------------------------- -module(stdlib_SUITE). --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). % Default timetrap timeout (set in init_per_testcase). @@ -28,8 +28,9 @@ -define(application, stdlib). % Test server specific exports --export([all/1]). --export([init_per_testcase/2, fin_per_testcase/2]). +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2]). +-export([init_per_testcase/2, end_per_testcase/2]). % Test cases must be exported. -export([app_test/1]). @@ -38,15 +39,31 @@ %% %% all/1 %% -all(doc) -> - []; -all(suite) -> - [?cases]. +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [app_test]. + +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + init_per_testcase(_Case, Config) -> ?line Dog=test_server:timetrap(?default_timeout), [{watchdog, Dog}|Config]. -fin_per_testcase(_Case, Config) -> +end_per_testcase(_Case, Config) -> Dog=?config(watchdog, Config), test_server:timetrap_cancel(Dog), ok. @@ -58,7 +75,7 @@ app_test(suite) -> []; app_test(doc) -> ["Application consistency test."]; -app_test(Config) when list(Config) -> +app_test(Config) when is_list(Config) -> ?t:app_test(stdlib), ok. diff --git a/lib/stdlib/test/string_SUITE.erl b/lib/stdlib/test/string_SUITE.erl index 3171b87c44..1dcd4be21e 100644 --- a/lib/stdlib/test/string_SUITE.erl +++ b/lib/stdlib/test/string_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2009. All Rights Reserved. +%% Copyright Ericsson AB 2004-2011. 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,15 +20,16 @@ %%% Purpose: string test suite. %%%----------------------------------------------------------------- -module(string_SUITE). --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). % Default timetrap timeout (set in init_per_testcase). -define(default_timeout, ?t:minutes(1)). % Test server specific exports --export([all/1]). --export([init_per_testcase/2, fin_per_testcase/2]). +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2]). +-export([init_per_testcase/2, end_per_testcase/2]). % Test cases must be exported. -export([len/1,equal/1,concat/1,chr_rchr/1,str_rstr/1]). @@ -40,19 +41,34 @@ %% %% all/1 %% -all(doc) -> - []; -all(suite) -> - [len,equal,concat,chr_rchr,str_rstr, - span_cspan,substr,tokens,chars, - copies,words,strip,sub_word,left_right, - sub_string,centre, join, - to_integer,to_float,to_upper_to_lower]. +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [len, equal, concat, chr_rchr, str_rstr, span_cspan, + substr, tokens, chars, copies, words, strip, sub_word, + left_right, sub_string, centre, join, to_integer, + to_float, to_upper_to_lower]. + +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + init_per_testcase(_Case, Config) -> ?line Dog=test_server:timetrap(?default_timeout), [{watchdog, Dog}|Config]. -fin_per_testcase(_Case, Config) -> +end_per_testcase(_Case, Config) -> Dog=?config(watchdog, Config), test_server:timetrap_cancel(Dog), ok. @@ -240,7 +256,8 @@ copies(Config) when is_list(Config) -> ?line "." = string:copies(".", 1), ?line 30 = length(string:copies("123", 10)), %% invalid arg type - ?line {'EXIT',_} = (catch string:chars("hej", -1)), + ?line {'EXIT',_} = (catch string:copies("hej", -1)), + ?line {'EXIT',_} = (catch string:copies("hej", 2.0)), ok. words(suite) -> diff --git a/lib/stdlib/test/supervisor_1.erl b/lib/stdlib/test/supervisor_1.erl index 297550b230..3198be0fed 100644 --- a/lib/stdlib/test/supervisor_1.erl +++ b/lib/stdlib/test/supervisor_1.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-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 diff --git a/lib/stdlib/test/supervisor_SUITE.erl b/lib/stdlib/test/supervisor_SUITE.erl index 039ea298c4..c79a5002fb 100644 --- a/lib/stdlib/test/supervisor_SUITE.erl +++ b/lib/stdlib/test/supervisor_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. 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,51 +20,114 @@ -module(supervisor_SUITE). --include("test_server.hrl"). +-include_lib("common_test/include/ct.hrl"). +-define(TIMEOUT, 1000). %% Testserver specific export --export([all/1]). +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2, init_per_testcase/2, + end_per_testcase/2]). -%% Indirect spawn export --export([init/1]). +%% Internal export +-export([init/1, terminate_all_children/1]). %% API tests --export([sup_start/1, sup_start_normal/1, sup_start_ignore_init/1, - sup_start_ignore_child/1, sup_start_error_return/1, - sup_start_fail/1, sup_stop/1, sup_stop_infinity/1, - sup_stop_timeout/1, sup_stop_brutal_kill/1, child_adm/1, - child_adm_simple/1, child_specs/1, extra_return/1]). +-export([ sup_start_normal/1, sup_start_ignore_init/1, + sup_start_ignore_child/1, sup_start_error_return/1, + sup_start_fail/1, sup_stop_infinity/1, + sup_stop_timeout/1, sup_stop_brutal_kill/1, child_adm/1, + child_adm_simple/1, child_specs/1, extra_return/1]). %% Tests concept permanent, transient and temporary --export([normal_termination/1, permanent_normal/1, transient_normal/1, - temporary_normal/1, abnormal_termination/1, - permanent_abnormal/1, transient_abnormal/1, - temporary_abnormal/1]). +-export([ permanent_normal/1, transient_normal/1, + temporary_normal/1, + permanent_abnormal/1, transient_abnormal/1, + temporary_abnormal/1]). %% Restart strategy tests --export([restart_one_for_one/1, one_for_one/1, - one_for_one_escalation/1, restart_one_for_all/1, one_for_all/1, - one_for_all_escalation/1, restart_simple_one_for_one/1, - simple_one_for_one/1, simple_one_for_one_escalation/1, - restart_rest_for_one/1, rest_for_one/1, rest_for_one_escalation/1, - simple_one_for_one_extra/1]). +-export([ one_for_one/1, + one_for_one_escalation/1, one_for_all/1, + one_for_all_escalation/1, + simple_one_for_one/1, simple_one_for_one_escalation/1, + rest_for_one/1, rest_for_one_escalation/1, + simple_one_for_one_extra/1]). %% Misc tests --export([child_unlink/1, tree/1, count_children_memory/1]). +-export([child_unlink/1, tree/1, count_children_memory/1, + do_not_save_start_parameters_for_temporary_children/1, + do_not_save_child_specs_for_temporary_children/1, + simple_one_for_one_scale_many_temporary_children/1]). + +%%------------------------------------------------------------------------- + +suite() -> + [{ct_hooks,[ts_install_cth]}]. + +all() -> + [{group, sup_start}, {group, sup_stop}, child_adm, + child_adm_simple, extra_return, child_specs, + {group, restart_one_for_one}, + {group, restart_one_for_all}, + {group, restart_simple_one_for_one}, + {group, restart_rest_for_one}, + {group, normal_termination}, + {group, abnormal_termination}, child_unlink, tree, + count_children_memory, do_not_save_start_parameters_for_temporary_children, + do_not_save_child_specs_for_temporary_children, + simple_one_for_one_scale_many_temporary_children]. + +groups() -> + [{sup_start, [], + [sup_start_normal, sup_start_ignore_init, + sup_start_ignore_child, sup_start_error_return, + sup_start_fail]}, + {sup_stop, [], + [sup_stop_infinity, sup_stop_timeout, + sup_stop_brutal_kill]}, + {normal_termination, [], + [permanent_normal, transient_normal, temporary_normal]}, + {abnormal_termination, [], + [permanent_abnormal, transient_abnormal, + temporary_abnormal]}, + {restart_one_for_one, [], + [one_for_one, one_for_one_escalation]}, + {restart_one_for_all, [], + [one_for_all, one_for_all_escalation]}, + {restart_simple_one_for_one, [], + [simple_one_for_one, simple_one_for_one_extra, + simple_one_for_one_escalation]}, + {restart_rest_for_one, [], + [rest_for_one, rest_for_one_escalation]}]. + +init_per_suite(Config0) -> + Config = lists:keydelete(watchdog, 1, Config0), + Dog = test_server:timetrap(?TIMEOUT), + [{watchdog, Dog} | Config]. + +end_per_suite(_Config) -> + ok. -%------------------------------------------------------------------------- +init_per_group(_GroupName, Config) -> + Config. -all(suite) -> - {req,[stdlib], - [sup_start, sup_stop, child_adm, - child_adm_simple, extra_return, child_specs, - restart_one_for_one, restart_one_for_all, - restart_simple_one_for_one, restart_rest_for_one, - normal_termination, abnormal_termination, child_unlink, tree, - count_children_memory]}. +end_per_group(_GroupName, Config) -> + Config. +init_per_testcase(count_children_memory, Config) -> + MemoryState = erlang:system_info(allocator), + case count_children_allocator_test(MemoryState) of + true -> Config; + false -> + {skip, "+Meamin used during test; erlang:memory/1 not available"} + end; +init_per_testcase(_Case, Config) -> + erlang:display(_Case), + Config. -start(InitResult) -> +end_per_testcase(_Case, _Config) -> + ok. + +start_link(InitResult) -> supervisor:start_link({local, sup_test}, ?MODULE, InitResult). %% Simulate different supervisors callback. @@ -81,162 +144,87 @@ get_child_counts(Supervisor) -> proplists:get_value(supervisors, Counts), proplists:get_value(workers, Counts)]. - -%------------------------------------------------------------------------- -% -% Test cases starts here. -% -%------------------------------------------------------------------------- - -sup_start(doc) -> - ["Test start of a supervisor."]; -sup_start(suite) -> - [sup_start_normal, sup_start_ignore_init, sup_start_ignore_child, - sup_start_error_return, sup_start_fail]. - -%------------------------------------------------------------------------- +%%------------------------------------------------------------------------- +%% Test cases starts here. +%%------------------------------------------------------------------------- sup_start_normal(doc) -> ["Tests that the supervisor process starts correctly and that it " - "can be terminated gracefully."]; + "can be terminated gracefully."]; sup_start_normal(suite) -> []; sup_start_normal(Config) when is_list(Config) -> process_flag(trap_exit, true), - ?line {ok, Pid} = start({ok, {{one_for_one, 2, 3600}, []}}), - ?line exit(Pid, shutdown), - receive - {'EXIT', Pid, shutdown} -> - ok; - {'EXIT', Pid, Else} -> - ?line test_server:fail({bad_exit_reason, Else}) - after - 2000 -> - ?line test_server:fail(no_exit_reason) - end, - ok. -%------------------------------------------------------------------------- + {ok, Pid} = start_link({ok, {{one_for_one, 2, 3600}, []}}), + terminate(Pid, shutdown). + +%%------------------------------------------------------------------------- sup_start_ignore_init(doc) -> ["Tests what happens if init-callback returns ignore"]; sup_start_ignore_init(suite) -> []; sup_start_ignore_init(Config) when is_list(Config) -> process_flag(trap_exit, true), - ?line ignore = start(ignore), - - receive - {'EXIT', _Pid, normal} -> - ok; - {'EXIT', _Pid, Else} -> - ?line test_server:fail({bad_exit_reason, Else}) - after - 2000 -> - ?line test_server:fail(no_exit_reason) - end, - ok. + ignore = start_link(ignore), + check_exit_reason(normal). - -%------------------------------------------------------------------------- +%%------------------------------------------------------------------------- sup_start_ignore_child(doc) -> ["Tests what happens if init-callback returns ignore"]; sup_start_ignore_child(suite) -> []; sup_start_ignore_child(Config) when is_list(Config) -> process_flag(trap_exit, true), - ?line {ok, _Pid} = start({ok, {{one_for_one, 2, 3600}, []}}), + {ok, _Pid} = start_link({ok, {{one_for_one, 2, 3600}, []}}), Child1 = {child1, {supervisor_1, start_child, [ignore]}, permanent, 1000, worker, []}, Child2 = {child2, {supervisor_1, start_child, []}, permanent, 1000, worker, []}, - - ?line {ok, undefined} = supervisor:start_child(sup_test, Child1), - ?line {ok, CPid2} = supervisor:start_child(sup_test, Child2), - ?line [{child2, CPid2, worker, []},{child1, undefined, worker, []}] - = supervisor:which_children(sup_test), - ?line [2,1,0,2] = get_child_counts(sup_test), + {ok, undefined} = supervisor:start_child(sup_test, Child1), + {ok, CPid2} = supervisor:start_child(sup_test, Child2), - ok. + [{child2, CPid2, worker, []},{child1, undefined, worker, []}] + = supervisor:which_children(sup_test), + [2,1,0,2] = get_child_counts(sup_test). -%------------------------------------------------------------------------- +%%------------------------------------------------------------------------- sup_start_error_return(doc) -> ["Tests what happens if init-callback returns a invalid value"]; sup_start_error_return(suite) -> []; sup_start_error_return(Config) when is_list(Config) -> process_flag(trap_exit, true), - ?line {error, Term} = start(invalid), - - receive - {'EXIT', _Pid, Term} -> - ok; - {'EXIT', _Pid, Else} -> - ?line test_server:fail({bad_exit_reason, Else}) - after - 2000 -> - ?line test_server:fail(no_exit_reason) - end, - ok. + {error, Term} = start_link(invalid), + check_exit_reason(Term). -%------------------------------------------------------------------------- +%%------------------------------------------------------------------------- sup_start_fail(doc) -> ["Tests what happens if init-callback fails"]; sup_start_fail(suite) -> []; sup_start_fail(Config) when is_list(Config) -> process_flag(trap_exit, true), - ?line {error, Term} = start(fail), + {error, Term} = start_link(fail), + check_exit_reason(Term). - receive - {'EXIT', _Pid, Term} -> - ok; - {'EXIT', _Pid, Else} -> - ?line test_server:fail({bad_exit_reason, Else}) - after - 2000 -> - ?line test_server:fail(no_exit_reason) - end, - ok. -%------------------------------------------------------------------------- -sup_stop(doc) -> - ["Tests that the supervisor shoutdowns its children if it is " - "shutdown itself."]; -sup_stop(suite) -> [sup_stop_infinity, sup_stop_timeout, sup_stop_brutal_kill]. - -%------------------------------------------------------------------------- +%%------------------------------------------------------------------------- sup_stop_infinity(doc) -> ["See sup_stop/1 when Shutdown = infinity, this walue is only allowed " - "for children of type supervisor"]; + "for children of type supervisor"]; sup_stop_infinity(suite) -> []; sup_stop_infinity(Config) when is_list(Config) -> process_flag(trap_exit, true), - ?line {ok, Pid} = start({ok, {{one_for_one, 2, 3600}, []}}), + {ok, Pid} = start_link({ok, {{one_for_one, 2, 3600}, []}}), Child1 = {child1, {supervisor_1, start_child, []}, permanent, infinity, supervisor, []}, Child2 = {child2, {supervisor_1, start_child, []}, permanent, - infinity, worker, []}, - ?line {ok, CPid1} = supervisor:start_child(sup_test, Child1), + infinity, worker, []}, + {ok, CPid1} = supervisor:start_child(sup_test, Child1), link(CPid1), - ?line {error, {invalid_shutdown,infinity}} = - supervisor:start_child(sup_test, Child2), + {error, {invalid_shutdown,infinity}} = + supervisor:start_child(sup_test, Child2), - ?line exit(Pid, shutdown), + terminate(Pid, shutdown), + check_exit_reason(CPid1, shutdown). - receive - {'EXIT', Pid, shutdown} -> - ok; - {'EXIT', Pid, Else} -> - ?line test_server:fail({bad_exit_reason, Else}) - after - 5000 -> - ?line test_server:fail(no_exit_reason) - end, - receive - {'EXIT', CPid1, shutdown} -> ok; - {'EXIT', CPid1, Reason} -> - ?line test_server:fail({bad_exit_reason, Reason}) - after - 2000 -> ?line test_server:fail(no_exit_reason) - end, - ok. - -%------------------------------------------------------------------------- +%%------------------------------------------------------------------------- sup_stop_timeout(doc) -> ["See sup_stop/1 when Shutdown = 1000"]; @@ -244,93 +232,47 @@ sup_stop_timeout(suite) -> []; sup_stop_timeout(Config) when is_list(Config) -> process_flag(trap_exit, true), - ?line {ok, Pid} = start({ok, {{one_for_one, 2, 3600}, []}}), + {ok, Pid} = start_link({ok, {{one_for_one, 2, 3600}, []}}), Child1 = {child1, {supervisor_1, start_child, []}, permanent, 1000, worker, []}, Child2 = {child2, {supervisor_1, start_child, []}, permanent, 1000, worker, []}, - ?line {ok, CPid1} = supervisor:start_child(sup_test, Child1), + {ok, CPid1} = supervisor:start_child(sup_test, Child1), link(CPid1), - ?line {ok, CPid2} = supervisor:start_child(sup_test, Child2), + {ok, CPid2} = supervisor:start_child(sup_test, Child2), link(CPid2), - + CPid2 ! {sleep, 200000}, - ?line exit(Pid, shutdown), + terminate(Pid, shutdown), - receive - {'EXIT', Pid, shutdown} -> - ok; - {'EXIT', Pid, Else} -> - ?line test_server:fail({bad_exit_reason, Else}) - after - 5000 -> - ?line test_server:fail(no_exit_reason) - end, + check_exit_reason(CPid1, shutdown), + check_exit_reason(CPid2, killed). - receive - {'EXIT', CPid1, shutdown} -> ok; - {'EXIT', CPid1, Reason} -> - ?line test_server:fail({bad_exit_reason,Reason}) - after - 2000 -> ?line test_server:fail(no_exit_reason) - end, - - receive - {'EXIT', CPid2, killed} -> ok; - {'EXIT', CPid2, Reason2} -> - ?line test_server:fail({bad_exit_reason, Reason2}) - after - 2000 -> ?line test_server:fail(no_exit_reason) - end, - ok. -%------------------------------------------------------------------------- +%%------------------------------------------------------------------------- sup_stop_brutal_kill(doc) -> ["See sup_stop/1 when Shutdown = brutal_kill"]; sup_stop_brutal_kill(suite) -> []; sup_stop_brutal_kill(Config) when is_list(Config) -> process_flag(trap_exit, true), - ?line {ok, Pid} = start({ok, {{one_for_one, 2, 3600}, []}}), + {ok, Pid} = start_link({ok, {{one_for_one, 2, 3600}, []}}), Child1 = {child1, {supervisor_1, start_child, []}, permanent, 1000, worker, []}, Child2 = {child2, {supervisor_1, start_child, []}, permanent, brutal_kill, worker, []}, - ?line {ok, CPid1} = supervisor:start_child(sup_test, Child1), + {ok, CPid1} = supervisor:start_child(sup_test, Child1), link(CPid1), - ?line {ok, CPid2} = supervisor:start_child(sup_test, Child2), + {ok, CPid2} = supervisor:start_child(sup_test, Child2), link(CPid2), - ?line exit(Pid, shutdown), + terminate(Pid, shutdown), - receive - {'EXIT', Pid, shutdown} -> - ok; - {'EXIT', Pid, Else} -> - ?line test_server:fail({bad_exit_reason, Else}) - after - 5000 -> - ?line test_server:fail(no_exit_reason) - end, + check_exit_reason(CPid1, shutdown), + check_exit_reason(CPid2, killed). - receive - {'EXIT', CPid1, shutdown} -> ok; - {'EXIT', CPid1, Reason} -> - ?line test_server:fail({bad_exit_reason, Reason}) - after - 2000 -> ?line test_server:fail(no_exit_reason) - end, - receive - {'EXIT', CPid2, killed} -> ok; - {'EXIT', CPid2, Reason2} -> - ?line test_server:fail({bad_exit_reason, Reason2}) - after - 2000 -> ?line test_server:fail(no_exit_reason) - end, - ok. - -%------------------------------------------------------------------------- +%%------------------------------------------------------------------------- extra_return(doc) -> ["The start function provided to start a child may " "return {ok, Pid} or {ok, Pid, Info}, if it returns " @@ -344,46 +286,40 @@ extra_return(Config) when is_list(Config) -> Child = {child1, {supervisor_1, start_child, [extra_return]}, permanent, 1000, worker, []}, - ?line {ok, _Pid} = start({ok, {{one_for_one, 2, 3600}, [Child]}}), - ?line [{child1, CPid, worker, []}] = supervisor:which_children(sup_test), + {ok, _Pid} = start_link({ok, {{one_for_one, 2, 3600}, [Child]}}), + [{child1, CPid, worker, []}] = supervisor:which_children(sup_test), link(CPid), - ?line {error, not_found} = supervisor:terminate_child(sup_test, hej), - ?line {error, not_found} = supervisor:delete_child(sup_test, hej), - ?line {error, not_found} = supervisor:restart_child(sup_test, hej), - ?line {error, running} = supervisor:delete_child(sup_test, child1), - ?line {error, running} = supervisor:restart_child(sup_test, child1), - ?line [{child1, CPid, worker, []}] = supervisor:which_children(sup_test), - ?line [1,1,0,1] = get_child_counts(sup_test), - - ?line ok = supervisor:terminate_child(sup_test, child1), - receive - {'EXIT', CPid, shutdown} -> ok; - {'EXIT', CPid, Reason} -> - ?line test_server:fail({bad_reason, Reason}) - after 1000 -> - ?line test_server:fail(no_child_termination) - end, - ?line [{child1,undefined,worker,[]}] = supervisor:which_children(sup_test), - ?line [1,0,0,1] = get_child_counts(sup_test), - - ?line {ok, CPid2,extra_return} = + {error, not_found} = supervisor:terminate_child(sup_test, hej), + {error, not_found} = supervisor:delete_child(sup_test, hej), + {error, not_found} = supervisor:restart_child(sup_test, hej), + {error, running} = supervisor:delete_child(sup_test, child1), + {error, running} = supervisor:restart_child(sup_test, child1), + [{child1, CPid, worker, []}] = supervisor:which_children(sup_test), + [1,1,0,1] = get_child_counts(sup_test), + + ok = supervisor:terminate_child(sup_test, child1), + check_exit_reason(CPid, shutdown), + [{child1,undefined,worker,[]}] = supervisor:which_children(sup_test), + [1,0,0,1] = get_child_counts(sup_test), + + {ok, CPid2,extra_return} = supervisor:restart_child(sup_test, child1), - ?line [{child1, CPid2, worker, []}] = supervisor:which_children(sup_test), - ?line [1,1,0,1] = get_child_counts(sup_test), + [{child1, CPid2, worker, []}] = supervisor:which_children(sup_test), + [1,1,0,1] = get_child_counts(sup_test), - ?line ok = supervisor:terminate_child(sup_test, child1), - ?line ok = supervisor:terminate_child(sup_test, child1), - ?line ok = supervisor:delete_child(sup_test, child1), - ?line {error, not_found} = supervisor:restart_child(sup_test, child1), - ?line [] = supervisor:which_children(sup_test), - ?line [0,0,0,0] = get_child_counts(sup_test), + ok = supervisor:terminate_child(sup_test, child1), + ok = supervisor:terminate_child(sup_test, child1), + ok = supervisor:delete_child(sup_test, child1), + {error, not_found} = supervisor:restart_child(sup_test, child1), + [] = supervisor:which_children(sup_test), + [0,0,0,0] = get_child_counts(sup_test), - ?line {ok, CPid3, extra_return} = supervisor:start_child(sup_test, Child), - ?line [{child1, CPid3, worker, []}] = supervisor:which_children(sup_test), - ?line [1,1,0,1] = get_child_counts(sup_test), + {ok, CPid3, extra_return} = supervisor:start_child(sup_test, Child), + [{child1, CPid3, worker, []}] = supervisor:which_children(sup_test), + [1,1,0,1] = get_child_counts(sup_test), ok. -%------------------------------------------------------------------------- +%%------------------------------------------------------------------------- child_adm(doc)-> ["Test API functions start_child/2, terminate_child/2, delete_child/2 " "restart_child/2, which_children/1, count_children/1. Only correct " @@ -394,116 +330,124 @@ child_adm(Config) when is_list(Config) -> process_flag(trap_exit, true), Child = {child1, {supervisor_1, start_child, []}, permanent, 1000, worker, []}, - ?line {ok, _Pid} = start({ok, {{one_for_one, 2, 3600}, [Child]}}), - ?line [{child1, CPid, worker, []}] = supervisor:which_children(sup_test), - ?line [1,1,0,1] = get_child_counts(sup_test), + {ok, _Pid} = start_link({ok, {{one_for_one, 2, 3600}, [Child]}}), + [{child1, CPid, worker, []}] = supervisor:which_children(sup_test), + [1,1,0,1] = get_child_counts(sup_test), link(CPid), %% Start of an already runnig process - ?line {error,{already_started, CPid}} = + {error,{already_started, CPid}} = supervisor:start_child(sup_test, Child), - + %% Termination - ?line {error, not_found} = supervisor:terminate_child(sup_test, hej), - ?line {'EXIT',{noproc,{gen_server,call, _}}} = + {error, not_found} = supervisor:terminate_child(sup_test, hej), + {'EXIT',{noproc,{gen_server,call, _}}} = (catch supervisor:terminate_child(foo, child1)), - ?line ok = supervisor:terminate_child(sup_test, child1), - receive - {'EXIT', CPid, shutdown} -> ok; - {'EXIT', CPid, Reason} -> - ?line test_server:fail({bad_reason, Reason}) - after 1000 -> - ?line test_server:fail(no_child_termination) - end, - ?line [{child1,undefined,worker,[]}] = supervisor:which_children(sup_test), - ?line [1,0,0,1] = get_child_counts(sup_test), + ok = supervisor:terminate_child(sup_test, child1), + check_exit_reason(CPid, shutdown), + [{child1,undefined,worker,[]}] = supervisor:which_children(sup_test), + [1,0,0,1] = get_child_counts(sup_test), %% Like deleting something that does not exist, it will succeed! - ?line ok = supervisor:terminate_child(sup_test, child1), + ok = supervisor:terminate_child(sup_test, child1), %% Start of already existing but not running process - ?line {error,already_present} = - supervisor:start_child(sup_test, Child), + {error,already_present} = supervisor:start_child(sup_test, Child), %% Restart - ?line {ok, CPid2} = supervisor:restart_child(sup_test, child1), - ?line [{child1, CPid2, worker, []}] = supervisor:which_children(sup_test), - ?line [1,1,0,1] = get_child_counts(sup_test), - ?line {error, running} = supervisor:restart_child(sup_test, child1), - ?line {error, not_found} = supervisor:restart_child(sup_test, child2), - + {ok, CPid2} = supervisor:restart_child(sup_test, child1), + [{child1, CPid2, worker, []}] = supervisor:which_children(sup_test), + [1,1,0,1] = get_child_counts(sup_test), + {error, running} = supervisor:restart_child(sup_test, child1), + {error, not_found} = supervisor:restart_child(sup_test, child2), + %% Deletion - ?line {error, running} = supervisor:delete_child(sup_test, child1), - ?line {error, not_found} = supervisor:delete_child(sup_test, hej), - ?line {'EXIT',{noproc,{gen_server,call, _}}} = + {error, running} = supervisor:delete_child(sup_test, child1), + {error, not_found} = supervisor:delete_child(sup_test, hej), + {'EXIT',{noproc,{gen_server,call, _}}} = (catch supervisor:delete_child(foo, child1)), - ?line ok = supervisor:terminate_child(sup_test, child1), - ?line ok = supervisor:delete_child(sup_test, child1), - ?line {error, not_found} = supervisor:restart_child(sup_test, child1), - ?line [] = supervisor:which_children(sup_test), - ?line [0,0,0,0] = get_child_counts(sup_test), - + ok = supervisor:terminate_child(sup_test, child1), + ok = supervisor:delete_child(sup_test, child1), + {error, not_found} = supervisor:restart_child(sup_test, child1), + [] = supervisor:which_children(sup_test), + [0,0,0,0] = get_child_counts(sup_test), + %% Start - ?line {'EXIT',{noproc,{gen_server,call, _}}} = + {'EXIT',{noproc,{gen_server,call, _}}} = (catch supervisor:start_child(foo, Child)), - ?line {ok, CPid3} = supervisor:start_child(sup_test, Child), - ?line [{child1, CPid3, worker, []}] = supervisor:which_children(sup_test), - ?line [1,1,0,1] = get_child_counts(sup_test), + {ok, CPid3} = supervisor:start_child(sup_test, Child), + [{child1, CPid3, worker, []}] = supervisor:which_children(sup_test), + [1,1,0,1] = get_child_counts(sup_test), + + %% Terminate with Pid not allowed when not simple_one_for_one + {error,not_found} = supervisor:terminate_child(sup_test, CPid3), + [{child1, CPid3, worker, []}] = supervisor:which_children(sup_test), + [1,1,0,1] = get_child_counts(sup_test), - ?line {'EXIT',{noproc,{gen_server,call,[foo,which_children,infinity]}}} + {'EXIT',{noproc,{gen_server,call,[foo,which_children,infinity]}}} = (catch supervisor:which_children(foo)), - ?line {'EXIT',{noproc,{gen_server,call,[foo,count_children,infinity]}}} + {'EXIT',{noproc,{gen_server,call,[foo,count_children,infinity]}}} = (catch supervisor:count_children(foo)), ok. -%------------------------------------------------------------------------- +%%------------------------------------------------------------------------- child_adm_simple(doc) -> ["The API functions terminate_child/2, delete_child/2 " "restart_child/2 are not valid for a simple_one_for_one supervisor " - "check that the correct error message is returned."]; + "check that the correct error message is returned."]; child_adm_simple(suite) -> []; child_adm_simple(Config) when is_list(Config) -> Child = {child, {supervisor_1, start_child, []}, permanent, 1000, worker, []}, - ?line {ok, _Pid} = start({ok, {{simple_one_for_one, 2, 3600}, [Child]}}), + {ok, _Pid} = start_link({ok, {{simple_one_for_one, 2, 3600}, [Child]}}), %% In simple_one_for_one all children are added dynamically - ?line [] = supervisor:which_children(sup_test), - ?line [1,0,0,0] = get_child_counts(sup_test), - + [] = supervisor:which_children(sup_test), + [1,0,0,0] = get_child_counts(sup_test), + %% Start - ?line {'EXIT',{noproc,{gen_server,call, _}}} = + {'EXIT',{noproc,{gen_server,call, _}}} = (catch supervisor:start_child(foo, [])), - ?line {ok, CPid1} = supervisor:start_child(sup_test, []), - ?line [{undefined, CPid1, worker, []}] = + {ok, CPid1} = supervisor:start_child(sup_test, []), + [{undefined, CPid1, worker, []}] = supervisor:which_children(sup_test), - ?line [1,1,0,1] = get_child_counts(sup_test), - - ?line {ok, CPid2} = supervisor:start_child(sup_test, []), - ?line Children = supervisor:which_children(sup_test), - ?line 2 = length(Children), - ?line true = lists:member({undefined, CPid2, worker, []}, Children), - ?line true = lists:member({undefined, CPid1, worker, []}, Children), - ?line [1,2,0,2] = get_child_counts(sup_test), + [1,1,0,1] = get_child_counts(sup_test), + + {ok, CPid2} = supervisor:start_child(sup_test, []), + Children = supervisor:which_children(sup_test), + 2 = length(Children), + true = lists:member({undefined, CPid2, worker, []}, Children), + true = lists:member({undefined, CPid1, worker, []}, Children), + [1,2,0,2] = get_child_counts(sup_test), %% Termination - ?line {error, simple_one_for_one} = - supervisor:terminate_child(sup_test, child1), + {error, simple_one_for_one} = supervisor:terminate_child(sup_test, child1), + [1,2,0,2] = get_child_counts(sup_test), + ok = supervisor:terminate_child(sup_test,CPid1), + [_] = supervisor:which_children(sup_test), + [1,1,0,1] = get_child_counts(sup_test), + false = erlang:is_process_alive(CPid1), + %% Terminate non-existing proccess is ok + ok = supervisor:terminate_child(sup_test,CPid1), + [_] = supervisor:which_children(sup_test), + [1,1,0,1] = get_child_counts(sup_test), + %% Terminate pid which is not a child of this supervisor is not ok + NoChildPid = spawn_link(fun() -> receive after infinity -> ok end end), + {error, not_found} = supervisor:terminate_child(sup_test, NoChildPid), + true = erlang:is_process_alive(NoChildPid), %% Restart - ?line {error, simple_one_for_one} = - supervisor:restart_child(sup_test, child1), - + {error, simple_one_for_one} = supervisor:restart_child(sup_test, child1), + %% Deletion - ?line {error, simple_one_for_one} = - supervisor:delete_child(sup_test, child1), + {error, simple_one_for_one} = supervisor:delete_child(sup_test, child1), ok. - -%------------------------------------------------------------------------- + +%%------------------------------------------------------------------------- child_specs(doc) -> ["Tests child specs, invalid formats should be rejected."]; child_specs(suite) -> []; child_specs(Config) when is_list(Config) -> process_flag(trap_exit, true), - ?line {ok, _Pid} = start({ok, {{one_for_one, 2, 3600}, []}}), - ?line {error, _} = supervisor:start_child(sup_test, hej), + {ok, _Pid} = start_link({ok, {{one_for_one, 2, 3600}, []}}), + {error, _} = supervisor:start_child(sup_test, hej), %% Bad child specs B1 = {child, mfa, permanent, 1000, worker, []}, @@ -513,7 +457,7 @@ child_specs(Config) when is_list(Config) -> B5 = {child, {m,f,[a]}, permanent, infinity, worker, []}, B6 = {child, {m,f,[a]}, permanent, 1000, worker, dy}, B7 = {child, {m,f,[a]}, permanent, 1000, worker, [1,2,3]}, - + %% Correct child specs! %% <Modules> (last parameter in a child spec) can be [] as we do %% not test code upgrade here. @@ -522,358 +466,261 @@ child_specs(Config) when is_list(Config) -> C3 = {child, {m,f,[a]}, temporary, 1000, worker, dynamic}, C4 = {child, {m,f,[a]}, transient, 1000, worker, [m]}, - ?line {error, {invalid_mfa,mfa}} = supervisor:start_child(sup_test, B1), - ?line {error, {invalid_restart_type, prmanent}} = + {error, {invalid_mfa,mfa}} = supervisor:start_child(sup_test, B1), + {error, {invalid_restart_type, prmanent}} = supervisor:start_child(sup_test, B2), - ?line {error, {invalid_shutdown,-10}} - = supervisor:start_child(sup_test, B3), - ?line {error, {invalid_child_type,wrker}} + {error, {invalid_shutdown,-10}} + = supervisor:start_child(sup_test, B3), + {error, {invalid_child_type,wrker}} = supervisor:start_child(sup_test, B4), - ?line {error, _} = supervisor:start_child(sup_test, B5), - ?line {error, {invalid_modules,dy}} + {error, _} = supervisor:start_child(sup_test, B5), + {error, {invalid_modules,dy}} = supervisor:start_child(sup_test, B6), - - ?line {error, {invalid_mfa,mfa}} = supervisor:check_childspecs([B1]), - ?line {error, {invalid_restart_type,prmanent}} = + + {error, {invalid_mfa,mfa}} = supervisor:check_childspecs([B1]), + {error, {invalid_restart_type,prmanent}} = supervisor:check_childspecs([B2]), - ?line {error, {invalid_shutdown,-10}} = supervisor:check_childspecs([B3]), - ?line {error, {invalid_child_type,wrker}} + {error, {invalid_shutdown,-10}} = supervisor:check_childspecs([B3]), + {error, {invalid_child_type,wrker}} = supervisor:check_childspecs([B4]), - ?line {error, _} = supervisor:check_childspecs([B5]), - ?line {error, {invalid_modules,dy}} = supervisor:check_childspecs([B6]), - ?line {error, {invalid_module, 1}} = + {error, _} = supervisor:check_childspecs([B5]), + {error, {invalid_modules,dy}} = supervisor:check_childspecs([B6]), + {error, {invalid_module, 1}} = supervisor:check_childspecs([B7]), - ?line ok = supervisor:check_childspecs([C1]), - ?line ok = supervisor:check_childspecs([C2]), - ?line ok = supervisor:check_childspecs([C3]), - ?line ok = supervisor:check_childspecs([C4]), + ok = supervisor:check_childspecs([C1]), + ok = supervisor:check_childspecs([C2]), + ok = supervisor:check_childspecs([C3]), + ok = supervisor:check_childspecs([C4]), ok. -%------------------------------------------------------------------------- -normal_termination(doc) -> - ["Testes the supervisors behaviour if a child dies with reason normal"]; -normal_termination(suite) -> - [permanent_normal, transient_normal, temporary_normal]. -%------------------------------------------------------------------------- +%%------------------------------------------------------------------------- permanent_normal(doc) -> ["A permanent child should always be restarted"]; permanent_normal(suite) -> []; permanent_normal(Config) when is_list(Config) -> - ?line {ok, _SupPid} = start({ok, {{one_for_one, 2, 3600}, []}}), + {ok, SupPid} = start_link({ok, {{one_for_one, 2, 3600}, []}}), Child1 = {child1, {supervisor_1, start_child, []}, permanent, 1000, worker, []}, - - ?line {ok, CPid1} = supervisor:start_child(sup_test, Child1), - - CPid1 ! stop, - test_server:sleep(100), - ?line [{child1, Pid ,worker,[]}] = supervisor:which_children(sup_test), + + {ok, CPid1} = supervisor:start_child(sup_test, Child1), + + terminate(SupPid, CPid1, child1, normal), + + [{child1, Pid ,worker,[]}] = supervisor:which_children(sup_test), case is_pid(Pid) of true -> ok; false -> - ?line test_server:fail({permanent_child_not_restarted, Child1}) + test_server:fail({permanent_child_not_restarted, Child1}) end, - ?line [1,1,0,1] = get_child_counts(sup_test), + [1,1,0,1] = get_child_counts(sup_test). - ok. -%------------------------------------------------------------------------- +%%------------------------------------------------------------------------- transient_normal(doc) -> ["A transient child should not be restarted if it exits with " "reason normal"]; transient_normal(suite) -> []; transient_normal(Config) when is_list(Config) -> - ?line {ok, _SupPid} = start({ok, {{one_for_one, 2, 3600}, []}}), + {ok, SupPid} = start_link({ok, {{one_for_one, 2, 3600}, []}}), Child1 = {child1, {supervisor_1, start_child, []}, transient, 1000, worker, []}, - - ?line {ok, CPid1} = supervisor:start_child(sup_test, Child1), - - CPid1 ! stop, - test_server:sleep(100), - - ?line [{child1,undefined,worker,[]}] = supervisor:which_children(sup_test), - ?line [1,0,0,1] = get_child_counts(sup_test), - ok. -%------------------------------------------------------------------------- + {ok, CPid1} = supervisor:start_child(sup_test, Child1), + + terminate(SupPid, CPid1, child1, normal), + + [{child1,undefined,worker,[]}] = supervisor:which_children(sup_test), + [1,0,0,1] = get_child_counts(sup_test). + +%%------------------------------------------------------------------------- temporary_normal(doc) -> ["A temporary process should never be restarted"]; temporary_normal(suite) -> []; temporary_normal(Config) when is_list(Config) -> - ?line {ok, _SupPid} = start({ok, {{one_for_one, 2, 3600}, []}}), + {ok, SupPid} = start_link({ok, {{one_for_one, 2, 3600}, []}}), Child1 = {child1, {supervisor_1, start_child, []}, temporary, 1000, worker, []}, - - ?line {ok, CPid1} = supervisor:start_child(sup_test, Child1), - - CPid1 ! stop, - test_server:sleep(100), - - ?line [{child1,undefined,worker,[]}] = supervisor:which_children(sup_test), - ?line [1,0,0,1] = get_child_counts(sup_test), - ok. -%------------------------------------------------------------------------- -abnormal_termination(doc) -> - ["Testes the supervisors behaviour if a child dies with reason abnormal"]; -abnormal_termination(suite) -> - [permanent_abnormal, transient_abnormal, temporary_abnormal]. + {ok, CPid1} = supervisor:start_child(sup_test, Child1), + + terminate(SupPid, CPid1, child1, normal), + + [] = supervisor:which_children(sup_test), + [0,0,0,0] = get_child_counts(sup_test). -%------------------------------------------------------------------------- +%%------------------------------------------------------------------------- permanent_abnormal(doc) -> ["A permanent child should always be restarted"]; permanent_abnormal(suite) -> []; permanent_abnormal(Config) when is_list(Config) -> - ?line {ok, _SupPid} = start({ok, {{one_for_one, 2, 3600}, []}}), + {ok, SupPid} = start_link({ok, {{one_for_one, 2, 3600}, []}}), Child1 = {child1, {supervisor_1, start_child, []}, permanent, 1000, worker, []}, - - ?line {ok, CPid1} = supervisor:start_child(sup_test, Child1), - - CPid1 ! die, - test_server:sleep(100), - ?line [{child1, Pid ,worker,[]}] = supervisor:which_children(sup_test), + + {ok, CPid1} = supervisor:start_child(sup_test, Child1), + terminate(SupPid, CPid1, child1, abnormal), + + [{child1, Pid ,worker,[]}] = supervisor:which_children(sup_test), case is_pid(Pid) of true -> ok; false -> - ?line test_server:fail({permanent_child_not_restarted, Child1}) + test_server:fail({permanent_child_not_restarted, Child1}) end, - ?line [1,1,0,1] = get_child_counts(sup_test), + [1,1,0,1] = get_child_counts(sup_test). - ok. -%------------------------------------------------------------------------- +%%------------------------------------------------------------------------- transient_abnormal(doc) -> ["A transient child should be restarted if it exits with " "reason abnormal"]; transient_abnormal(suite) -> []; transient_abnormal(Config) when is_list(Config) -> - ?line {ok, _SupPid} = start({ok, {{one_for_one, 2, 3600}, []}}), + {ok, SupPid} = start_link({ok, {{one_for_one, 2, 3600}, []}}), Child1 = {child1, {supervisor_1, start_child, []}, transient, 1000, worker, []}, - - ?line {ok, CPid1} = supervisor:start_child(sup_test, Child1), - - CPid1 ! die, - test_server:sleep(100), - - ?line [{child1, Pid ,worker,[]}] = supervisor:which_children(sup_test), + + {ok, CPid1} = supervisor:start_child(sup_test, Child1), + terminate(SupPid, CPid1, child1, abnormal), + + [{child1, Pid ,worker,[]}] = supervisor:which_children(sup_test), case is_pid(Pid) of true -> ok; false -> - ?line test_server:fail({transient_child_not_restarted, Child1}) + test_server:fail({transient_child_not_restarted, Child1}) end, - ?line [1,1,0,1] = get_child_counts(sup_test), + [1,1,0,1] = get_child_counts(sup_test). - ok. -%------------------------------------------------------------------------- +%%------------------------------------------------------------------------- temporary_abnormal(doc) -> ["A temporary process should never be restarted"]; temporary_abnormal(suite) -> []; temporary_abnormal(Config) when is_list(Config) -> - ?line {ok, _SupPid} = start({ok, {{one_for_one, 2, 3600}, []}}), + {ok, SupPid} = start_link({ok, {{one_for_one, 2, 3600}, []}}), Child1 = {child1, {supervisor_1, start_child, []}, temporary, 1000, worker, []}, - - ?line {ok, CPid1} = supervisor:start_child(sup_test, Child1), - - CPid1 ! die, - test_server:sleep(100), - - ?line [{child1,undefined,worker,[]}] = supervisor:which_children(sup_test), - ?line [1,0,0,1] = get_child_counts(sup_test), - ok. -%------------------------------------------------------------------------- -restart_one_for_one(doc) -> - ["Test that the one_for_one strategy works."]; + {ok, CPid1} = supervisor:start_child(sup_test, Child1), + terminate(SupPid, CPid1, child1, abnormal), -restart_one_for_one(suite) -> [one_for_one, one_for_one_escalation]. + [] = supervisor:which_children(sup_test), + [0,0,0,0] = get_child_counts(sup_test). -%------------------------------------------------------------------------- +%%------------------------------------------------------------------------- one_for_one(doc) -> ["Test the one_for_one base case."]; one_for_one(suite) -> []; one_for_one(Config) when is_list(Config) -> process_flag(trap_exit, true), Child1 = {child1, {supervisor_1, start_child, []}, permanent, 1000, - worker, []}, + worker, []}, Child2 = {child2, {supervisor_1, start_child, []}, permanent, 1000, - worker, []}, - ?line {ok, Pid} = start({ok, {{one_for_one, 2, 3600}, []}}), - ?line {ok, CPid1} = supervisor:start_child(sup_test, Child1), - link(CPid1), - ?line {ok, CPid2} = supervisor:start_child(sup_test, Child2), - link(CPid2), - CPid1 ! die, - receive - {'EXIT', CPid1, died} -> ok; - {'EXIT', CPid1, Reason} -> - ?line test_server:fail({bad_exit_reason, Reason}) - end, - test_server:sleep(100), + worker, []}, + {ok, SupPid} = start_link({ok, {{one_for_one, 2, 3600}, []}}), + {ok, CPid1} = supervisor:start_child(sup_test, Child1), + {ok, CPid2} = supervisor:start_child(sup_test, Child2), + + terminate(SupPid, CPid1, child1, abnormal), Children = supervisor:which_children(sup_test), if length(Children) == 2 -> case lists:keysearch(CPid2, 2, Children) of {value, _} -> ok; - _ -> ?line test_server:fail(bad_child) + _ -> test_server:fail(bad_child) end; - true -> ?line test_server:fail({bad_child_list, Children}) + true -> test_server:fail({bad_child_list, Children}) end, - ?line [2,2,0,2] = get_child_counts(sup_test), - + [2,2,0,2] = get_child_counts(sup_test), + %% Test restart frequency property - CPid2 ! die, - receive - {'EXIT', CPid2, _} -> ok - end, - test_server:sleep(100), - [{_, Pid4, _, _}|_] = supervisor:which_children(sup_test), - Pid4 ! die, - receive - {'EXIT', Pid, _} -> ok - after 3000 -> ?line test_server:fail(restart_failed) - end, - ok. -%------------------------------------------------------------------------- + terminate(SupPid, CPid2, child2, abnormal), + + [{Id4, Pid4, _, _}|_] = supervisor:which_children(sup_test), + terminate(SupPid, Pid4, Id4, abnormal), + check_exit([SupPid]). + +%%------------------------------------------------------------------------- one_for_one_escalation(doc) -> ["Test restart escalation on a one_for_one supervisor."]; one_for_one_escalation(suite) -> []; one_for_one_escalation(Config) when is_list(Config) -> process_flag(trap_exit, true), + Child1 = {child1, {supervisor_1, start_child, [error]}, permanent, 1000, - worker, []}, + worker, []}, Child2 = {child2, {supervisor_1, start_child, []}, permanent, 1000, - worker, []}, - ?line {ok, Pid} = start({ok, {{one_for_one, 4, 3600}, []}}), - ?line {ok, CPid1} = supervisor:start_child(sup_test, Child1), - link(CPid1), - ?line {ok, CPid2} = supervisor:start_child(sup_test, Child2), + worker, []}, + + {ok, SupPid} = start_link({ok, {{one_for_one, 4, 3600}, []}}), + {ok, CPid1} = supervisor:start_child(sup_test, Child1), + {ok, CPid2} = supervisor:start_child(sup_test, Child2), link(CPid2), - CPid1 ! die, - receive - {'EXIT', CPid1, died} -> ok; - {'EXIT', CPid1, Reason} -> - ?line test_server:fail({bad_exit_reason, Reason}) - end, - receive - {'EXIT', Pid, _} -> ok - after - 2000 -> ?line test_server:fail(supervisor_alive) - end, - receive - {'EXIT', CPid2, _} -> ok - after - 4000 -> ?line test_server:fail(all_not_terminated) - end, - ok. -%------------------------------------------------------------------------- -restart_one_for_all(doc) -> - ["Test that the one_for_all strategy works."]; -restart_one_for_all(suite) -> - [one_for_all, one_for_all_escalation]. + terminate(SupPid, CPid1, child1, abnormal), + check_exit([SupPid, CPid2]). + -%------------------------------------------------------------------------- +%%------------------------------------------------------------------------- one_for_all(doc) -> ["Test the one_for_all base case."]; one_for_all(suite) -> []; one_for_all(Config) when is_list(Config) -> process_flag(trap_exit, true), + Child1 = {child1, {supervisor_1, start_child, []}, permanent, 1000, worker, []}, Child2 = {child2, {supervisor_1, start_child, []}, permanent, 1000, worker, []}, - ?line {ok, Pid} = start({ok, {{one_for_all, 2, 3600}, []}}), - ?line {ok, CPid1} = supervisor:start_child(sup_test, Child1), - link(CPid1), - ?line {ok, CPid2} = supervisor:start_child(sup_test, Child2), + {ok, SupPid} = start_link({ok, {{one_for_all, 2, 3600}, []}}), + {ok, CPid1} = supervisor:start_child(sup_test, Child1), + {ok, CPid2} = supervisor:start_child(sup_test, Child2), link(CPid2), - CPid1 ! die, - receive - {'EXIT', CPid1, died} -> ok; - {'EXIT', CPid1, Reason} -> - ?line test_server:fail({bad_exit_reason, Reason}) - end, - receive - {'EXIT', CPid2, _} -> ok - end, - test_server:sleep(100), + + terminate(SupPid, CPid1, child1, abnormal), + check_exit([CPid2]), + Children = supervisor:which_children(sup_test), if length(Children) == 2 -> ok; - true -> ?line test_server:fail({bad_child_list, Children}) + true -> + test_server:fail({bad_child_list, Children}) end, + %% Test that no old children is still alive - SCh = lists:map(fun({_,P,_,_}) -> P end, Children), - case lists:member(CPid1, SCh) of - true -> ?line test_server:fail(bad_child); - false -> ok - end, - case lists:member(CPid2, SCh) of - true -> ?line test_server:fail(bad_child); - false -> ok - end, - ?line [2,2,0,2] = get_child_counts(sup_test), + not_in_child_list([CPid1, CPid2], lists:map(fun({_,P,_,_}) -> P end, Children)), + + [2,2,0,2] = get_child_counts(sup_test), %%% Test restart frequency property - [{_, Pid3, _, _}|_] = supervisor:which_children(sup_test), - Pid3 ! die, - test_server:sleep(100), - [{_, Pid4, _, _}|_] = supervisor:which_children(sup_test), - Pid4 ! die, - receive - {'EXIT', Pid, _} -> ok - after 3000 -> ?line test_server:fail(restart_failed) - end, - exit(Pid, shutdown). + [{Id3, Pid3, _, _}|_] = supervisor:which_children(sup_test), + terminate(SupPid, Pid3, Id3, abnormal), + [{Id4, Pid4, _, _}|_] = supervisor:which_children(sup_test), + terminate(SupPid, Pid4, Id4, abnormal), + check_exit([SupPid]). + -%------------------------------------------------------------------------- +%%------------------------------------------------------------------------- one_for_all_escalation(doc) -> ["Test restart escalation on a one_for_all supervisor."]; one_for_all_escalation(suite) -> []; one_for_all_escalation(Config) when is_list(Config) -> process_flag(trap_exit, true), + Child1 = {child1, {supervisor_1, start_child, []}, permanent, 1000, worker, []}, Child2 = {child2, {supervisor_1, start_child, [error]}, permanent, 1000, worker, []}, - ?line {ok, Pid} = start({ok, {{one_for_all, 4, 3600}, []}}), - ?line {ok, CPid1} = supervisor:start_child(sup_test, Child1), - link(CPid1), - ?line {ok, CPid2} = supervisor:start_child(sup_test, Child2), + {ok, SupPid} = start_link({ok, {{one_for_all, 4, 3600}, []}}), + {ok, CPid1} = supervisor:start_child(sup_test, Child1), + {ok, CPid2} = supervisor:start_child(sup_test, Child2), link(CPid2), - CPid1 ! die, - receive - {'EXIT', CPid1, died} -> ok; - {'EXIT', CPid1, Reason} -> - ?line test_server:fail({bad_exit_reason, Reason}) - end, - receive - {'EXIT', CPid2, _} -> ok - after - 2000 -> ?line test_server:fail(all_not_terminated) - end, - receive - {'EXIT', Pid, _} -> ok - after - 4000 -> ?line test_server:fail(supervisor_alive) - end, - ok. -%------------------------------------------------------------------------- -restart_simple_one_for_one(doc) -> - ["Test that the simple_one_for_one strategy works."]; + terminate(SupPid, CPid1, child1, abnormal), + check_exit([CPid2, SupPid]). -restart_simple_one_for_one(suite) -> - [simple_one_for_one, simple_one_for_one_extra, - simple_one_for_one_escalation]. -%------------------------------------------------------------------------- +%%------------------------------------------------------------------------- simple_one_for_one(doc) -> ["Test the simple_one_for_one base case."]; simple_one_for_one(suite) -> []; @@ -881,42 +728,31 @@ simple_one_for_one(Config) when is_list(Config) -> process_flag(trap_exit, true), Child = {child, {supervisor_1, start_child, []}, permanent, 1000, worker, []}, - ?line {ok, Pid} = start({ok, {{simple_one_for_one, 2, 3600}, [Child]}}), - ?line {ok, CPid1} = supervisor:start_child(sup_test, []), - link(CPid1), - ?line {ok, CPid2} = supervisor:start_child(sup_test, []), - link(CPid2), - CPid1 ! die, - receive - {'EXIT', CPid1, died} -> ok; - {'EXIT', CPid1, Reason} -> - ?line test_server:fail({bad_exit_reason, Reason}) - end, - test_server:sleep(100), + {ok, SupPid} = start_link({ok, {{simple_one_for_one, 2, 3600}, [Child]}}), + {ok, CPid1} = supervisor:start_child(sup_test, []), + {ok, CPid2} = supervisor:start_child(sup_test, []), + + terminate(SupPid, CPid1, child1, abnormal), + Children = supervisor:which_children(sup_test), if length(Children) == 2 -> case lists:keysearch(CPid2, 2, Children) of {value, _} -> ok; - _ -> ?line test_server:fail(bad_child) + _ -> test_server:fail(bad_child) end; - true -> ?line test_server:fail({bad_child_list, Children}) + true -> test_server:fail({bad_child_list, Children}) end, - ?line [1,2,0,2] = get_child_counts(sup_test), + [1,2,0,2] = get_child_counts(sup_test), %% Test restart frequency property - CPid2 ! die, - receive - {'EXIT', CPid2, _} -> ok - end, - test_server:sleep(100), - [{_, Pid4, _, _}|_] = supervisor:which_children(sup_test), - Pid4 ! die, - receive - {'EXIT', Pid, _} -> ok - after 3000 -> ?line test_server:fail(restart_failed) - end, - ok. -%------------------------------------------------------------------------- + terminate(SupPid, CPid2, child2, abnormal), + + [{Id4, Pid4, _, _}|_] = supervisor:which_children(sup_test), + + terminate(SupPid, Pid4, Id4, abnormal), + check_exit([SupPid]). + +%%------------------------------------------------------------------------- simple_one_for_one_extra(doc) -> ["Tests automatic restart of children " "who's start function return extra info."]; @@ -925,41 +761,26 @@ simple_one_for_one_extra(Config) when is_list(Config) -> process_flag(trap_exit, true), Child = {child, {supervisor_1, start_child, [extra_info]}, permanent, 1000, worker, []}, - ?line {ok, Pid} = start({ok, {{simple_one_for_one, 2, 3600}, [Child]}}), - ?line {ok, CPid1, extra_info} = supervisor:start_child(sup_test, []), - link(CPid1), - ?line {ok, CPid2, extra_info} = supervisor:start_child(sup_test, []), + {ok, SupPid} = start_link({ok, {{simple_one_for_one, 2, 3600}, [Child]}}), + {ok, CPid1, extra_info} = supervisor:start_child(sup_test, []), + {ok, CPid2, extra_info} = supervisor:start_child(sup_test, []), link(CPid2), - CPid1 ! die, - receive - {'EXIT', CPid1, died} -> ok; - {'EXIT', CPid1, Reason} -> - ?line test_server:fail({bad_exit_reason, Reason}) - end, - test_server:sleep(100), + terminate(SupPid, CPid1, child1, abnormal), Children = supervisor:which_children(sup_test), if length(Children) == 2 -> case lists:keysearch(CPid2, 2, Children) of {value, _} -> ok; - _ -> ?line test_server:fail(bad_child) + _ -> test_server:fail(bad_child) end; - true -> ?line test_server:fail({bad_child_list, Children}) + true -> test_server:fail({bad_child_list, Children}) end, - ?line [1,2,0,2] = get_child_counts(sup_test), + [1,2,0,2] = get_child_counts(sup_test), + terminate(SupPid, CPid2, child2, abnormal), + [{Id4, Pid4, _, _}|_] = supervisor:which_children(sup_test), + terminate(SupPid, Pid4, Id4, abnormal), + check_exit([SupPid]). - CPid2 ! die, - receive - {'EXIT', CPid2, _} -> ok - end, - test_server:sleep(100), - [{_, Pid4, _, _}|_] = supervisor:which_children(sup_test), - Pid4 ! die, - receive - {'EXIT', Pid, _} -> ok - after 3000 -> ?line test_server:fail(restart_failed) - end, - ok. -%------------------------------------------------------------------------- +%%------------------------------------------------------------------------- simple_one_for_one_escalation(doc) -> ["Test restart escalation on a simple_one_for_one supervisor."]; simple_one_for_one_escalation(suite) -> []; @@ -967,34 +788,16 @@ simple_one_for_one_escalation(Config) when is_list(Config) -> process_flag(trap_exit, true), Child = {child, {supervisor_1, start_child, []}, permanent, 1000, worker, []}, - ?line {ok, Pid} = start({ok, {{simple_one_for_one, 4, 3600}, [Child]}}), - ?line {ok, CPid1} = supervisor:start_child(sup_test, [error]), + {ok, SupPid} = start_link({ok, {{simple_one_for_one, 4, 3600}, [Child]}}), + {ok, CPid1} = supervisor:start_child(sup_test, [error]), link(CPid1), - ?line {ok, CPid2} = supervisor:start_child(sup_test, []), + {ok, CPid2} = supervisor:start_child(sup_test, []), link(CPid2), - CPid1 ! die, - receive - {'EXIT', CPid1, died} -> ok; - {'EXIT', CPid1, Reason} -> - ?line test_server:fail({bad_exit_reason, Reason}) - end, - receive - {'EXIT', Pid, _} -> ok - after - 2000 -> ?line test_server:fail(supervisor_alive) - end, - receive - {'EXIT', CPid2, _} -> ok - after - 2000 -> ?line test_server:fail(all_not_terminated) - end, - ok. -%------------------------------------------------------------------------- -restart_rest_for_one(doc) -> - ["Test that the rest_for_one strategy works."]; -restart_rest_for_one(suite) -> [rest_for_one, rest_for_one_escalation]. -%------------------------------------------------------------------------- + terminate(SupPid, CPid1, child, abnormal), + check_exit([SupPid, CPid2]). + +%%------------------------------------------------------------------------- rest_for_one(doc) -> ["Test the rest_for_one base case."]; rest_for_one(suite) -> []; @@ -1006,70 +809,45 @@ rest_for_one(Config) when is_list(Config) -> worker, []}, Child3 = {child3, {supervisor_1, start_child, []}, permanent, 1000, worker, []}, - ?line {ok, Pid} = start({ok, {{rest_for_one, 2, 3600}, []}}), - ?line {ok, CPid1} = supervisor:start_child(sup_test, Child1), + {ok, SupPid} = start_link({ok, {{rest_for_one, 2, 3600}, []}}), + {ok, CPid1} = supervisor:start_child(sup_test, Child1), link(CPid1), - ?line {ok, CPid2} = supervisor:start_child(sup_test, Child2), - link(CPid2), - ?line {ok, CPid3} = supervisor:start_child(sup_test, Child3), + {ok, CPid2} = supervisor:start_child(sup_test, Child2), + {ok, CPid3} = supervisor:start_child(sup_test, Child3), link(CPid3), - ?line [3,3,0,3] = get_child_counts(sup_test), + [3,3,0,3] = get_child_counts(sup_test), + + terminate(SupPid, CPid2, child2, abnormal), - CPid2 ! die, - receive - {'EXIT', CPid2, died} -> ok; - {'EXIT', CPid2, Reason} -> - ?line test_server:fail({bad_exit_reason, Reason}) - after 2000 -> - ?line test_server:fail(no_exit) - end, %% Check that Cpid3 did die - receive - {'EXIT', CPid3, _} -> ok - after 2000 -> - ?line test_server:fail(no_exit) - end, - %% Check that Cpid1 didn't die - receive - {'EXIT', CPid1, _} -> - ?line test_server:fail(bad_exit) - after - 100 -> ok - end, + check_exit([CPid3]), + Children = supervisor:which_children(sup_test), - if length(Children) == 3 -> ok; - true -> ?line test_server:fail({bad_child_list, Children}) + is_in_child_list([CPid1], Children), + + if length(Children) == 3 -> + ok; + true -> + test_server:fail({bad_child_list, Children}) end, - ?line [3,3,0,3] = get_child_counts(sup_test), + [3,3,0,3] = get_child_counts(sup_test), %% Test that no old children is still alive - SCh = lists:map(fun({_,P,_,_}) -> P end, Children), - case lists:member(CPid1, SCh) of - true -> ok; - false -> ?line test_server:fail(bad_child) - end, - case lists:member(CPid2, SCh) of - true -> ?line test_server:fail(bad_child); - false -> ok - end, - case lists:member(CPid3, SCh) of - true -> ?line test_server:fail(bad_child); - false -> ok - end, - + Pids = lists:map(fun({_,P,_,_}) -> P end, Children), + not_in_child_list([CPid2, CPid3], Pids), + in_child_list([CPid1], Pids), + %% Test restart frequency property [{child3, Pid3, _, _}|_] = supervisor:which_children(sup_test), - Pid3 ! die, - test_server:sleep(100), + + terminate(SupPid, Pid3, child3, abnormal), + [_,{child2, Pid4, _, _}|_] = supervisor:which_children(sup_test), - Pid4 ! die, - receive - {'EXIT', Pid, _} -> ok - after 3000 -> ?line test_server:fail(restart_failed) - end, - exit(Pid, shutdown). -%------------------------------------------------------------------------- + terminate(SupPid, Pid4, child2, abnormal), + check_exit([SupPid]). + +%%------------------------------------------------------------------------- rest_for_one_escalation(doc) -> ["Test restart escalation on a rest_for_one supervisor."]; rest_for_one_escalation(suite) -> []; @@ -1080,42 +858,29 @@ rest_for_one_escalation(Config) when is_list(Config) -> Child2 = {child2, {supervisor_1, start_child, [error]}, permanent, 1000, worker, []}, - ?line {ok, Pid} = start({ok, {{rest_for_one, 4, 3600}, []}}), - ?line {ok, CPid1} = supervisor:start_child(sup_test, Child1), - link(CPid1), - ?line {ok, CPid2} = supervisor:start_child(sup_test, Child2), + {ok, SupPid} = start_link({ok, {{rest_for_one, 4, 3600}, []}}), + {ok, CPid1} = supervisor:start_child(sup_test, Child1), + {ok, CPid2} = supervisor:start_child(sup_test, Child2), link(CPid2), - CPid1 ! die, - receive - {'EXIT', CPid1, died} -> ok; - {'EXIT', CPid1, Reason} -> - ?line test_server:fail({bad_exit_reason, Reason}) - end, - receive - {'EXIT', CPid2, _} -> ok - after - 2000 -> ?line test_server:fail(not_terminated) - end, - receive - {'EXIT', Pid, _} -> ok - after - 4000 -> ?line test_server:fail(supervisor_alive) - end, - ok. -%------------------------------------------------------------------------- -child_unlink(doc)-> ["Test that the supervisor does not hang forever if " - "the child unliks and then is terminated by the supervisor."]; -child_unlink(suite) -> []; + terminate(SupPid, CPid1, child1, abnormal), + check_exit([CPid2, SupPid]). + +%%------------------------------------------------------------------------- +child_unlink(doc)-> + ["Test that the supervisor does not hang forever if " + "the child unliks and then is terminated by the supervisor."]; +child_unlink(suite) -> + []; child_unlink(Config) when is_list(Config) -> - - ?line {ok, SupPid} = start({ok, {{one_for_one, 2, 3600}, []}}), - + + {ok, SupPid} = start_link({ok, {{one_for_one, 2, 3600}, []}}), + Child = {naughty_child, {naughty_child, start_link, [SupPid]}, permanent, 1000, worker, [supervisor_SUITE]}, - - ?line {ok, _ChildPid} = supervisor:start_child(sup_test, Child), + + {ok, _ChildPid} = supervisor:start_child(sup_test, Child), Pid = spawn(supervisor, terminate_child, [SupPid, naughty_child]), @@ -1128,17 +893,16 @@ child_unlink(Config) when is_list(Config) -> ok; _ -> exit(Pid, kill), - ?line test_server:fail(supervisor_hangs) + test_server:fail(supervisor_hangs) end. -%------------------------------------------------------------------------- - +%%------------------------------------------------------------------------- tree(doc) -> ["Test a basic supervison tree."]; tree(suite) -> []; tree(Config) when is_list(Config) -> process_flag(trap_exit, true), - + Child1 = {child1, {supervisor_1, start_child, []}, permanent, 1000, worker, []}, @@ -1164,134 +928,63 @@ tree(Config) when is_list(Config) -> supervisor, []}, %% Top supervisor - ?line {ok, Pid} = start({ok, {{one_for_all, 4, 3600}, []}}), - + {ok, SupPid} = start_link({ok, {{one_for_all, 4, 3600}, []}}), + %% Child supervisors - ?line {ok, Sup1} = supervisor:start_child(Pid, ChildSup1), - ?line {ok, Sup2} = supervisor:start_child(Pid, ChildSup2), - ?line [2,2,2,0] = get_child_counts(Pid), - + {ok, Sup1} = supervisor:start_child(SupPid, ChildSup1), + {ok, Sup2} = supervisor:start_child(SupPid, ChildSup2), + [2,2,2,0] = get_child_counts(SupPid), + %% Workers - ?line [{_, CPid2, _, _},{_, CPid1, _, _}] = + [{_, CPid2, _, _},{_, CPid1, _, _}] = supervisor:which_children(Sup1), - ?line [2,2,0,2] = get_child_counts(Sup1), - ?line [0,0,0,0] = get_child_counts(Sup2), - + [2,2,0,2] = get_child_counts(Sup1), + [0,0,0,0] = get_child_counts(Sup2), + %% Dynamic children - ?line {ok, CPid3} = supervisor:start_child(Sup2, Child3), - ?line {ok, CPid4} = supervisor:start_child(Sup2, Child4), - ?line [2,2,0,2] = get_child_counts(Sup1), - ?line [2,2,0,2] = get_child_counts(Sup2), - - link(Sup1), - link(Sup2), - link(CPid1), - link(CPid2), - link(CPid3), - link(CPid4), - + {ok, CPid3} = supervisor:start_child(Sup2, Child3), + {ok, CPid4} = supervisor:start_child(Sup2, Child4), + [2,2,0,2] = get_child_counts(Sup1), + [2,2,0,2] = get_child_counts(Sup2), + %% Test that the only the process that dies is restarted - CPid4 ! die, - - receive - {'EXIT', CPid4, _} -> ?line ok - after 10000 -> - ?line test_server:fail(child_was_not_killed) - end, - - test_server:sleep(100), - - ?line [{_, CPid2, _, _},{_, CPid1, _, _}] = + terminate(Sup2, CPid4, child4, abnormal), + + [{_, CPid2, _, _},{_, CPid1, _, _}] = supervisor:which_children(Sup1), - ?line [2,2,0,2] = get_child_counts(Sup1), - - ?line [{_, NewCPid4, _, _},{_, CPid3, _, _}] = + [2,2,0,2] = get_child_counts(Sup1), + + [{_, NewCPid4, _, _},{_, CPid3, _, _}] = supervisor:which_children(Sup2), - ?line [2,2,0,2] = get_child_counts(Sup2), - - link(NewCPid4), + [2,2,0,2] = get_child_counts(Sup2), + + false = NewCPid4 == CPid4, %% Test that supervisor tree is restarted, but not dynamic children. - CPid3 ! die, + terminate(Sup2, CPid3, child3, abnormal), - receive - {'EXIT', CPid3, died} -> ?line ok; - {'EXIT', CPid3, Reason} -> - ?line test_server:fail({bad_exit_reason, Reason}) - after 1000 -> - ?line test_server:fail(child_was_not_killed) - end, + timer:sleep(1000), - test_server:sleep(1000), + [{supchild2, NewSup2, _, _},{supchild1, NewSup1, _, _}] = + supervisor:which_children(SupPid), + [2,2,2,0] = get_child_counts(SupPid), - receive - {'EXIT', NewCPid4, _} -> ?line ok - after 1000 -> - ?line test_server:fail(child_was_not_killed) - end, - - receive - {'EXIT', Sup2, _} -> ?line ok - after 1000 -> - ?line test_server:fail(child_was_not_killed) - end, - - receive - {'EXIT', CPid1, _} -> ?line ok - after 1000 -> - ?line test_server:fail(child_was_not_killed) - end, - - receive - {'EXIT', CPid2, _} -> ?line ok - after 1000 -> - ?line test_server:fail(child_was_not_killed) - end, - - receive - {'EXIT', Sup1, _} -> ?line ok - after 1000 -> - ?line test_server:fail(child_was_not_killed) - end, - - ?line [{supchild2, NewSup2, _, _},{supchild1, NewSup1, _, _}] = - supervisor:which_children(Pid), - ?line [2,2,2,0] = get_child_counts(Pid), - - ?line [{child2, _, _, _},{child1, _, _, _}] = + [{child2, _, _, _},{child1, _, _, _}] = supervisor:which_children(NewSup1), - ?line [2,2,0,2] = get_child_counts(NewSup1), - - ?line [] = supervisor:which_children(NewSup2), - ?line [0,0,0,0] = get_child_counts(NewSup2), - - ok. -%------------------------------------------------------------------------- -count_children_allocator_test(MemoryState) -> - Allocators = [temp_alloc, eheap_alloc, binary_alloc, ets_alloc, - driver_alloc, sl_alloc, ll_alloc, fix_alloc, std_alloc, - sys_alloc], - MemoryStateList = element(4, MemoryState), - AllocTypes = [lists:keyfind(Alloc, 1, MemoryStateList) - || Alloc <- Allocators], - AllocStates = [lists:keyfind(e, 1, AllocValue) - || {_Type, AllocValue} <- AllocTypes], - lists:all(fun(State) -> State == {e, true} end, AllocStates). + [2,2,0,2] = get_child_counts(NewSup1), + [] = supervisor:which_children(NewSup2), + [0,0,0,0] = get_child_counts(NewSup2). +%%------------------------------------------------------------------------- count_children_memory(doc) -> - ["Test that which_children eats memory, but count_children does not."]; + ["Test that count_children does not eat memory."]; count_children_memory(suite) -> - MemoryState = erlang:system_info(allocator), - case count_children_allocator_test(MemoryState) of - true -> []; - false -> - {skip, "+Meamin used during test; erlang:memory/1 not available"} - end; + []; count_children_memory(Config) when is_list(Config) -> process_flag(trap_exit, true), Child = {child, {supervisor_1, start_child, []}, temporary, 1000, worker, []}, - ?line {ok, _Pid} = start({ok, {{simple_one_for_one, 2, 3600}, [Child]}}), + {ok, SupPid} = start_link({ok, {{simple_one_for_one, 2, 3600}, [Child]}}), [supervisor:start_child(sup_test, []) || _Ignore <- lists:seq(1,1000)], garbage_collect(), @@ -1299,7 +992,7 @@ count_children_memory(Config) when is_list(Config) -> Children = supervisor:which_children(sup_test), _Size2 = erlang:memory(processes_used), ChildCount = get_child_counts(sup_test), - Size3 = erlang:memory(processes_used), + _Size3 = erlang:memory(processes_used), [supervisor:start_child(sup_test, []) || _Ignore2 <- lists:seq(1,1000)], @@ -1315,40 +1008,318 @@ count_children_memory(Config) when is_list(Config) -> ChildCount3 = get_child_counts(sup_test), Size7 = erlang:memory(processes_used), - ?line 1000 = length(Children), - ?line [1,1000,0,1000] = ChildCount, - ?line 2000 = length(Children2), - ?line [1,2000,0,2000] = ChildCount2, - ?line Children3 = Children2, - ?line ChildCount3 = ChildCount2, + 1000 = length(Children), + [1,1000,0,1000] = ChildCount, + 2000 = length(Children2), + [1,2000,0,2000] = ChildCount2, + Children3 = Children2, + ChildCount3 = ChildCount2, %% count_children consumes memory using an accumulator function, - %% but the space can be reclaimed incrementally, whereas - %% which_children generates a return list. + %% but the space can be reclaimed incrementally, + %% which_children may generate garbage that will be reclaimed later. case (Size5 =< Size4) of true -> ok; false -> - ?line test_server:fail({count_children, used_more_memory}) + test_server:fail({count_children, used_more_memory}) end, case Size7 =< Size6 of true -> ok; false -> - ?line test_server:fail({count_children, used_more_memory}) + test_server:fail({count_children, used_more_memory}) end, - case Size4 > Size3 of - true -> ok; + [terminate(SupPid, Pid, child, kill) || {undefined, Pid, worker, _Modules} <- Children3], + [1,0,0,0] = get_child_counts(sup_test). + +count_children_allocator_test(MemoryState) -> + Allocators = [temp_alloc, eheap_alloc, binary_alloc, ets_alloc, + driver_alloc, sl_alloc, ll_alloc, fix_alloc, std_alloc, + sys_alloc], + MemoryStateList = element(4, MemoryState), + AllocTypes = [lists:keyfind(Alloc, 1, MemoryStateList) + || Alloc <- Allocators], + AllocStates = [lists:keyfind(e, 1, AllocValue) + || {_Type, AllocValue} <- AllocTypes], + lists:all(fun(State) -> State == {e, true} end, AllocStates). + +%%------------------------------------------------------------------------- +do_not_save_start_parameters_for_temporary_children(doc) -> + ["Temporary children shall not be restarted so they should not " + "save start parameters, as it potentially can " + "take up a huge amount of memory for no purpose."]; +do_not_save_start_parameters_for_temporary_children(suite) -> + []; +do_not_save_start_parameters_for_temporary_children(Config) when is_list(Config) -> + process_flag(trap_exit, true), + dont_save_start_parameters_for_temporary_children(one_for_all), + dont_save_start_parameters_for_temporary_children(one_for_one), + dont_save_start_parameters_for_temporary_children(rest_for_one), + dont_save_start_parameters_for_temporary_children(simple_one_for_one). + +start_children(_,_, 0) -> + ok; +start_children(Sup, Args, N) -> + Spec = child_spec(Args, N), + {ok, _, _} = supervisor:start_child(Sup, Spec), + start_children(Sup, Args, N-1). + +child_spec([_|_] = SimpleOneForOneArgs, _) -> + SimpleOneForOneArgs; +child_spec({Name, MFA, RestartType, Shutdown, Type, Modules}, N) -> + NewName = list_to_atom((atom_to_list(Name) ++ integer_to_list(N))), + {NewName, MFA, RestartType, Shutdown, Type, Modules}. + +%%------------------------------------------------------------------------- +do_not_save_child_specs_for_temporary_children(doc) -> + ["Temporary children shall not be restarted so supervisors should " + "not save their spec when they terminate"]; +do_not_save_child_specs_for_temporary_children(suite) -> + []; +do_not_save_child_specs_for_temporary_children(Config) when is_list(Config) -> + process_flag(trap_exit, true), + dont_save_child_specs_for_temporary_children(one_for_all, kill), + dont_save_child_specs_for_temporary_children(one_for_one, kill), + dont_save_child_specs_for_temporary_children(rest_for_one, kill), + + dont_save_child_specs_for_temporary_children(one_for_all, normal), + dont_save_child_specs_for_temporary_children(one_for_one, normal), + dont_save_child_specs_for_temporary_children(rest_for_one, normal), + + dont_save_child_specs_for_temporary_children(one_for_all, abnormal), + dont_save_child_specs_for_temporary_children(one_for_one, abnormal), + dont_save_child_specs_for_temporary_children(rest_for_one, abnormal), + + dont_save_child_specs_for_temporary_children(one_for_all, supervisor), + dont_save_child_specs_for_temporary_children(one_for_one, supervisor), + dont_save_child_specs_for_temporary_children(rest_for_one, supervisor). + +%%------------------------------------------------------------------------- +dont_save_start_parameters_for_temporary_children(simple_one_for_one = Type) -> + Permanent = {child, {supervisor_1, start_child, []}, + permanent, 1000, worker, []}, + Transient = {child, {supervisor_1, start_child, []}, + transient, 1000, worker, []}, + Temporary = {child, {supervisor_1, start_child, []}, + temporary, 1000, worker, []}, + {ok, Sup1} = supervisor:start_link(?MODULE, {ok, {{Type, 2, 3600}, [Permanent]}}), + {ok, Sup2} = supervisor:start_link(?MODULE, {ok, {{Type, 2, 3600}, [Transient]}}), + {ok, Sup3} = supervisor:start_link(?MODULE, {ok, {{Type, 2, 3600}, [Temporary]}}), + + LargeList = lists:duplicate(10, "Potentially large"), + + start_children(Sup1, [LargeList], 100), + start_children(Sup2, [LargeList], 100), + start_children(Sup3, [LargeList], 100), + + [{memory,Mem1}] = process_info(Sup1, [memory]), + [{memory,Mem2}] = process_info(Sup2, [memory]), + [{memory,Mem3}] = process_info(Sup3, [memory]), + + true = (Mem3 < Mem1) and (Mem3 < Mem2), + + terminate(Sup1, shutdown), + terminate(Sup2, shutdown), + terminate(Sup3, shutdown); + +dont_save_start_parameters_for_temporary_children(Type) -> + {ok, Sup1} = supervisor:start_link(?MODULE, {ok, {{Type, 2, 3600}, []}}), + {ok, Sup2} = supervisor:start_link(?MODULE, {ok, {{Type, 2, 3600}, []}}), + {ok, Sup3} = supervisor:start_link(?MODULE, {ok, {{Type, 2, 3600}, []}}), + + LargeList = lists:duplicate(10, "Potentially large"), + + Permanent = {child1, {supervisor_1, start_child, [LargeList]}, + permanent, 1000, worker, []}, + Transient = {child2, {supervisor_1, start_child, [LargeList]}, + transient, 1000, worker, []}, + Temporary = {child3, {supervisor_1, start_child, [LargeList]}, + temporary, 1000, worker, []}, + + start_children(Sup1, Permanent, 100), + start_children(Sup2, Transient, 100), + start_children(Sup3, Temporary, 100), + + [{memory,Mem1}] = process_info(Sup1, [memory]), + [{memory,Mem2}] = process_info(Sup2, [memory]), + [{memory,Mem3}] = process_info(Sup3, [memory]), + + true = (Mem3 < Mem1) and (Mem3 < Mem2), + + terminate(Sup1, shutdown), + terminate(Sup2, shutdown), + terminate(Sup3, shutdown). + +dont_save_child_specs_for_temporary_children(Type, TerminateHow)-> + {ok, Sup} = + supervisor:start_link(?MODULE, {ok, {{Type, 2, 3600}, []}}), + + Permanent = {child1, {supervisor_1, start_child, []}, + permanent, 1000, worker, []}, + Transient = {child2, {supervisor_1, start_child, []}, + transient, 1000, worker, []}, + Temporary = {child3, {supervisor_1, start_child, []}, + temporary, 1000, worker, []}, + + permanent_child_spec_saved(Permanent, Sup, TerminateHow), + + transient_child_spec_saved(Transient, Sup, TerminateHow), + + temporary_child_spec_not_saved(Temporary, Sup, TerminateHow), + + terminate(Sup, shutdown). + +permanent_child_spec_saved(ChildSpec, Sup, supervisor = TerminateHow) -> + already_present(Sup, ChildSpec, TerminateHow); + +permanent_child_spec_saved(ChildSpec, Sup, TerminateHow) -> + restarted(Sup, ChildSpec, TerminateHow). + +transient_child_spec_saved(ChildSpec, Sup, supervisor = TerminateHow) -> + already_present(Sup, ChildSpec, TerminateHow); + +transient_child_spec_saved(ChildSpec, Sup, normal = TerminateHow) -> + already_present(Sup, ChildSpec, TerminateHow); + +transient_child_spec_saved(ChildSpec, Sup, TerminateHow) -> + restarted(Sup, ChildSpec, TerminateHow). + +temporary_child_spec_not_saved({Id, _,_,_,_,_} = ChildSpec, Sup, TerminateHow) -> + {ok, Pid} = supervisor:start_child(Sup, ChildSpec), + terminate(Sup, Pid, Id, TerminateHow), + {ok, _} = supervisor:start_child(Sup, ChildSpec). + +already_present(Sup, {Id,_,_,_,_,_} = ChildSpec, TerminateHow) -> + {ok, Pid} = supervisor:start_child(Sup, ChildSpec), + terminate(Sup, Pid, Id, TerminateHow), + {error, already_present} = supervisor:start_child(Sup, ChildSpec), + {ok, _} = supervisor:restart_child(Sup, Id). + +restarted(Sup, {Id,_,_,_,_,_} = ChildSpec, TerminateHow) -> + {ok, Pid} = supervisor:start_child(Sup, ChildSpec), + terminate(Sup, Pid, Id, TerminateHow), + %% Permanent processes will be restarted by the supervisor + %% when not terminated by api + {error, {already_started, _}} = supervisor:start_child(Sup, ChildSpec). + + +%%------------------------------------------------------------------------- +%% OTP-9242: Pids for dynamic temporary children were saved as a list, +%% which caused bad scaling when adding/deleting many processes. +simple_one_for_one_scale_many_temporary_children(_Config) -> + process_flag(trap_exit, true), + Child = {child, {supervisor_1, start_child, []}, temporary, 1000, + worker, []}, + {ok, _SupPid} = start_link({ok, {{simple_one_for_one, 2, 3600}, [Child]}}), + + C1 = [begin + {ok,P} = supervisor:start_child(sup_test,[]), + P + end || _<- lists:seq(1,1000)], + {T1,done} = timer:tc(?MODULE,terminate_all_children,[C1]), + + C2 = [begin + {ok,P} = supervisor:start_child(sup_test,[]), + P + end || _<- lists:seq(1,10000)], + {T2,done} = timer:tc(?MODULE,terminate_all_children,[C2]), + + Scaling = T2 div T1, + if Scaling > 20 -> + %% The scaling shoul be linear (i.e.10, really), but we + %% give some extra here to avoid failing the test + %% unecessarily. + ?t:fail({bad_scaling,Scaling}); + true -> + ok + end. + + +terminate_all_children([C|Cs]) -> + ok = supervisor:terminate_child(sup_test,C), + terminate_all_children(Cs); +terminate_all_children([]) -> + done. + + + +%%------------------------------------------------------------------------- +terminate(Pid, Reason) when Reason =/= supervisor -> + terminate(dummy, Pid, dummy, Reason). + +terminate(Sup, _, ChildId, supervisor) -> + ok = supervisor:terminate_child(Sup, ChildId); +terminate(_, ChildPid, _, kill) -> + Ref = erlang:monitor(process, ChildPid), + exit(ChildPid, kill), + receive + {'DOWN', Ref, process, ChildPid, killed} -> + ok + end; +terminate(_, ChildPid, _, shutdown) -> + Ref = erlang:monitor(process, ChildPid), + exit(ChildPid, shutdown), + receive + {'DOWN', Ref, process, ChildPid, shutdown} -> + ok + end; +terminate(_, ChildPid, _, normal) -> + Ref = erlang:monitor(process, ChildPid), + ChildPid ! stop, + receive + {'DOWN', Ref, process, ChildPid, normal} -> + ok + end; +terminate(_, ChildPid, _,abnormal) -> + Ref = erlang:monitor(process, ChildPid), + ChildPid ! die, + receive + {'DOWN', Ref, process, ChildPid, died} -> + ok + end. + +in_child_list([], _) -> + true; +in_child_list([Pid | Rest], Pids) -> + case is_in_child_list(Pid, Pids) of + true -> + in_child_list(Rest, Pids); false -> - ?line test_server:fail({which_children, used_no_memory}) - end, - case Size6 > Size5 of - true -> ok; + test_server:fail(child_should_be_alive) + end. +not_in_child_list([], _) -> + true; +not_in_child_list([Pid | Rest], Pids) -> + case is_in_child_list(Pid, Pids) of + true -> + test_server:fail(child_should_not_be_alive); false -> - ?line test_server:fail({which_children, used_no_memory}) - end, + not_in_child_list(Rest, Pids) + end. - [exit(Pid, kill) || {undefined, Pid, worker, _Modules} <- Children3], - test_server:sleep(100), - ?line [1,0,0,0] = get_child_counts(sup_test), +is_in_child_list(Pid, ChildPids) -> + lists:member(Pid, ChildPids). - ok. +check_exit([]) -> + ok; +check_exit([Pid | Pids]) -> + receive + {'EXIT', Pid, _} -> + check_exit(Pids) + end. + +check_exit_reason(Reason) -> + receive + {'EXIT', _, Reason} -> + ok; + {'EXIT', _, Else} -> + test_server:fail({bad_exit_reason, Else}) + end. + +check_exit_reason(Pid, Reason) -> + receive + {'EXIT', Pid, Reason} -> + ok; + {'EXIT', Pid, Else} -> + test_server:fail({bad_exit_reason, Else}) + end. diff --git a/lib/stdlib/test/supervisor_bridge_SUITE.erl b/lib/stdlib/test/supervisor_bridge_SUITE.erl index b23bac2d44..f2dbad0b3b 100644 --- a/lib/stdlib/test/supervisor_bridge_SUITE.erl +++ b/lib/stdlib/test/supervisor_bridge_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. 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,16 +17,37 @@ %% %CopyrightEnd% %% -module(supervisor_bridge_SUITE). --export([all/1,starting/1,mini_terminate/1,mini_die/1,badstart/1]). +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2,starting/1, + mini_terminate/1,mini_die/1,badstart/1]). -export([client/1,init/1,internal_loop_init/1,terminate/2]). --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). -define(bridge_name,supervisor_bridge_SUITE_server). -define(work_bridge_name,work_supervisor_bridge_SUITE_server). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -all(suite) -> [starting,mini_terminate,mini_die,badstart]. +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [starting, mini_terminate, mini_die, badstart]. + +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/lib/stdlib/test/sys_SUITE.erl b/lib/stdlib/test/sys_SUITE.erl index e44fd56403..72b089aa3f 100644 --- a/lib/stdlib/test/sys_SUITE.erl +++ b/lib/stdlib/test/sys_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. 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,9 +17,11 @@ %% %CopyrightEnd% %% -module(sys_SUITE). --export([all/1,log/1,log_to_file/1,stats/1,trace/1,suspend/1,install/1]). +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2,log/1,log_to_file/1, + stats/1,trace/1,suspend/1,install/1]). -export([handle_call/3,terminate/2,init/1]). --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). -define(server,sys_SUITE_server). @@ -29,7 +31,26 @@ %% system messages at all. -all(suite) -> [log,log_to_file,stats,trace,suspend,install]. +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [log, log_to_file, stats, trace, suspend, install]. + +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/lib/stdlib/test/tar_SUITE.erl b/lib/stdlib/test/tar_SUITE.erl index 7646f4c249..e32704ca65 100644 --- a/lib/stdlib/test/tar_SUITE.erl +++ b/lib/stdlib/test/tar_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2010. All Rights Reserved. +%% Copyright Ericsson AB 1997-2011. 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,21 +18,39 @@ %% -module(tar_SUITE). --export([all/1, borderline/1, atomic/1, long_names/1, +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2, borderline/1, atomic/1, long_names/1, create_long_names/1, bad_tar/1, errors/1, extract_from_binary/1, extract_from_binary_compressed/1, extract_from_open_file/1, symlinks/1, open_add_close/1, cooked_compressed/1, memory/1]). --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). -include_lib("kernel/include/file.hrl"). -all(suite) -> [borderline, atomic, long_names, create_long_names, - bad_tar, errors, - extract_from_binary, extract_from_binary_compressed, - extract_from_open_file, - symlinks, open_add_close, cooked_compressed, - memory]. +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [borderline, atomic, long_names, create_long_names, + bad_tar, errors, extract_from_binary, + extract_from_binary_compressed, extract_from_open_file, + symlinks, open_add_close, cooked_compressed, memory]. + +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + borderline(doc) -> ["Test creating, listing and extracting one file from an archive", diff --git a/lib/stdlib/test/timer_SUITE.erl b/lib/stdlib/test/timer_SUITE.erl index 5f38c91c64..f84c72b0f8 100644 --- a/lib/stdlib/test/timer_SUITE.erl +++ b/lib/stdlib/test/timer_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. 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,12 +18,12 @@ %% -module(timer_SUITE). --export([all/1]). +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2]). -export([do_big_test/1]). -export([big_test/1, collect/3, i_t/3, a_t/2]). -export([do_nrev/1, internal_watchdog/2]). --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). %% Test suite for timer module. This is a really nasty test it runs a %% lot of timeouts and then checks in the end if any of them was @@ -51,7 +51,26 @@ %% amount of load. The test suite should also include tests that test the %% interface of the timer module. -all(suite) -> [do_big_test]. +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [do_big_test]. + +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + %% ------------------------------------------------------- %% diff --git a/lib/stdlib/test/timer_simple_SUITE.erl b/lib/stdlib/test/timer_simple_SUITE.erl index 021a22c61b..dc751aad16 100644 --- a/lib/stdlib/test/timer_simple_SUITE.erl +++ b/lib/stdlib/test/timer_simple_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2011. 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,7 +21,8 @@ -module(timer_simple_SUITE). %% external --export([all/1, +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2, init_per_testcase/2, apply_after/1, send_after1/1, @@ -49,31 +50,35 @@ timer/4, timer/5]). --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). -define(MAXREF, (1 bsl 18)). -define(REFMARG, 30). -all(doc) -> "Test of the timer module."; -all(suite) -> - [apply_after, - send_after1, - send_after2, - send_after3, - exit_after1, - exit_after2, - kill_after1, - kill_after2, - apply_interval, - send_interval1, - send_interval2, - send_interval3, - send_interval4, - cancel1, - cancel2, - tc, - unique_refs, - timer_perf]. +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [apply_after, send_after1, send_after2, send_after3, + exit_after1, exit_after2, kill_after1, kill_after2, + apply_interval, send_interval1, send_interval2, + send_interval3, send_interval4, cancel1, cancel2, tc, + unique_refs, timer_perf]. + +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + init_per_testcase(_, Config) when is_list(Config) -> timer:start(), @@ -224,13 +229,48 @@ cancel2(Config) when is_list(Config) -> tc(doc) -> "Test sleep/1 and tc/3."; tc(suite) -> []; tc(Config) when is_list(Config) -> - % This should both sleep and tc - ?line {Res, ok} = timer:tc(timer, sleep, [500]), - ?line ok = if - Res < 500*1000 -> {too_early, Res}; % Too early - Res > 800*1000 -> {too_late, Res}; % Too much time + %% This should test both sleep and tc/3 + ?line {Res1, ok} = timer:tc(timer, sleep, [500]), + ?line ok = if + Res1 < 500*1000 -> {too_early, Res1}; % Too early + Res1 > 800*1000 -> {too_late, Res1}; % Too much time + true -> ok + end, + + %% tc/2 + ?line {Res2, ok} = timer:tc(fun(T) -> timer:sleep(T) end, [500]), + ?line ok = if + Res2 < 500*1000 -> {too_early, Res2}; % Too early + Res2 > 800*1000 -> {too_late, Res2}; % Too much time true -> ok end, + + %% tc/1 + ?line {Res3, ok} = timer:tc(fun() -> timer:sleep(500) end), + ?line ok = if + Res3 < 500*1000 -> {too_early, Res3}; % Too early + Res3 > 800*1000 -> {too_late, Res3}; % Too much time + true -> ok + end, + + %% Check that timer:tc don't catch errors + ?line ok = try timer:tc(erlang, exit, [foo]) + catch exit:foo -> ok + end, + + ?line ok = try timer:tc(fun(Reason) -> 1 = Reason end, [foo]) + catch error:{badmatch,_} -> ok + end, + + ?line ok = try timer:tc(fun() -> throw(foo) end) + catch foo -> ok + end, + + %% Check that return values are propageted + Self = self(), + ?line {_, Self} = timer:tc(erlang, self, []), + ?line {_, Self} = timer:tc(fun(P) -> P end, [self()]), + ?line {_, Self} = timer:tc(fun() -> self() end), ?line Sec = timer:seconds(4), ?line Min = timer:minutes(4), diff --git a/lib/stdlib/test/unicode_SUITE.erl b/lib/stdlib/test/unicode_SUITE.erl index 141ac64606..9aa800209d 100644 --- a/lib/stdlib/test/unicode_SUITE.erl +++ b/lib/stdlib/test/unicode_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2010. All Rights Reserved. +%% Copyright Ericsson AB 2008-2011. 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,11 +18,12 @@ %% -module(unicode_SUITE). --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). --export([all/1, +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2, init_per_testcase/2, - fin_per_testcase/2, + end_per_testcase/2, utf8_illegal_sequences_bif/1, utf16_illegal_sequences_bif/1, random_lists/1, @@ -34,12 +35,32 @@ init_per_testcase(Case, Config) when is_atom(Case), is_list(Config) -> Dog=?t:timetrap(?t:minutes(20)), [{watchdog, Dog}|Config]. -fin_per_testcase(_Case, Config) -> +end_per_testcase(_Case, Config) -> Dog = ?config(watchdog, Config), ?t:timetrap_cancel(Dog). -all(suite) -> - [utf8_illegal_sequences_bif,utf16_illegal_sequences_bif,random_lists,roundtrips,latin1,exceptions]. +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [utf8_illegal_sequences_bif, + utf16_illegal_sequences_bif, random_lists, roundtrips, + latin1, exceptions]. + +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + exceptions(Config) when is_list(Config) -> diff --git a/lib/stdlib/test/win32reg_SUITE.erl b/lib/stdlib/test/win32reg_SUITE.erl index c8cc82f61e..d3984ba67c 100644 --- a/lib/stdlib/test/win32reg_SUITE.erl +++ b/lib/stdlib/test/win32reg_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2010. All Rights Reserved. +%% Copyright Ericsson AB 1997-2011. 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,22 +18,34 @@ %% -module(win32reg_SUITE). --export([all/1,long/1,evil_write/1]). --export([ostype/1,fini/1]). +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2,long/1,evil_write/1]). --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). -all(suite) -> - [{conf,ostype,[long,evil_write],fini}]. +suite() -> [{ct_hooks,[ts_install_cth]}]. -ostype(Config) when is_list(Config) -> +all() -> + [long, evil_write]. + +groups() -> + []. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + +init_per_suite(Config) when is_list(Config) -> case os:type() of {win32, _} -> Config; _ -> {skip,"Doesn't run on UNIX."} end. -fini(Config) when is_list(Config) -> +end_per_suite(Config) when is_list(Config) -> Config. long(doc) -> "Test long keys and entries (OTP-3446)."; diff --git a/lib/stdlib/test/y2k_SUITE.erl b/lib/stdlib/test/y2k_SUITE.erl index a574d5e36e..d4d0721abf 100644 --- a/lib/stdlib/test/y2k_SUITE.erl +++ b/lib/stdlib/test/y2k_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% Copyright Ericsson AB 1998-2011. 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,30 +21,38 @@ -module(y2k_SUITE). --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). --export([all/1, +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2, date_1999_01_01/1, date_1999_02_28/1, date_1999_09_09/1, date_2000_01_01/1, date_2000_02_29/1, date_2001_01_01/1, date_2001_02_29/1, date_2004_02_29/1 ]). -all(doc) -> - "This is the test suite for year 2000. Eight dates according " - "to Ericsson Corporate Millennium Test Specification " - "(LME/DT-98:1097 are tested."; - -all(suite) -> - [date_1999_01_01, - date_1999_02_28, - date_1999_09_09, - date_2000_01_01, - date_2000_02_29, - date_2001_01_01, - date_2001_02_29, - date_2004_02_29 - ]. +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [date_1999_01_01, date_1999_02_28, date_1999_09_09, + date_2000_01_01, date_2000_02_29, date_2001_01_01, + date_2001_02_29, date_2004_02_29]. + +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + date_1999_01_01(doc) -> "#1 : 1999-01-01: test roll-over from 1998-12-31 to 1999-01-01."; diff --git a/lib/stdlib/test/zip_SUITE.erl b/lib/stdlib/test/zip_SUITE.erl index 12ca655000..d5f2cd52d4 100644 --- a/lib/stdlib/test/zip_SUITE.erl +++ b/lib/stdlib/test/zip_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2010. All Rights Reserved. +%% Copyright Ericsson AB 2006-2011. 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,24 +18,43 @@ %% -module(zip_SUITE). --export([all/1, borderline/1, atomic/1, +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2, borderline/1, atomic/1, bad_zip/1, unzip_from_binary/1, unzip_to_binary/1, zip_to_binary/1, unzip_options/1, zip_options/1, list_dir_options/1, aliases/1, openzip_api/1, zip_api/1, unzip_jar/1, - compress_control/1]). + compress_control/1, + foldl/1]). --include("test_server.hrl"). +-include_lib("test_server/include/test_server.hrl"). -include("test_server_line.hrl"). -include_lib("kernel/include/file.hrl"). -include_lib("stdlib/include/zip.hrl"). -all(suite) -> [borderline, atomic, bad_zip, - unzip_from_binary, unzip_to_binary, - zip_to_binary, - unzip_options, zip_options, list_dir_options, aliases, - openzip_api, zip_api, unzip_jar, - compress_control]. +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [borderline, atomic, bad_zip, unzip_from_binary, + unzip_to_binary, zip_to_binary, unzip_options, + zip_options, list_dir_options, aliases, openzip_api, + zip_api, unzip_jar, compress_control, foldl]. + +groups() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + borderline(doc) -> ["Test creating, listing and extracting one file from an archive " @@ -110,17 +129,17 @@ get_data(Port, Expect) -> {Port, {data, Bytes}} -> get_data(Port, match_output(Bytes, Expect, Port)); {Port, eof} -> - Port ! {self(), close}, + Port ! {self(), close}, receive {Port, closed} -> true - end, + end, receive - {'EXIT', Port, _} -> + {'EXIT', Port, _} -> ok after 1 -> % force context switch ok - end, + end, match_output(eof, Expect, Port) end. @@ -290,7 +309,7 @@ unzip_options(Config) when is_list(Config) -> DataDir = ?config(data_dir, Config), PrivDir = ?config(priv_dir, Config), Long = filename:join(DataDir, "abc.zip"), - + %% create a temp directory Subdir = filename:join(PrivDir, "t"), ok = file:make_dir(Subdir), @@ -303,7 +322,7 @@ unzip_options(Config) when is_list(Config) -> %% Verify. ?line true = (length(FList) =:= length(RetList)), - ?line lists:foreach(fun(F)-> {ok,B} = file:read_file(filename:join(DataDir, F)), + ?line lists:foreach(fun(F)-> {ok,B} = file:read_file(filename:join(DataDir, F)), {ok,B} = file:read_file(filename:join(Subdir, F)) end, FList), ?line lists:foreach(fun(F)-> ok = file:delete(F) end, @@ -321,7 +340,7 @@ unzip_jar(Config) when is_list(Config) -> DataDir = ?config(data_dir, Config), PrivDir = ?config(priv_dir, Config), JarFile = filename:join(DataDir, "test.jar"), - + %% create a temp directory Subdir = filename:join(PrivDir, "jartest"), ok = file:make_dir(Subdir), @@ -479,7 +498,7 @@ unzip_to_binary(Config) when is_list(Config) -> DataDir = ?config(data_dir, Config), PrivDir = ?config(priv_dir, Config), - delete_all_in(PrivDir), + delete_all_in(PrivDir), file:set_cwd(PrivDir), Long = filename:join(DataDir, "abc.zip"), @@ -595,17 +614,17 @@ do_delete_files([],Cnt) -> Cnt; do_delete_files([Item|Rest], Cnt) -> case file:delete(Item) of - ok -> + ok -> DelCnt = 1; {error,eperm} -> file:change_mode(Item, 8#777), DelCnt = delete_files(filelib:wildcard(filename:join(Item, "*"))), - file:del_dir(Item); + file:del_dir(Item); {error,eacces} -> %% We'll see about that! file:change_mode(Item, 8#777), case file:delete(Item) of - ok -> + ok -> DelCnt = 1; {error,_} -> erlang:yield(), @@ -643,22 +662,22 @@ compress_control(Config) when is_list(Config) -> ], test_compress_control(Dir, - Files, + Files, [{compress, []}], []), test_compress_control(Dir, - Files, + Files, [{uncompress, all}], []), test_compress_control(Dir, - Files, + Files, [{uncompress, []}], [".txt", ".exe", ".zip", ".lzh", ".arj"]), test_compress_control(Dir, - Files, + Files, [], [".txt", ".exe"]), @@ -686,7 +705,7 @@ test_compress_control(Dir, Files, ZipOptions, Expected) -> create_files(Files), {ok, Zip} = zip:create(Zip, [Dir], ZipOptions), - + {ok, OpenZip} = zip:openzip_open(Zip, [memory]), {ok,[#zip_comment{comment = ""} | ZipList]} = zip:openzip_list_dir(OpenZip), io:format("compress_control: -> ~p -> ~p\n -> ~pn", [Expected, ZipOptions, ZipList]), @@ -698,19 +717,19 @@ test_compress_control(Dir, Files, ZipOptions, Expected) -> delete_files(lists:reverse(Names)), % Remove plain files before directories ok. - + verify_compression([{Name, Kind, _Filler} | Files], ZipList, OpenZip, ZipOptions, Expected) -> {Name2, BinSz} = case Kind of - dir -> + dir -> {Name ++ "/", 0}; - _ -> + _ -> {ok, {Name, Bin}} = zip:openzip_get(Name, OpenZip), {Name, size(Bin)} end, {Name2, {value, ZipFile}} = {Name2, lists:keysearch(Name2, #zip_file.name, ZipList)}, #zip_file{info = #file_info{size = InfoSz, type = InfoType}, comp_size = InfoCompSz} = ZipFile, - + Ext = filename:extension(Name), IsComp = is_compressed(Ext, Kind, ZipOptions), ExpComp = lists:member(Ext, Expected), @@ -757,3 +776,33 @@ extensions([H | T], Old) -> extensions([], Old) -> Old. +foldl(Config) -> + PrivDir = ?config(priv_dir, Config), + File = filename:join([PrivDir, "foldl.zip"]), + + FooBin = <<"FOO">>, + BarBin = <<"BAR">>, + Files = [{"foo", FooBin}, {"bar", BarBin}], + ?line {ok, {File, Bin}} = zip:create(File, Files, [memory]), + ZipFun = fun(N, I, B, Acc) -> [{N, B(), I()} | Acc] end, + ?line {ok, FileSpec} = zip:foldl(ZipFun, [], {File, Bin}), + ?line [{"bar", BarBin, #file_info{}}, {"foo", FooBin, #file_info{}}] = FileSpec, + ?line {ok, {File, Bin}} = zip:create(File, lists:reverse(FileSpec), [memory]), + ?line {foo_bin, FooBin} = + try + zip:foldl(fun("foo", _, B, _) -> throw(B()); (_, _, _, Acc) -> Acc end, [], {File, Bin}) + catch + throw:FooBin -> + {foo_bin, FooBin} + end, + ?line ok = file:write_file(File, Bin), + ?line {ok, FileSpec} = zip:foldl(ZipFun, [], File), + + ?line {error, einval} = zip:foldl(fun() -> ok end, [], File), + ?line {error, einval} = zip:foldl(ZipFun, [], 42), + ?line {error, einval} = zip:foldl(ZipFun, [], {File, 42}), + + ?line ok = file:delete(File), + ?line {error, enoent} = zip:foldl(ZipFun, [], File), + + ok. diff --git a/lib/stdlib/vsn.mk b/lib/stdlib/vsn.mk index 75dc7dc62f..c0956030cf 100644 --- a/lib/stdlib/vsn.mk +++ b/lib/stdlib/vsn.mk @@ -1,2 +1 @@ -STDLIB_VSN = 1.16.5 - +STDLIB_VSN = 1.17.4 |